Re: JavaMemoryModel: Minor issue on hb edges and interrupts

From: Bart Jacobs (bart.jacobs@cs.kuleuven.ac.be)
Date: Sat Mar 20 2004 - 13:22:59 EST


> This does raise the issue though of how to document methods in a way
> that makes all of this clearer, so people don't have to step through
> series of inferences and/or guesses about intent to see that
> implementations must somehow entail locking and/or volatiles that
> provide the required happens-before guarantees. We do NOT want people
> to explicitly document methods as being "synchronized", but no
> standard phrasings or documentation conventions have emerged to
> otherwise specify memory/ordering properties. Any ideas?

I think a good convention would be to phrase all synchronization
requirements and guarantees in terms of happens-before edges, or in
higher-level terms that translate in a well-defined way to
happens-before edges.

For example, the documentation for Thread.interrupt and
Thread.isInterrupted could look like this:

In each execution of a Java program, each invocation I1 of
Thread.isInterrupted() on a thread T that returns true is associated
with exactly one invocation I2 of Thread.interrupt() on T, and there is
no other such invocation of Thread.isInterrupted() that is associated
wit I2. Also, the start of I2 happens-before the return of I1.

Another example:

Each invocation I1 by the Java Platform of Thread.run on a Thread object
T is associated with exactly one invocation I2 by the user of
Thread.start on T, and no other invocation of Thread.run on T is
associated with I2. Also, the start of I2 happens-before the start of
I1. Also, for each invocation I3 of Thread.join on T, the return of I1
happens-before the return of I3.

I see no reason why this could not be documented in the API docs instead
of the JLS.

The above specifications specify only guarantees and no requirements.
The specifications below specify only requirements and no guarantees.

A good default concurrency specification for most classes in the Java
API and for most third-party classes might be:

This class requires that all invocations on any one object are totally
ordered by the happens-before relation. That is, the client must ensure
that there is a total order on the invocations such that the return of
one invocation happens-before the start of the next invocation.

Note however: The above specification might be sufficient for classes
where in each invocation of a method (or construction of an object) only
one object is involved. A more complex specification is required if some
of the arguments are other objects of the same class, and a more complex
specification still is needed if some of the arguments are objects of
other classes.

Classes with such a specification could be called "serial-use-only"
classes. I guess most classes are "serial-use-only".

Perhaps this would work:

For each object O: (the set of all invocations of methods of
serial-use-only classes that have O either as the target or as an
argument must be totally ordered by hb).

Of course, the user is not responsible for invocations that are internal
in a class library. If an object is used implicitly during an invocation
(i.e. the object is not the target or an argument), then the document
must specify that it accesses this object for concurrency purposes.

For example: the specification of ArrayList (or List) might specify that
an invocation of add(...) accesses all elements of the ArrayList
(because it calls their equals(...) method). This entails the
requirement that all invocations that involve any element must be
totally ordered with respect to this invocation of add(...).

A few classes have greater requirements still: they are thread-affine.
Either all invocations on an object must occur in the thread in which
the object was created (something that one could call "weak thread
affinity"), or all invocations and object creations must occur in a
particular thread (such as the Swing thread) (which one could call
"strong thread affinity"). I guess one can write thread-affine classes
only by using thread-local storage or by using other thread-affine classes.

An interesting exercise is the specification of a self-synchronizing
class, like Vector or StringBuffer:

Consider an execution of a Java program. Then StringBuffer guarantees
that for each StringBuffer object O, there is a total order on all
invocations on O, consistent with the hb order, such that these
invocations behave as specified in the concurrency-agnostic
documentation under this order. Also, StringBuffer generates hb edges
such that for two invocations I1 and I2 such that I1 precedes I2 in this
total order, the start of I1 happens-before the return of I2.

-------------------------------
JavaMemoryModel mailing list - http://www.cs.umd.edu/~pugh/java/memoryModel



This archive was generated by hypermail 2b29 : Thu Oct 13 2005 - 07:01:01 EDT