Lots of C APIs that you might want to call from julia uses a special thread local global integer errno
to indicate errors because C only allows one return value and does not provide exceptions to indicate errors. Usually the function interface will define a special return value to indicate an error, and let set errno
to a number that represents an index into an array of standard error messages. Some functions, like unix strtoul
, do not have a special value to indicate error, but says it will change errno to an appropriate value if an error is encountered. This will require a cautious programmer to set errno = 0;
before calling the function and check if it is different from 0
after calling the function.
errno = 0;
arg = strtoul (buf, NULL, 0);
if (errno)
perror ("strtoul");
A common problem with error reporting using errno
is that there is only one such variable per thread, and that any function you call might change the value. This can also happen for functions that does not use errno
for error reporting, because they might call a function that does.
This snippet is buggy because it calls printf
before checking if errno == EIO
, and printf might change the value of errno.
if (fsync (fd) == −1) {
fprintf (stderr, "fsync failed!\n");
if (errno == EIO)
fprintf (stderr, "I/O error on %d!\n", fd);
}
ccall
is the special function-like syntax in Julia that allows you to talk to a C-function from Julia on the C-function's terms. If you want to call functions that do error reporting through errno
correctly you will have to use the special variant with the same syntax eccall
that returns two values. The first is the return value from the C-function, and the second is the value of errno
right after the function returned. eccall
will of course set errno
to zero before calling the function so that you can be sure that the error actually came from that function.
1 buff = "2333"
2 v, err = eccall("strtoul", Uint64, (Ptr{Uint8}, Ptr{Void}, Int), buff, C_NULL, 10)
3 err && throw(SystemError(err, "strtoul failed"))
which should be equivalent to the following C code
// line 1
char* buff = "2333"
// line 2
errno = 0;
int v = strtoul(buff, NULL, 10);
int err = errno;
// line 3
if err
perror("strtoul failed");
Early versions of Julia did not expose a special intrinsic eccall
for dealing with errno
APIs, but had a function called Base.errno()
that would allow programmers to access the value of errno
, and also a function systemerror
, that checked erron and raised an appropriate SystemError
. This was deprecated because it was impossible to know if something might cause the value of errno
to change between a ccall and when someone checked the error. In julia there is a runtime system with garbage collection and JIT compiling that might stop the execution of your code. Optimizations might also interfere with execution order. The current approach of explicitly storing it is much safer.