Interrupt Me Before I Run Again
Working with multiple threads is, in a way, like writing transactional database codeyou need to think carefully about which operations have to be atomic to preserve the system, yet being too conservative in this regard can make your code inefficient. This shouldn't come as a surprise, as a database is typically handling multiple requests from multiple clients in parallel. If you look at ThreadManager as a miniscule database, the same conditions apply: run() and stopAll() can be called in parallel. What guarantees should be made?
In my prototype application, one worker thread is spawned periodically based on external events, runs for a few hundred milliseconds, and then exits. (Because it takes little time to complete and doesn't block, I even used a null interrupt() method.) Creation of this thread was independent of when my bundle might be stopped; in fact, the two always occurred on different threads. The guarantees wanted as a client of ThreadManager are:
- If a call to run() succeeds, then that Interruptible will be stopped by a (possibly simultaneous) call to stopAll().
- If stopAll() is called, no later calls to run() on that ThreadManager succeed.
Together, these rules guarantee the necessary transactionality, and thus synchronization, between creating and stopping threads using the same ThreadManager instance. Without them, race conditions could permit new threads created at the same time as stopAll() to continue running after stopAll() returns.
These guarantees are implemented with a Boolean object in ThreadManager, isStopping, that tracks whether a call to stopAll() has started. Once set, subsequent calls to run() fail. It is important that synchronization is performed on this variable and that run() and stopAll() are not themselves synchronized. If they were, calls to run() would block while stopAll() was executing. Because stopAll() could be stopping a thread calling run(), deadlock could result.