Concurrency Property Checklist

Draft. Comments and suggestions welcome.

All sorts of questions can arise when people try to use your classes in concurrent programs. While it is not usually possible or desirable to document all of them for every class or method (especially for interfaces that cover multiple implementations, or library classes which may evolve), keep these in mind when writing documentation for others using, reviewing, co-developing, or maintaining your code.

Each property is given a name, just for ease of referencing. Some of the names are not very good. This is not an exhaustive categorization. Any given method or class will usually have several of these properties. Also, many of them can overlap. While these properties focus on properties of methods, many of them cannot be applied on a method-by-method basis, but instead be described together for all of the methods in a particular class, or even groups of related classes.

Will this method produce its stated effects if I call it, without any further precautions, when other threads may also be concurrently invoking this or other methods of this object?

Usage. This usually holds for either all or none of the methods in a class. In-between cases are not common because it is difficult to promise that all invariants and postconditions hold across concurrent invocations of arbitrary sets of methods unless all are safe. Typically applies when methods rely only on immutable state, and/or locks or atomics controlling updates and accesses to mutable state. Additionally, the class cannot rely on any other independently accessible objects (including statics) in unsafe ways. All fields are private. All objects referenced by internal fields are either completely inaccessible outside this class or managed safely. For example, none of these helper classes have methods that may send this as an argument or return value to unmanaged code. In general, the safety of one method relies on the safety of all other objects and methods that it may access.

In practice, this and most other safety guarantees apply only if the caller also ensures that the reference to the object used for the call is itself maintained safely.

Even if some or all of the methods of this class are not safe, is instance construction (either via constructor or a factory method) safe? Must the thread constructing the object first call some special initialization method before the object is safe?

Usage. This requires that constructors or factory methods are Safe, and additionally do not make references to the constructed objects available for access by other threads (e.g., by recording in a static field) before initialization is complete.

If I create a synchronization wrapper around this class (or use client-side locking) and otherwise guarantee lack of unsynchronized access, will it be safe (and live)? Or would this be so if I also took any other precautions, like using the same lock for some other associated objects?

Usage. Usually holds for either all or none of the methods in a class. Classes are Lockable in a useful sense if they do not access independently accessible mutable objects or their fields (including statics), or if they do, the set of such classes is small and all are also Lockable. A Lockable class may contain methods that don't actually require locking, for example Stateless methods that can run concurrently with others without need for any lock.

If I create a ReadWriteLock wrapper around this class (or use client-side read-write locking) and otherwise guarantee lack of unsynchronized access, will it be safe (and live)? If so, which methods should be read-locked, and which ones write-locked?

Usage. Typically holds when a class is Lockable, and additionally, all accessor methods are purely read-only. To be useful, you must indicate which methods require which kinds of locks (possibly along with indications of those methods that do not need either kind of lock):

Does the safety of this method require that it be invoked, without any further precautions, only by the thread that created this object? If so, can ownership be transfered?

Usage. Usually holds for either all or none of the methods in a class. Typically, classes are OwnerSafe if any independently accessible mutable objects or their fields (including statics) that they rely on are also at least OwnerSafe. Note that Lockable and OwnerSafe may be the same if the class does not access any such objects, but may still differ if some parts of a program adopt one or the other usage style in a way that other parts must also obey. A class is usefully OwnerSafe if the list of accessed classes is small and all are also OwnerSafe. The most common means of transferring ownership of an object across threads is via a BlockingQueue or similar structure in which a producer thread never again accesses an object or any OwnerSafe objects it relies on once it is placed in the queue.

Does this method rely on the value of a ThreadLocal or other thread-specific data that was produced by some thread, and thus must only be called by that thread unless other precautions are taken?

Usage. Usually, this is a restricted form of OwnerSafe, but may also apply when a method relies on particular security settings (e.g., an assumed AccessControlContext). This property may preclude a task from being run in a thread pool unless each required ThreadLocal value can be listed and so can be set by worker threads before execution.

Does the safety of this method have the precondition that the object is in a state that is produced by first calling other particular methods?

Usage. One common case is an object that undergoes a series of mutations by its owner, and is then never again modified, but may be safely read by other threads that never again change its state. For example a collection that is filled in, and then used only via Collections.unmodifiableCollection wrappers.

Does the safety of this method have the precondition that the caller holds some particular lock? If so, is the precondition absolute, or do I just need to do this if I have made the object accessible from multiple threads? Does this need to be the same lock used for some other particular classes and methods? And do I need to hold some outer lock before acquiring the lock required by this method?

Usage. This may be the consequence of applying locks to a Lockable class. It may also hold for objects that are designed to be used only as components of some container class.

If this method encounters an exception, will subsequent calls still be safe? If not, is there a way to recover the state of this object, or create a new one? Are some exceptions OK, others not? Can any exception leave the object in an unadvertised unusable state?

Usage. Not being FailureSafe with respect to unrecoverable failures (such as OutOfMemoryErrors) might be an exception to main safety properties of a class or method.

Are the state changes and other effects produced by this method atomic with respect to all other methods? If not, which ones? Are only some of the effects atomic? Is there anything I can do do ensure atomicity with respect to those other methods or effects?

Usage. Usually, atomicity is ensured by using locks, or in designs based on atomics, by guaranteeing that there exists a "linearization point" before which an update is not in effect, and after which it is. Sometimes, a class will provide "core" methods that are atomic, plus other "layered" composite methods that are not necessarily atomic. For example, add vs addAll in a Collection. Providing callers with a way to guarantee atomicity of composite methods usually requires exposing internal synchronization mechanics of a class. Atomicity normally requires that all fields bearing some invariant relation to each other are only updated and accessed under a common lock, or represented using some kind of snapshot technique. All code must additionally that if a condition is assumed to hold that it actually does throughout the course of code relying on this, usually by placing the condition and action occur within the same locked code segment.

Can some or all of the effects of this method occur in other threads that need not complete upon method return? Is there a way I can wait these out if I need to?

Usage. This applies to Thread.start, Executor.execute, AWT, Swing, and JavaBeans event triggers, and other methods that use them. Asynchronous methods may be accompanied by TerminationBlocking methods allowing callers to optionally wait out effects, as in Thread.Join, Futures, and invokeAndWait.

Does this method always return the same result when invoked multiple times with the same arguments?

Usage. This normally applies to a Safe method whose actions and results rely only on its arguments and/or values of final fields (including final fields of other objects accessed by final references). A class containing only Stateless methods is Immutable.

Are the results returned by this method guaranteed not to be arbitrarily stale and not to reveal transient illegal values? If not, is there anything I can do to ensure this? Are there any exceptions to consistency properties?

Usage. An accessor method is AccessorConsistent if it cannot encounter read/write conflicts that might reveal state inconsistencies, or reorderings that might cause apparent lags with respect to concurrent updates. Generally, this can be ensured by using locks, or for aspects of state that need not obey invariants with others, accessing volatile or atomic fields. If an accessor can be invoked as a callback, it must be prepared for the case where internal state is not consistent because it is being called indirectly from the insides of another method during which fields have taken on transient values.

For a class that holds references to another independently accessible base object, under what conditions will state changes in the base object be seen and acted upon by the methods in the view object? Always? Never? Sometimes? Does this hold per-operation or across all sequences of operations? Will the view method instead abort if it detects changes? Under what conditions will it detect these changes? And is there anything I can do to suppress or prevent this?

Usage. Usually, these decisions must be communicated with users of the view class only if it is possible for a view method to reveal a state of the base object inconsistent with that reported by the base object itself, especially across sequences of operations on the view object. Options range from simple locking to complex caching protocols. Common categories include:

Does this method guarantee not to ever block, and not to loop more than a finite (and small) number of steps, no matter what other threads are running (modulo OS resources and scheduling)?

Usage. This usually holds only for non-looping lockless code, but also applies to fancier nonblocking data structure operations that have somehow been proven to complete in a finite number of steps regardless of contention.

Does this method guarantee not to ever block, and additionally guarantee to only contain loops that will eventually terminate, no matter what other threads are running (modulo OS resources and scheduling)?

Usage. This is weakening of WaitFree, where the number of looping steps cannot be bounded beyond saying that they eventually terminate, generally because at least one of a contending set of threads will make progress in a finite number of steps.

Does this method guarantee not to ever block, and additionally guarantee not to unboundedly loop except due to contention with other threads?

Usage. This is a weakening of LockFree in which progress cannot be strictly guaranteed in the face of unbounded thread contention, and applies to methods that may require some kind of contention-avoidance (for example exponential backoff) to guarantee progress.

Does this method guarantee not to block except due to lock contention with other threads, and to use locks to cover only loopless, recursionless code, so holds them only for finite (and short) periods?

Usage. This is a weakening of ObstructionFree applying when a method may use locks, and so one thread may fail to make progress because another thread is suspended while holding a needed lock, and there might not be anything you can do to force progress beyond waiting for the OS/Scheduler to eventually cause the suspended thread to resume.

Does this method guarantee eventual progress in the face of unbounded thread contention? (Modulo OS resources/scheduling) If so, does it promise stronger fairness properties such as FIFO?

Usage.This is an alternative to BoundedLocking in which loops etc may occur, but unbounded delays of any given thread due to contention cannot occur, so long as the underlying OS/Scheduler maintains minimal fairness properties. Generally, a method claiming to be Fair can only call other Fair or WaitFree methods.

Does the correctness or well-behavedness of any multithreaded program using this method or class rely on fair synchronization and/or thread scheduling?

Usage. This may apply if it is possible for one thread to repeatedly acquire and release a lock without giving any other thread a chance to acquire it. One common case of this is methods with code of form for (...) synchronized(...) { ... } }, which might, without fairness guarantees, be executed as if it instead placed the synchronized around the loop. Usually, there are alternatives to such constructions.

Does this method possibly use locks, so may block due to contention, but will eventually progress assuming that other threads eventually release those locks?

Usage. This holds for any method that may encounter lock (including those in downstream calls) for which you cannot make any stronger liveness claims beyond the fact that it is not possible for an invocation of this method to be holding one lock while trying another, while another thread is doing the opposite. Note that a method might not itself be fully safe, yet still somehow encounter locks in the course of execution.

Does this method acquire locks on any object sent in as an argument, or any other independently accessible objects, so could lock up if I'm not careful? Does it promise to acquire locks in some particular order? Will it use tryLocks or other means to avoid deadlocks?

Usage. This is the most important property for users to know to prevent deadlocks, but often the hardest to accurately specify when a method relies on the locking properties of unknown downstream virtual calls.

Does this accessor method guarantee eventual progress without blocking, even though mutator methods may block?

Usage. This is most useful when applied to all accessor methods of a class.

Does this method return a failure indication or throw an exception if some some condition does not hold, where this condition is intended to be produced by some other thread? Can a failed call be retried or must the caller abort? Is there anything special that needs to be done before retrying a failed call?

Usage. Normally, balking applies when conditions are strictly state-based versus those that may somehow be related to values of arguments to the method.

Does this method block waiting for some condition that must be produced by some other thread? What can or must I do to ensure that the condition eventually holds? Will a failure in another thread trying to change this condition cause an associated failure in this method? (This applies for example, to CyclicBarriers.) Do I need to avoid holding any particular locks when calling this method so as to avoid locking out other threads trying to perform actions that could unblock the call?

Usage. It is normal practice to implicitly identify methods that may block on conditions by declaring that they can throw InterruptedException. implementations must ensure that it is not possible for a thread requiring a condition to block waiting for it even though another thread has already been signalled that the condition holds.

Does this method block until one or more other threads terminate?

Usage. This is a special case of ConditionBlocking, where the condition being waited for is termiation of another task. It usually applies when a method blocks on Thread.join, Future.get, or related methods.

Will this method somehow complete in cases where bounded queues, collections, resource pools, thread pools, etc reach their limits?

Usage. This applies to ConditionBlocking methods that block on resource availability. Typical means for achieving liveness under saturation include aborting (usually throwing an exception), shedding work, or applying back-pressure causing other threads producing work to slow down.

Does this method give up after a timeout? If so, is there any way to control the timeout value? What condition is the timeout waiting for?

Usage. You may need to distinguish whether the method will report failure if the timeout elapses even if the waited-for condition does hold when the time elapses, or whether it will treat this as a successful case and proceed.

Does this method repeatedly poll/spin until some condition or result holds? What can or must I do to minimize or eliminate spinning?

Usage. Condition polling is usually not a desirable feature, but instead a design compromise that users of a class have to tolerate. Usually, it is best to have a non-spinning ConditionBalking method so that that callers can control spin rates etc.

Does this method check interrupt or cancellation state and abort cleanly, so it can be safely cancelled from another thread?

Usage. This is usually a consideration only for methods containing long or unbounded loops, as well as condition or IO blocks.

Does this method have a (soft) real-time deadline? What happens if it is not met? Does this method fail, throw away work, or reduce guarantees when it falls behind?

Usage. These are the most common concurrency-related Quality of Service considerations not otherwise deal with here.

Does this method block waiting for IO? If so, will it sometimes instead time-out and fail? If so, can it be retried or must it be aborted? Is the IO acting as a remote service call whose result affects state of local objects?

Usage. There are many further sub-questions about IO and remote operations that frequently interact with concurrency but not dealt with here.

Doug Lea
Last modified: Mon Dec 27 10:20:35 EST 2004