4 ms·
The author is unhappy with how signals work, and proposes some really wonky ways to get the behavior he thinks is correct. But for a programmer, it's a lot easi
by t8sr 2y ago
The author is unhappy with how signals work, and proposes some really wonky ways to get the behavior he thinks is correct. But for a programmer, it's a lot easier to just do things the way designers of signals intended.
For example, multiple SIGCHLD coalesce, so you are supposed to call waitpid in a loop:
int status;
while((pid_t child = waitpid(-1, &status, WHOHANG)) > 0) { // handle zombie
This API is mature and obvious problems like the author points out have already been solved before. The author seems to reject the established practice on purely aesthetic grounds, which is a... choice. But not one you should make, if you want your life to be easy.
- the8472 2y agowaitpid(-1) in a program composed of libraries means that loop is stealing PIDs from other things that manage children within the same process and try to wait on them by PID, which means they'll lose access to the exit status and can also lead to hangs due to pid recycling and then waiting on the wrong pid. It can work in a self-contained program, but not in anything complicated. For children specifically pidfds are more reliable. But that doesn't help with other signals.
- chubot 2y agoYes waitpid(-1) doesn't compose, but lots of things in the C world don't compose - using library A which uses say libuv, and one that uses libevent (two different event loops) - forking while holding locks - two libraries using two different thread pools -- concurrency policy is a global concern. (and this one isn't specific to C) FWIW the solution I use in a shell is simple - create a Waiter object that wraps waitpid(), and only code that has a reference to the waiter can do anything with processes. It's basically like making an event loop object and passing it around, rather than making it global. This is also an argument for libraries not doing I/O -- they should be pure. They should be parameterized by I/O, including starting threads, etc. I haven't ever wanted to use a library that starts processes behind my back
- thayne 2y ago> using library A which uses say libuv, and one that uses libevent (two different event loops) It's not ideal, but I don't see a reason this wouldn't work if you ran the two event loops on separate threads > forking while holding locks Yes. You can't use fork in a multi-threaded program, unless it is followed by an exec. Which is one reason forking is somewhat rare in modern code. > two libraries using two different thread pools -- concurrency policy is a global concern. That doesn't break the semantics of the program. > create a Waiter object that wraps waitpid(), and only code that has a reference to the waiter can do anything with processes. This doesn't help if you use a library that calls waitpid directly. This is actually probably more of a problem in non-c code, where the standard library likely to have an abstraction that calls waitpid on child processes. So handling SIGCHLD can interfere with other code waiting for a child to finish.