Yikes! I'm appalled by the one-sided justification for semaphores in this book. (2.3, "Why semaphores?")<p>Semaphores are actually a horrible primitive for building concurrency. I sometimes call them "the goto of synchronization", which is deliberately ironic since they are a gift to the world from the "goto considered harmful" thinker, Dijkstra.<p>Debugging a problem with semaphores is difficult because they have no useful state beyond their count. You don't know how a set of semaphores might have gotten into a given state. (Vaguely analogous to the the argument that in a spaghetti program with backward gotos, it's difficult to have an idea of how control arrived at a particular node in the program.)<p>For instance, when we (ab)use a semaphore to simulate a mutex, that semaphore does not actually track the ownership: who is locking it now? It can be signaled by any task at all, whereas a proper mutex can diagnose the fact that it is unlocked by its owner.<p>Semaphores most certainly do not <i>impose deliberate constraints that avoid programmers avoid errors</i>, whatever that is supposed to mean, ouch!<p>When higher level primitives are correctly built on semaphores (condition variables, read-write locks, you name it), the solutions look like Rube Goldberg mousetraps. And that's before additional requirements are added like error checking, or handling priority inversion and whatnot.<p>Semaphores have one very good application and that is a situation in which a very fragile context (such as an interrupt service routine) needs to generate a wakeup signal. The semaphore signal operation can be made reentrant fairly easily. This is not only true in OS kernels. Note that in POSIX, for instance, the sem_post operation is noteworthy for being async-signal-safe, meaning that it can be called from a signal handler. By contrast, other things like pthread_cond_signal cannot be.