6

When an error occurs in one of the UNIX System functions, a negative value is often returned, and the integer errno is set to a value, that gives additional information. -- Advanced Programming in the UNIX Environment, section 1.7

This seems weird:

  • It introduces coupling between different compilation units of the standard library - error states are not defined in the modules causing them.
  • It introduces implementation complexity, because errno needs to be thread local.
  • It introduces usability complexity because the user needs to check that the original system call is in error, and then check errno Which is also another function call.

Why not encode the error states in the return values?

pilcrow
  • 56,591
  • 13
  • 94
  • 135
Vorac
  • 8,726
  • 11
  • 58
  • 101
  • 4
    Pretty safe to assume that this mechanism was invented before threads. Also, to be somewhat pedantic, checking `errno` is not necessarily a function call. – unwind Jan 29 '16 at 16:27
  • Also, return values from functions are used to return actual data in many cases. See [`read()`](http://pubs.opengroup.org/onlinepubs/9699919799/functions/read.html). – Andrew Henle Jan 29 '16 at 16:28
  • 1
    `read()` is a bad example as especially here returning `-errno` would work, assuming it returned a `signed` integer, which it doesn't. @AndrewHenle – alk Jan 29 '16 at 16:31
  • 2
    @alk Surely [`read()`](http://linux.die.net/man/2/read) returns `ssize_t`, the signed cousin of `size_t`? – unwind Jan 29 '16 at 16:33
  • 1
    @alk - OK, but the point that an error code can't in general be separated from valid return values still stands. – Andrew Henle Jan 29 '16 at 16:33
  • `ssize_t` is not specified to be a `signed`, but just to be capable to hold a value `>= -1`. @unwind Moreover it's POSIX not C. – alk Jan 29 '16 at 16:39
  • @alk: `ssize_t` has to be a signed type to be able to hold negative values. In any case, [POSIX says](http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/sys_types.h.html) "**`blksize_t`**, **`pid_t`**, and **`ssize_t`** shall be signed integer types". – Keith Thompson Jan 29 '16 at 16:44
  • @KeithThompson: Well, two lines below it says "*The type ssize_t shall be capable of storing values at least in the range [-1, {SSIZE_MAX}].*". – alk Jan 29 '16 at 16:46
  • @alk: Yes. but you said "`ssize_t` is not specified to be a `signed`". If you mean it's not specified to be type `signed` (i.e., `signed int`) then of course that's true -- but nobody said it was. If you meant that it's not specified to be a signed type, that's incorrect -- it clearly is. – Keith Thompson Jan 29 '16 at 16:55

2 Answers2

8

For historical reasons mostly.

Notice that in reality, errno is not an ordinary global variable today (that was the case in the 1980s). It is today (C99, C11...) a macro - usually expanded to some function call, perhaps __errno() (and recent C standard requires errno to be a macro, see n1570 §7.5); or it might be expanded to some thread local variable, or even some compiler magic.

errno wants to be a macro for multi-threading needs, so I guess that standards evolved to require it to be some macro

So you should #include <errno.h> and use the errno macro nearly as if it was some global variable, but knowing that in fact it is not one.

Details are implementation specific. Look inside the source code of your C standard library, e.g. musl-libc has in errno/__errno_location.c

  int *__errno_location(void)
  {
     return &__pthread_self()->errno_val;
  }

and in include/errno.h public header:

 int *__errno_location(void);
 #define errno (*__errno_location())

and GNU libc has something very similar

BTW some system functions do not return an integer (e.g. mmap), and some POSIX functions don't indicate errors thru errno, e.g. dlopen (see dlerror). So it would be difficult to guarantee in some standard that every error could be indicated by returned values.

Basile Starynkevitch
  • 223,805
  • 18
  • 296
  • 547
5

I'll cite Linus Torvalds for this.

"errno" is one of those few really bad stupidities in UNIX. Linux has fixed it, and doesn't use it internally, and never shall. Too bad that user space has to fix up the correct error code returning that the kernel does, and turn it into the "errno" stupidity for backwards compatibility.

[...]

"errno" is one of those fundamentally broken things that should not exist. It was wrong in original UNIX, it's wrong now.

[...]

The Linux way of returning negative error numbers is much nicer. It's inherently thread-safe, and it has no performance downsides. Of course, it does depend on having enough of a result domain that you can always separate error returns from good returns, but that's true in practice for all system calls.

Fabio A.
  • 2,517
  • 26
  • 35