Wandering Thoughts archives

2022-06-13

In general Unix system calls are not cancellable, just abortable

One of the common wishes in environments and languages that support concurrency is for (Unix) system calls to be cancellable in the way that other operations often are. Unfortunately this is not practical, which is part of why a lot of such environments don't try to support it (Go is famously one of them, which makes people unhappy since it does have a 'context' package that can cancel other things).

All or almost all Unix system calls can be aborted, which is to say that you can interrupt them before they complete and force control to return to the program. However, when you abort a system call this way the effects of the system call may be either incomplete or indeterminate, leaving you with either broken state or unusable state (or at least a peculiar state that you have to sort out). For example, if a close() is aborted, the state of the file descriptor involved is explicitly unknown. Only some Unix system calls can be cancelled, which is to say stopped with things in some orderly and known state. Often these system calls are the least interesting ones because all they do is inquire about the state of things, such as what file descriptors are ready or whether you have dead or stopped children.

Some interesting system calls can be cancelled under some but not all situations using special mechanisms that may have side effects. You may be able to relatively cleanly cancel certain network IO by setting the file descriptor to non-blocking, for example, but this will probably have done some IO and might affect other threads if they immediately try to do IO on the file descriptor before you can set it back to blocking.

Some languages and environments actually turn certain normally synchronous 'system call' operations into asynchronous actions that can be cancelled (to some degree). For example, Go's runtime and network IO subsystem cooperate to turn what looks like blocking read() or write() operations into non-blocking ones combined with waiting for network sockets to be ready (along with other things, such as periodic timer ticks). This allows these operations to be cancellable (although Go doesn't expose a clean way to do it). But this can only be applied to certain sorts of read()s and write()s; very few Unixes support cleanly cancelling filesystem IO, for example.

The distinction I'm drawing between cancelling a system call and aborting it may seem picky, but I think it's an important one. If you think of it as aborting a system call, it means means that you have to carefully consider and deal with partially done operations and potentially uncertain state. If you could cleanly cancel system calls without worrying about all of that, life would be much nicer. But you can't.

(This was sort of sparked by some comments here.)

unix/SystemCallsNotCancellable written at 22:15:33; Add Comment

By day for June 2022: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28; before June.

Page tools: See As Normal.
Search:
Login: Password:
Atom Syndication: Recent Pages, Recent Comments.

This dinky wiki is brought to you by the Insane Hackers Guild, Python sub-branch.