Guava: a dialect of Java without data races

22

Transcript of Guava: a dialect of Java without data races

Guava: A Dialect of Java without Data RacesDavid F. Bacon Robert E. Strom Ashis TarafdarIBM T.J. Watson Research Center University of Texas, AustinAbstractWe introduce Guava, a dialect of Java whose rules stati-cally guarantee that parallel threads access shared data onlythrough synchronized methods. Our dialect distinguishesthree categories of classes: (1) monitors, which may be ref-erenced from multiple threads, but whose methods are ac-cessed serially; (2) values, which cannot be referenced andtherefore are never shared; and (3) objects, which can havemultiple references but only from within one thread, andtherefore do not need to be synchronized. Guava circum-vents the problems associated with today's Java memorymodel, which must de�ne behavior when concurrent threadsaccess shared memory without synchronization.We present an overview of the syntax and the seman-tic rules of Guava. We discuss how implementations ofGuava can exploit these rules to re-enable compiler opti-mizations inhibited by standard Java. We discuss how com-pilers for certain multiprocessor architectures can automati-cally generate certain programming idioms, such as double-check reads, as optimizations of serialized monitors.1 IntroductionJava is a multithreaded object-oriented language whose prin-cipal means of inter-thread interaction is through monitors| that is, shared objects with synchronized methods. How-ever, Java does not require all shared access to be synchro-nized, and it is commonly believed that allowing certainunsynchronized reads and writes of shared variables is nec-essary to achieve high performance. Unfortunately, under-standing the implications of the concurrency model of theJava Language Speci�cation [12] in the context of modern,weakly ordered multiprocessors such as the PowerPC [20],Alpha [1], or Sparc Relaxed Memory Order [25] has provento be very di�cult, and a number of anomalies have beenfound that were not intended by the designers of the lan-Appears in OOPSLA 2000, the Conference on Object-Oriented Programming Systems, Languages, and Appli-cations. Copyright c 2000 ACM. Author contact: DavidBacon, IBM Watson Research Center, P.O. Box 704,Yorktown Heights, NY 10598, telephone (914) 784-7811,email [email protected].

guage. These anomalies can manifest themselves as surpris-ing or even undocumented behavior, or as the inability toperform apparently straightforward compiler optimizations[21].E�orts are underway to adjust the Java memory model.However, these e�orts are pulling the language in oppositedirections: strengthening the guarantees about behavior ofunsynchronized shared data inhibits optimizations a�ect-ing single-threaded programs; weakening these guaranteesbreaks programs or what is worse, allows incorrect programsto appear correct until ported to a di�erent multiprocessorarchitecture.In this paper we propose an alternative direction, namelyforbidding unsynchronized shared data altogether, and ex-plicitly distinguishing sharable from unsharable classes. Com-pilers will then be able to insert synchronization code onlywhere it is needed, and will not have to suppress code mo-tion or value numbering optimizations that are destroyedby possible multithreading. Implementations will be able tofurther increase concurrency by scheduling reads in paralleland (where the target architecture permits) use optimiza-tions such as double-check reading.We believe that explicitly distinguishing between objectsthat are concurrently shared and those that are not is asounder basis for building concurrent systems.We de�ne a new dialect, Guava, with all the familiarproperties of Java, except that unsynchronized shared accesscan never occur. Instead, every instance of a class is eithera monitor, where all access is fully synchronized, a value,where sharing is disallowed, or an ordinary object, wheresharing is allowed but only within a single thread.Guava introduces additional type rules required to assurethe above properties statically. Enforcing these type rulesgiven separate compilation requires some additional pro-gram annotations. In e�ect, we make a tradeo� of slightlyincreasing the programmer's documentation e�ort, and mod-erately decreasing the programmer's freedom to assign ref-

erences, in exchange for a simpler semantics (no need tode�ne the behavior of unsynchronized shared memory), andgreater information available to compilers and implementa-tions.Guava places the burden of complexity where it belongs:on concurrent programs. A single threaded program can bewritten entirely using objects and values, and requires noprogrammer annotations for concurrency.Guava can be compiled into standard Java bytecodes,although some of the performance bene�ts will only be re-alized with a specially modi�ed virtual machine.The rest of this paper is organized as follows: Section 2describes Guava through programming examples. Section 3describes the static checking. Section 4 describes how Guavaavoids the anomalies that Java su�ers on multiprocessors,and describes some optimizations that can be performed ina customized virtual machine. Finally, we compare Guavato related work and present our conclusions.Appendix A describes our changes to the Java syntax.Appendix B describes the modi�ed rules for type conver-sions in Guava. Appendix C describes how Guava can betranslated into standard Java bytecodes.2 The Guava LanguageGuava introduces the following changes to the Java lan-guage:� class categories: (Object, Value, and Monitor);� read and update annotations on methods and param-eters;� local and global annotations on methods; and� region annotations on parameters and return values.Guava is designed to make writing single-threaded pro-grams simple; most of the above changes will not be noticedby the programmer of single-threaded applications. Further-more, defaults have been chosen to minimize the amount ofannotation required for concurrent programs as well.2.1 Class CategoriesIn Java, there are only two categories of data: referencesand non-references. References either are null or point to in-stances of classes. Non-references are always values of prim-itive types such as int; they can never be aliased, and donot have classes, methods, inheritance, or any other featuresof objects.Guava introduces two changes. First, it extends the non-references to include user-de�ned classes as well as primitive

types. Second, it partitions the references into two cate-gories: those whose referents can be shared between threadsand those that cannot.Non-references are called values: depending upon theirclass they may or may not be mutable, but they are neversharable. The entities that can be shared between threadsare called monitors; the entities whose sharing is restrictedare called ordinary objects. Values, monitors, and objectsform disjoint class hierarchies. In this paper we will reservethe term object for ordinary (unsynchronized) objects, anduse instance as the all-inclusive term for values, monitors,and objects.2.1.1 ObjectsObjects in Guava are very much like objects in Java, withthe exception that they may not be concurrently accessed.Therefore, they may not have synchronized methods (thesynchronized keyword does not exist in Guava). Becausethey may not be concurrently accessed, there are restrictionson how objects may be passed between monitors and values.In particular, objects are always contained within a par-ticular monitor or value instance called their owner. Objectscan never migrate from one owner to another, nor may aninstance variable associated with one owner ever refer to anobject owned by a di�erent owner.2.1.2 MonitorsMonitors are instances that may be accessed concurrentlyby multiple threads. Operations on monitors are alwayssynchronized. It is irrelevant whether these operations aremethod invocations or accesses to �elds from outside of themonitor instance.Operations on non-monitors are never synchronized, sinceother instances are always uniquely owned by a single mon-itor. This provides a signi�cant improvement in both per-formance and simplicity.The methods wait, notify, and notifyAll are only ac-cessible for monitors, and are public final methods. Fi-nalizers are only allowed on monitors.Instances of Thread and its subclasses are monitors. How-ever, Guava recognizes the special final control methodsstop, interrupt, etc., and permits one thread to executeand a second thread to invoke control methods on the Threadobject, since this does not involve concurrent shared dataaccess.2.1.3 ValuesSince objects may not be freely passed between monitors,Guava includes values, which may encapsulate complex data2

structures and yet may be exchanged freely between moni-tors.A value instance is directly stored in a variable, ratherthan being pointed to via a reference. Values in Guava arelike \primitive values" of Java, like int and char in that:there are no references to them; assignment creates a copy ofthe value, not an alias; and the == operator tests for equalityof the value, rather than object identity.Since value classes may be user-de�ned, they may ex-port methods and public �elds. Uninitialized values have adefault initialization value (which for consistency with ref-erences, we will denote as null).Copying a value deep-copies any objects and values in-side it, but leaves references to monitors unchanged.For performance, we include a \move" operator whosee�ect is to move a value without copying it: the expression y= x-- assigns the value of x to the variable y, and resets thevalue of x to null. This operator is useful when transferringvalue instances to another entity without the overhead ofperforming a copy operation.Implementations are also free to implement copying byreference-passing whenever the source variable is dead orwhenever neither copy will be updated. In particular, avalue that is only written to in its constructor can alwaysbe passed by reference in the implementation.2.1.4 Class HierarchyObjects, monitors, and values are related in the Guava classhierarchy as shown in Figure 1. The abstract class Instanceis the root of the inheritance hierarchy. The class Instanceimplements the public methods associated with all instances,namely equals(), hashCode(), toString(), and getClass(),as well as the protected clone() method, but not the oper-ations wait(), etc., that are unique to monitors.All concrete instances are subclasses of one of the threecategory classes: Object, Value, and Monitor. In betweenInstance and the category classes are the category inter-faces; each class implements exactly two category interfaces.Interface Mobile includes values and monitors | a Mobilemay be moved freely between threads. Interface Local in-cludes objects and values | these are never shared betweenthreads. Interface Reference includes monitors and objects| these are copied by reference.2.2 Example I: Shared Hash TableFigure 2 shows a Guava program that makes use of all threekinds of instances. It de�nes a shared hash table | onethat can be used by multiple concurrent threads. Extraannotations required by Guava are shown in italics.The class SharedHashtable is de�ned as a monitor withits extends clause. It exports two methods, get, which re-

,QVWDQFHWR6WULQJ� FORQHKDVK&RGH �HTXDOV

JHW&ODVV

2EMHFW 0RQLWRUZDLW QRWLI\

ILQDOL]H� QRWLI\$OO9DOXH

/RFDO 5HIHUHQFH0RELOHFRS\

Figure 1: The top of the inheritance hierarchy in Guava.Solid boxes denote abstract classes; dashed boxes denoteinterfaces.trieves a value based on a key, put, which enters a key/valuepair into the hash table. Details such as resizing and removalare omitted for simplicity.The key and value instances used by SharedHashtableare de�ned to be of class Mobile, which means that theycan be any monitor or value. This is because a shared hashtable monitor is an owner; it can not save references passedin from other owners.Each list of buckets is contained in a value of type Bucket-List. While in this case BucketList could have been de�nedas either a value or an object, it is good programming prac-tice in Guava to use values wherever possible because theyprovide isolation.As Figure 3 shows, each linked list of Bucket objects iscontained inside of its BucketList. A Guava compiler knowsstatically that there are no references from buckets inside ofone bucket list to objects inside another. This may enablepowerful optimizations, as we discuss in Section 4.2.The class Bucket is an object class. When no extendsclause is given, a class is assumed by default to extendObject.2.2.1 Read and Update MethodsIn Guava, all methods must be identi�ed as either read orupdate methods.The put()methods of SharedHashtable and BucketListmodify the state of their instances, and must therefore bedeclared as update methods. By default, methods are as-sumed to be read methods; in the example in Figure 2, thehash() and get() methods of SharedHashtable, and theget() method of BucketList are all read methods.3

public class SharedHashtable extends Monitor fprivate final BucketList[] buckets =new BucketList[SIZE];private int hash(Mobile key) freturn key.hashCode() % buckets.length;gpublic Mobile get(Mobile key) freturn buckets[hash(key)].get(key);gpublic update void put(Mobile key, Mobile data)f buckets[hash(key)].put(key);ggclass BucketList extends Value fprivate Bucket head;public Mobile get(Mobile key) ffor (Bucket b = head; b != null; b = b.next)if (b.key.equals(key))return b.data;return null;gpublic update void put(Mobile key, Mobile data)f head = new Bucket(key, data, head);ggclass Bucket fpublic final Mobile key;public final Mobile data;public Bucket next;public Bucket(Mobile k, Mobile d, Bucket b) fkey = k; data = d; next = b;gg Figure 2: A Shared Hash Table

6KDUHG+DVKWDEOH

%XFNHW/LVW

%XFNHW/LVW

6KDUHG+DVKWDEOH

%XFNHW/LVW

%XFNHW/LVW

%XFNHW/LVW

%XFNHW/LVW

Figure 3: Containment in SharedHashtable instances.Constructors are always update methods, so it is notnecessary to annotate the Bucket constructor.Although value classes are normally deep-copied (exceptfor references to monitors), when a value is de�ned withno update methods (other than its constructors), then in-stances of the value can be passed by reference in the imple-mentation, making them very e�cient. Programmers shouldattempt to de�ne read-only value classes whenever possible.Mutable values should be exchanged by moving rather thancopying wherever possible.2.2.2 Concurrency in MonitorsIn Guava, there are two kinds of locks, just as there are twokinds of methods: read locks and update locks. Read locksare non-exclusive, whereas update locks are exclusive. Thisis an optimization enabled by the fact that Guava is ableto determine which are the read methods. Behaviorally, itshould be impossible to distinguish between an implemen-tation that uses mutual exclusion and one that allows readmethods to execute concurrently.Since SharedHashtable.get() is a read method, it is au-tomatically compiled to use a read lock. This means thatany number of get() methods may be executing concur-rently, providing parallelism and scalability on multiproces-sors.In contrast, Java only provides synchronized methods,which are exclusive. Shared data structures like hash ta-bles must either su�er performance consequences when allmethods are synchronized, or use obscure programming id-ioms that attempt to produce correct results without us-ing locks. These programming idioms are notoriously non-portable across di�erent multiprocessors and may even be4

subject to breakage in the face of aggressive compiler opti-mization.2.2.3 ArraysAs in Java, array classes are de�ned automatically whenthe corresponding array variable is declared. If the ele-ment type is a subclass of Mobile, the array is of categoryValue; otherwise it is of category Object. For example, typeBucketList[] is a subclass of Value, since BucketList is asubclass of Value.If the programmer wants an array to be a monitor or anobject, the above rule can be circumvented by declaring amonitor or object with a public �eld containing the array.Of course, a monitor may not contain a public array whoseelements are not Mobile, since that would violate the ruleprohibiting transferring object references between owners.2.3 Example II: String Bu�erOur second example of programming in Guava is shown inFigure 4. It is loosely patterned on the Java StringBufferclass, but it is an object, not a monitor, designed for usewithin a single thread.2.3.1 Read and Update ParametersIn Guava, methods must also specify whether they updatetheir non-scalar parameters; by default, parameters are read-only.A parameter is updated either when (1) it is the targetof an assignment, (2) an update method is invoked on it,or (3) it is passed to an update parameter of a method orconstructor.The method StringBuffer.print shows the use of anupdate parameter: it prints the characters contained in theStringBuffer to an output stream s. Since the LocalOutput-Stream.print() method is an update method (because itchanges the state of the output stream), s must be declaredas an update parameter.2.3.2 Region Basics: Kept and Lent ParametersAlthough objects may not be concurrently accessed by twomonitors, they may be passed between monitors and evenoperated upon as long as these operations do not lead toconcurrent sharing.To understand how Guava allows passing of objects be-tween monitors, we must introduce the notions of regionsand ownership. At run-time, every object is uniquely con-tained inside of a single monitor or value | that monitor orvalue is its owner.For instance, in Figure 3 the Bucket objects are all uniquelycontained inside their corresponding BucketList values, and

public class StringBuffer fprotected char[] string = new char[16];protected int size = 0;public char getChar(int i) freturn string[i];gpublic void print(update LocalOutputStream s) fs.print(string, size);gpublic update void append(char c) fif (size == string.length) fstring2 = new char[size * 2];for (int i = 0; i < size; i++)string2[i] = string[i];string = string2;gstring[size++] = c;gpublic update void append(lent StringBuffer s) ffor (int i = 0; i < s.size; i++)append(s.getChar(i));gpublic new StringBuffer duplicate() fStringBuffer b = new StringBuffer();b.size = size;b.string = new char[size];for (int i = 0; i < size; i++)b.string[i] = string[i];return b;gg Figure 4: The String Bu�er object classpublic class DataMonitor extends Monitor fStringBuffer data;public void appendData(update StringBuffer target)f target.append(data);g// Rest of code omitted from exampleg Figure 5: The DataMonitor class5

BucketList values are all uniquely contained in their Shared-HashTable monitors. It is impossible for there to be anyreferences between Bucket objects in di�erent monitors orvalues.At compile-time, the Guava compiler assigns a region toevery variable, such that if two variables are in the same re-gion, they either have the same owner, or no owner. Objectshave no owner when they have been allocated with new andnot yet connected to other objects by assignment.While this sounds complicated, in practice it is usuallyvery simple. Programmers need only provide region infor-mation for parameters, and usually this can be done withthe simple annotations kept and lent.A kept parameter has the same region as this. By de-fault, parameters to objects are kept, meaning that the ob-ject upon which the method is invoked and all of its param-eters have the same owner. When writing single-threadedcode that only uses objects, this will always be true: allof the objects will have a single owner associated with thethread in which they are run.On the other hand, a lent parameter is in an anonymousregion of its own, di�erent from the regions of all other vari-ables. A lent parameter is restricted by the compile-timerules of Guava, which will prevent any pointers being writ-ten which link the lent parameter to any other object. Theeasiest way to think of a lent annotation is as a promise notto keep a reference to the parameter. (The restriction worksin both directions, so the lent annotation also promises thatthe method will not store a reference to this inside the ob-ject passed as a lent parameter.) Without this restriction,it might be possible to retain a reference to an object inanother region, and then later two di�erent threads couldconcurrently access the object through these di�erent refer-ences.In Figure 4, the parameter s to the method append() isdeclared lent, indicating that the method does not keep apointer to s.In Figure 5, we show how the StringBuffer.append()method can be used to pass a StringBuffer object to amonitor which then updates the object.By default, all object parameters to monitors are lent(it is an error to pass an object as a kept parameter toa monitor). Therefore, the appendData() method takes alent parameter target, and invokes the append() operationon it, passing its local StringBuffer data. The append()method copies the characters from the local object (data)into the remote object (target).When the appendData() method returns, the modi�eddata will be visible in the caller, but the language rules willhave guaranteed that there was not and will not be anyconcurrent access of the objects involved.

2.3.3 New Return ValuesA method that returns a newly allocated object that hasnot been connected to any other objects and therefore hasno owner, is said to return a new value.Such methods are labelled with the annotation new, asfor instance for the StringBuffer.duplicate() method inFigure 4. The returned value consists entirely of newly allo-cated instances linked together, so the return value is itselfnew.Constructors always generate new return values, unlessthey have kept parameters, in which case the result of theconstructor is in the same region as its kept parameters.It is highly desirable to return new objects, since theymay be linked to objects in any region. We will show anexample of this below.2.4 Example III: RouterSo far the annotations for regions have been quite simple,and usually the defaults su�ce. We now introduce a pro-gramming example that shows how more complex regionannotations can be used. If the example seems somewhatcontrived, it is because there are relatively few situationswhere real programs will need to use such annotations.The class Router in Figure 6 is a monitor that encap-sulates a boolean switch. That switch determines whetherStringBuffer objects from an input stream are routed toone or the other of two output streams.We assume the existence of an object class StringList(not shown), which is a linked list of StringBuffer objects.The operations provided are append(StringBuffer), whichappends to the end of the list, and remove(), which removesa StringBuffer from the head of the list and returns it.2.4.1 Explicit Region Speci�cationsSince the StringList objects all belong to a di�erent ownerfrom the monitor that holds the boolean switch value, theparameters of the move() method are all speci�ed as beingin a single region, named R. This means that the move()method may inter-link the input, out1, and out2 param-eters and the objects reachable from them, but may notretain pointers to those objects in the monitor itself.The move() method calls moveString, after removing the�rst StringBuffer from the input list. The moveString()method in turn calls updateSwitch(), which uses the inputstring as well as its internal state, and possibly informationfrom other monitors, to update the switch variable. The re-sulting value of the switch variable is then used to determineto which list the string is appended.The result is that the Router monitor has re-arrangedlinked structures belonging to a di�erent monitor, while6

public class Router extends Monitor fprivate boolean switch;private StringBuffer default =new StringBuffer("default");public update void move(update StringList input in R,update StringList out1 in R,update StringList out2 in R)f moveString(input.remove(), out1, out2);gpublic update void moveDefault(update StringList out1 in R,update StringList out2 in R)f moveString(default.duplicate(), out1, out2);gprotected update void moveString(update StringBuffer input in R,update StringList out1 in R,update StringList out2 in R)f updateSwitch(input);(switch ? out1 : out2).append(input);gprivate update void updateSwitch(StringBuffer s)f switch = ...gpublic StringBuffer getDefault() freturn default.duplicate();gg Figure 6: The Router monitor class

public class RouteWithName fprivate static Router router;private static final String before = ": [";private static final String after = "]\n";public static update setRouter(Router r) frouter = r;gpublic final String name;public RouteWithName(String s) fname = before + s + after;gpublic global StringBuffer getDefault() freturn router.getDefault();gpublic global update void doMove(update StringList input,update StringList out1,update StringList out1)f input.append(new StringBuffer(name));while (! input.empty())router.move(input, out1, out2);gg Figure 7: The RouteWithName object classmaintaining the guarantee that the Router has not createdpointers that will subsequently allow it to concurrently ac-cess the StringList objects and their dependents.2.4.2 Use of New ParametersThe moveDefault() method illustrates how methods withnew parameters may be used. The moveDefault() methodcalls moveString(), which requires that all three of its pa-rameters be in a single region R.However, since the result of default.duplicate() is new(as de�ned in Figure 4), it satis�es any region. Therefore,the new parameter can be passed to moveString(), and asa result becomes part of the region R to which the otherparameters belong.2.5 Example IV: RouteWithNameOur next example is the class RouteWithName, shown in Fig-ure 7, which demonstrates the use of global methods, final�elds, and class statics.7

RouteWithName is an object that invokes the Routermon-itor (from Figure 6) to route lists of StringBuffer objects.It is a �lter that appends a name to each list that is routed.The Router object that it uses is stored in a class static �eldcalled router, which can be changed with the static methodsetRouter().2.5.1 Local and Global MethodsIn Guava, every method is either local or global. Localmethods do not change the state of monitors: they operateonly on objects and values. Global methods read or updatemonitor state, which can be observed by other threads. Bydefault, all methods of objects and values are local. Meth-ods of monitors are always global, and may not be declaredlocal.So far, these defaults have su�ced in the examples wehave shown: the methods of SharedHashtable, DataMonitor,and Router were global since they were monitor methods,and the methods of BucketList, Bucket, and StringBufferdefaulted to local, as appropriate for objects and valuesthat only operate upon their own internal state.However, the method getDefault() in Figure 7 is amethod of an object which invokes a monitor operation onrouter, and must therefore be declared global. Any meth-ods that call RouteWithName.getDefault() must also be de-clared global, since globality is a transitive property.Methods that invoke global update methods must bedeclared global update, even if they do not change thestate of their own instance variables. For instance, doMove()must be de�ned as a global update method even though itjust operates on its parameters, because the Router.move()method is global update (it is implicitly global becauseRouter is a monitor).2.5.2 Final FieldsFinal �elds of instances may only be assigned and/or up-dated within the body of the constructor or of a privatemethod called only from the constructor. They are consid-ered constant once the constructor has completed.Partially constructed objects are not allowed to be visi-ble outside of constructors. To guarantee this property, con-structors, and methods called from constructors (includingsuperclass methods) may never pass this as a parameter toa method or assign this to a �eld of an instance.Initializer blocks (a Java feature whereby a parameter-less block of code is executed after the constructor has com-pleted) are not considered constructors and do not have thisrestriction. However, they are treated as methods executedafter the constructor, and therefore they are forbidden toupdate final instance variables.

As a consequence, programmers and compiler writersmay assume that any final �eld of category Value is aconstant. For example, the string name in RouteWithNameobjects will never be observed to change its value outside ofthe constructor.Compiler writers may also always return the same resultwhen the same local readmethod is invoked multiple timeson values with equal parameters.For �elds that are objects and monitors (the Referencetypes), final has the same meaning as in Java: the variablealways refers to the same instance, but the data within theinstance is subject to change.2.5.3 Class Objects and Static FieldsA Class instance associated with a class is accessible to allmethods simply by naming the class | it is a global object.Therefore, Class instances are always monitors in Guava.The static �elds of the class are treated as members ofthe class monitor, and they must either be primitive typesor Mobile. Invocations of static methods are synchronized.Access to non-final static instance variables of the classare also synchronized, and treated as read or update op-erations according to whether they read or write the static�elds.In Figure 7, the �elds router, before, and after areall static and therefore belong to the monitor object as-sociated with the class RouteWithName. Even though theyare private, they are still shared by all instances of theRouteWithName class.The �elds before and after are final, and can thereforebe read without locking the class monitor. However, the �eldrouter is not final, so reading its value in the doMove()method causes the class monitor to be read-locked.Similarly, the static method setRouter() changes therouter �eld, and must therefore be declared as an updatemethod; since it is a method on the class monitor, it is bydefault global.The rules for when initialization takes place have beenweakened. The Java rules forbid a class from being initial-ized until the �rst active use. Under these rules, almost allmethods might be global update methods, since they couldtrigger class loading and therefore the running of static ini-tializers. In Guava a class is loaded between the time a classreferring to it is loaded, and the �rst active use of the class,and the use of a class does not count as a global operation.Note that static initializers can themselves cause otherclasses to be loaded, thereby executing other static initializ-ers that might read partially initialized static final �elds.Therefore we can not make the strong guarantees for staticfinal variables that we earlier did for final instance vari-ables.8

public class SimpleMain extends MainThread fpublic void run() fchar c;thisthread.out.println("Running test with" +args[0]);while ((c = thisthread.input.getc()) !=thisthread.input.EOF)thisthread.out.putc(c);gg Figure 8: The SimpleMain thread classpublic class ThreadData fThread thread;public LocalInputStream input;public LocalOutputStream out;public LocalOutputStream err;g Figure 9: The ThreadData class.2.6 Example V: SimpleMainIn this section we show how single-threaded programs arerun in Guava, and the special support for thread-local inputand output.2.6.1 Main ProgramsIn Guava, main programs can not be static methods ofclasses, since they would lock the class object as long asthey were running. Instead, a main program is de�ned asshown in Figure 8: a run() method is de�ned for a class(in this case SimpleMain) extended from the special classMainThread.The MainThread class binds the command-line argumentsto a vector args[], and creates thread-local input, output,and debug streams for the run() method. It then starts therun() method automatically.Normally, subsidiary threads started by the main threadare extended from class UserThread, which does not createthread-local input/output streams, but only a thread-localdebug output stream. It is generally assumed that multi-threaded programs will synchronize their input and outputwith one another.2.6.2 The thisthread ReferenceIn Guava, each method has an additional implicit parametercalled thisthread. The thisthread parameter is primarilyused to perform thread-local input/output. It can also be

used for any data that is global to the thread, but localizedfrom other threads.In any language it is good programming style to avoid theuse of global variables. In Guava it is particularly importantsince global variables require synchronization and globalmethod declarations. The thisthread implicit parameterprovides a convenient way for programmers to avoid usingglobal data in the form of class statics.The thisthread reference returns the value of the thread-Data �eld of the currently running thread, which is of typeThreadData as shown in Figure 9. Before starting the thread,the programmer may replace the default threadData �eldwith one of his own.2.6.3 Single-Thread Input/OutputThe normal use of thisthread for single-threaded input/output is shown in the body of the run() method in Fig-ure 8. Instead of using System.out.println(), the run()method invokes thisthread.out.println(), which has thesame interface but prints to an unshared output stream.Because the LocalOutputStream is an object, it can notbe shared with any other thread. Therefore, writes to �eldsof thisthread.out are local, not global operations, and thesingle-threaded program requires no global annotations.Furthermore, the writes to the output stream are not syn-chronized with the rest of the system.Multi-threaded programs can use the System.out callsfamiliar to Java programmers. However, since System.outis a global static monitor shared between all threads, anymethod that invokes calls like System.out.println() mustbe declared global.2.7 Example VI: Character InterfacesFigure 10 shows various interfaces for performing charac-ter output. The CharOutputStream interface is de�ned asan extension of Reference, indicating that it can be eithera monitor or an object. Shared streams are implementedas monitors, while thread-local streams are implemented asobjects.2.7.1 Restricting SignaturesThe interface CharOutputStream de�nes a single methodputChar() which is both global and update, and writesa character to a (possibly) shared output stream.The LocalCharOutputStream restricts the signature ofputChar() by removing the global annotation from themethod. This declares that the implementation may notinvoke monitor operations, making it a local update.The NullCharOutputStream, which simply discards char-acters fed to it, further restricts the signature by removing9

public interface CharOutputStreamextends Referencef global update void putChar(char c);gpublic interface LocalCharOutputStreamextends CharOutputStream, Objectf update void putChar(char c);gpublic interface NullCharOutputStreamextends LocalCharOutputStreamf void putChar(char c);g Figure 10: Character Output Interfacesthe update annotation, making putChar() a local read-onlymethod.In general, if a derived class overrides a method of abase class, it may restrict the signature. Similarly, if a classimplements an interface, it may restrict the signature. Asignature derived is more restrictive than signature base ifits only annotation di�erences are:1. base is a global method and derived is local.2. base declares a parameter as update and derived de-clares it as read.3. derived declares the return value new and base doesnot.4. base collapses parameter regions of derived, eitherbecause derived declared some parameters lent thatbase declared kept, or in r, or because base coalescestwo or more regions declared as separate regions inderived.The above conditions guarantee that any method satisfyingthe more restrictive signature also satis�es the base classsignature.3 Compile-Time CheckingThere are three kinds of additional static checking: the rulesfor checking that references never cross region boundaries,the rules for read and update, and the rules for global.

3.1 Region Type CheckingWe now formalize the type checking rules that assure thatobjects can never be concurrently accessed. We are lever-aging on the formal race-free type system of Flanagan andAbadi [9, 10]. A particularization of that system can be usedto show Guava to be race-free.In Flanagan and Abadi's system, every method has apermission indicating what locks are held when the methodis invoked, and a protection annotation indicating the lockthat protects updates to that method. Instance variablesare just special cases of methods.In Guava, every variable has a region type, and everymethod and constructor a region type signature. These typesand signatures carry information equivalent to Flanagan andAbadi's permissions and protection annotations.The region type is either the name of a lock or null. Aregion type of null means that the lock is not held, andmust be acquired before invoking methods on the variable.A region type of r means that (a) lock r is always used toprotect the instance, and (b) lock r is always held when thisvariable is in scope. The region type of this and of everyinstance variable is a distinguished name myowner.Objects (or polymorphic types that may denote objects| namely Instance, Reference, or Local) may not have aregion type of null, since invoking them requires obtaininga lock, and objects do not have locks. For of those poly-morphic types, which at run-time may either denote objectsor not, a region type of r means that the lock is held pro-vided that the variable denotes an object. Monitors andvalues will usually have a region type of null, although in-side the monitor or value itelf, the variable this will havea region type of myowner. For the purpose of this analysis,we are treating values as if they are locked; however, sincethere can be no multiple references to a value, such \lock"operations can be optimized away.Therefore every method or constructor has a protectionannotation of myowner, and a permission consisting of theset myowner combined with the region types of the param-eters passed to the method (including the implicit parame-ter thisthread). These region types appear on the regiontype signature. The region type signature for a method hasthe form [pthis = r0; p1 = r1; :::; pn = rn ! pret = rn+1];the region type signature for a constructor has the form[pthis = r0; p1 = r1; :::; pn = rn]. (Methods returning prim-itive or void just omit the pret speci�cation.) Each ri rep-resents the region type of the corresponding parameter piduring the invocation of that method; r0 is the region typeof this, and is the distinguished name myowner.Simple type checking rules applied to region types su�ceto guarantee that any well-formed Guava program is race-10

free.First, we discuss the Guava type rules assuming that ev-ery variable of other than a primitive type has been declaredwith a region type, and that every method and constructorinterface has been declared with a signature annotated withregion types.Second, we present a straightforward type inference al-gorithm which can be applied to actual Guava programsin which type annotations appear on signatures but not onvariable declarations. This algorithm either annotates allvariables with a region type satisfying the extended typechecking rules, or rejects the program.Finally, we map the existing Guava syntax to the nota-tion of this type system, indicating the defaults.3.1.1 Guava Region Type CheckingThere are only four distinct statement types relevant fortype checking:� Method Invocation: The statement isx = y.meth(p1, ..., pn);Assume x has region type tn+1, that y has region typet0, that each pi has region type ti, and that methody.meth has region type signature [pthis = r0; p1 =r1; :::; pn = rn ! pret = rn+1]. Then the statementis well-formed i�:{ For all i; j 2 f0::n+1g; (ri = rj)! (ti = tj_ri =null). This says that if the formal parametershave the same region type, then the correspond-ing actual parameters must have the same regiontype.{ For all i; j 2 f1::ng; ri 6= null ! ti 6= null.This says that if a formal parameter other thanthis is assumed locked, then the correspondingactual parameter must be assumed locked, butthe return value may be assumed locked insidethe method but unlocked on return.� Constructor Invocation: The statement isx = new Class (p1, ..., pn);Assume x has region type t0, that each pi has regiontype ti, and that the appropriate constructor for Classhas region type signature [pthis = r0; p1 = r1; :::; pn =rn]. Then the statement is well-formed if:{ For all i; j 2 f0::ng; ri = rj ! ti = tj . Thissays that if the formal parameters have the sameregion type, so will the actual parameters, andif any formal has region type myowner, then theactual parameter will have the same region typeas the returned instance.

{ For all i; j 2 f1::ng; ri 6= null ! ti 6= null. Thisis the same constraint as for method invocation.{ If Class is Mobile, then t0 = null. This restric-tion forbids monitors or values from being pro-tected by a lock on some other monitor or value.Therefore, outside the monitor or value, the lockis assumed not to be held. The restriction is notstrictly necessary, but it avoids certain complexsituations.� Casting: The statement isx = (Type) y;If Type is Mobile or a subclass, then the region typeof x must be null. Otherwise, either the region typesof x and y must be equal or the region type of x mustbe null.� Return: The statement is:return x;The variable x must have a region type that matchesthe return type rn+1 of the method's region type sig-nature.All Guava statements are in one of the above four forms.For the purpose of this analysis:� Assignments without casts behave like a method callwith signature[pthis = myowner ! pret = myowner].� Fetches to arrays and stores to arrays are treated asinvocations of a fetch method or a store method onthe array object. For arrays of objects, the signatureof the fetch method is [pthis = myowner ! pret =myowner], and the signature of the store method is[pthis = myowner; p1 = myowner !]. For arrays ofmonitors or values, the signature of the fetch methodis [pthis = myowner ! pret = null], and the signatureof the store method is [pthis = myowner; p1 = null!].� Fetches and stores to public �elds are similarly treatedas invocations of a fetch or a store method on the ob-ject.� Nested operations of public �elds that are values aretreated as invocations of methods on the outer object.For example, if a public �eld x of class Foo is declaredwith type int[], then class Foo is treated as if it hadfour methods: a pair of methods to fetch and store aint[], and a pair of methods to fetch and store an int.If these were treated as fetching the value and theninvoking the nested operation, this would not producethe desired results, since updating a copy of the valuedoes not update the value.11

Within a method with signature[pthis = r0; p1 = r1; :::; pn = rn ! pret = rn+1]or constructor with signature[pthis = r0; p1 = r1; :::; pn = rn]all instance variables have region type myowner, unless theyare Mobile or a subclass, in which case they have regiontype null. Parameter pi has region type ri. The variablethis always has region type myowner. The region type ofother variables is assigned by the type inference algorithmdiscussed below.Because of these rules, it is straightforward to show thatthe invariant is maintained that we are holding locks forany region type names that are in scope. Initially, a threadbegins holding only its own lock: myowner is all that is inscope in the run() method of a thread. When invokinga method, if it is an object, then there will be a regiontype name corresponding to its lock. If it is a monitor orvalue, then the region type name will be null, but a lockwill be acquired. In either case, the instance variables thatare locked by myowner will be in scope, and the lock willbe known to be held. Similarly, any parameters with non-null region type names will be known to have their locksheld because they were held by the caller. When returningfrom a method either no lock is released (method was on anobject), or a lock is released, but no references labelled bythe released lock are visible.3.1.2 Type InferenceThe previous rules assumed that every declared variable isannotated with a region type name. We will now show howto take a Guava program with just region type signatures onmethods and constructors, and produce declarations of theregion type name for each variable wherever this is possible.In fact, the above type checking rules provide a straight-forward algorithm for inferring types. We begin by notingthat each instance variable is of region type myowner or nulldepending on its class, that this is always of region typemyowner, and that parameters to methods or constructorsalways have the region type names given on the method'sregion type signature.To permit assigning references of non-null region type tovariables or formal parameters of null region types, we insertextra temporary variables whenever a variable is passed toa formal variable of null region type. That is,x = y.meth(a, b);is analyzed as if it were:

t1 = a;t2 = b;x = y.meth(t1, t2);For every statement where the rules require two regiontypes to be equal, and one is known and the other unknown,the unknown type is set equal to the known one. (This maytrigger further applications of this operation.) Whenever therules require two region types to be equal and they di�er,the program is ill-formed.If after applying the above operations, there remain un-known region types, we can safely set these arbitrarily, e.g.to the return region type.3.1.3 Mapping to Guava syntaxGuava syntax is oriented towards minimizing the amount ofannotation needed. We have seen that variable declarationsrequire no syntax at all to annotate regions, since these typesare determined by the type inference rules.Instance variables are always of type myowner or nulldepending on their category.The pthis component of every method signature is al-ways declared as myowner, hence this component need notbe written.Parameters that are monitors and values are assumed tobe null unless otherwise declared lent, which gives thema unique region type name, or kept, which gives them theregion typo name myowner.Object parameters and return values of methods of ob-jects are assumed to be myowner unless otherwise speci�ed:a parameter of lent or a return value of new implies a dis-tinct region type name; a parameter of in r implies a user-speci�ed region type name.Object parameters and return values of methods of mon-itors and values, or of methods of interfaces not explicitlyinheriting from object are assumed to each have a uniqueregion type name unless otherwise speci�ed: a parameter ofregion r implies a user-speci�ed region type name.3.2 Checking Read and UpdateThese checking rules guarantee that whenever a readmethodof a monitor is called, that read method does not update anyinformation which could become shared if another threadwere to concurrently execute a read method on the samemonitor. It is therefore safe for an implementation to use atwo-phase lock protocol in which read methods can executeconcurrently.A read method may not execute global update meth-ods.A read method may not update any variables whose re-gion type is myowner.12

A read method may not invoke update methods on anyvariables whose region type is myowner.A read method may not pass any variables whose regiontype is myowner to any formal parameter declared update.No method may invoke update methods on parametersthat are not update parameters, or pass such parameters toany formal parameter declared update.3.3 Checking GlobalThese checking rules guarantee that any method or construc-tor invocation that might acquire a lock is declared global.Methods that are global cannot be assumed to be idempo-tent, and methods that are both global and update cannotbe invoked from within read methods.All invocations of monitors, except for fetches of final�elds, are global by default. Such methods do not have tobe declared global.Any method or constructor that invokes a global methodor constructor must be declared global.4 Performance: Java vs. GuavaAt �rst blush it might seem as though Guava programs willsu�er in performance relative to the equivalent Java pro-grams, since all access to any potentially shared mutabledata structure must be synchronized. However, the muchsimpler semantics of Guava can substantially improve bothuniprocessor and multiprocessor performance.Some Java performance problems result from the factthat in Java, an unsynchronized object might be shared, andcompilers must be wary about code transformations thatwould violate memory ordering constraints if the object wereshared by multiple threads. Because of this, compilers mustavoid many conventional optimizations, and must also intro-duce processor-level synchronization operations that wouldnot be needed if it were known that the object could not beconcurrently shared. Paradoxically, this could induce pro-grammers to declare the methods synchronized to get bettercode generation, but that would introduce synchronizationoverhead.Other optimizations are inhibited because Java is notable to track that some methods are purely local and thatsome methods are read-only. For example, in Guava, a localread method is guaranteed to be idempotent unless its returnvalue is declared new. Idempotent means that any resultit produces only depends on the parameters passed to it,and therefore that successive calls with the same parameters(without any intervening update methods) may return thesame result. 11Because of non-determinism (e.g. allocating a new object andreading its hashcode), successive calls will not necessarily return thesame result; however, it is always correct for a compiler to replace the

In this section we describe these performance e�ects indetail.4.1 Multiple Reader ParallelismOne apparent ine�ciency of the serial semantics of monitorsis that threads can lock one another out. In multiprocessorsystems, multiple reader parallelism is essential for achiev-ing high performance. In Java, synchronized methods areexclusive. The only way to achieve general-purpose multiplereader parallelism is to program it explicitly.Implementations of Guava can execute read methods con-currently. This behavior is achieved in a fashion similar totwo-phase locking in transaction systems. When a threadexecutes a monitor method, it acquires a lock on the mon-itor. If the method is an update method, a conventional,exclusive lock is acquired. If the method is a read method,a read lock is acquired. A read lock can be obtained pro-vided that no other thread holds a write lock; there may beother threads holding a read lock.By the rules of Guava, a read method will not executeany global update methods, and therefore it will never at-tempt to acquire another write lock. It will only write tostack variables and to objects passed from its callers, neverto instance variables, or to monitors. All modi�ed variablesare owned by some monitor or value on the stack; there-fore they are invisible to other threads. These objects arealso guaranteed not to update any monitor, including themonitor containing the read method.It is possible for the body of an outer read method toinvoke nested read methods on other monitors. If it does,it must acquire a read lock as before. However, as in two-phase locking, the read lock of the nested method may notbe released until the outer read method completes. 24.2 Lock GranularityAlthough logically, each call to a monitor method is serial-ized, it is possible for Guava to detect that it is more e�cientto use a �ner granularity of locking, while still achieving thesame semantics.Consider the class SharedHashtable introduced in Fig-ure 2 of Section 2. There is a single instance variable, thearray buckets, and each method invocation invokes a muta-tor method on a single component of the array. These twoconditions enable the compiler to implement a separate lockfor each component of the array, rather than a single lock formultiple calls by a single call.2If this were not the case, it would be possible for two read methodsM1 | which reads global variable A and then B, and M2 | whichreads global variable B and then A, to execute concurrently, with M1seeing old A, and new B, and M2 seeing new A and old B. Then therewould be no serial order of M1 and M2 consistent with these results.Since read locks only lock out writers, there is no additional potentialfor deadlock introduced because of retaining these locks.13

the instance of the SharedHashtable. As a result, multiplethreads can invoke method put() on the same instance ofSharedHashtable without locking one another out, providedthat they access di�erent BucketList instances.The lock granularity optimization requires a few addi-tional properties to be met:� It must be possible to de�ne an order of the locks suchthat on all paths those locks that are acquired are ac-quired in the same order. Otherwise, the optimiza-tion could introduce a deadlock that would not havebeen present in the unoptimized version. In this ex-ample, it is straightforward because no path throughthe program ever acquires more than one lock. Ina more complicated but realistic implementation ofSharedHashtable, for example one that periodicallyresized the bucketlist, the analysis would be less straight-forward.� Externally visible side e�ects of update operations wouldhave to be delayed to take place during the lock releasephase. Otherwise some external monitor could observethat the optimized monitor was being executed concur-rently rather than serially. If these side e�ects can'tbe moved, then the optimization cannot be performed.In this example, there are no such side e�ects, becauseno global updates are performed.The implementation is able to exploit the rules of Guava.Each BucketList value in the array owns a disjoint set ofBucket objects. Buckets from one BucketList may notpoint to buckets in another. And of course, the multi-ple BucketList values in the array are themselves disjoint.Without these guarantees, (e.g., in a regular Java implemen-tation) it would be unsafe to assume that each lock protectsa mutually exclusive set of objects.4.3 Multiprocessor IssuesSometimes an optimization will work on certain multipro-cessor architectures and not others. In our opinion, it iseasier to program to a straightforward monitor-based ab-straction and then develop optimizers specialized to thesearchitectures, rather than let the programmer try to code asupposedly portable program using an idiom that only workswell (or at all) on certain architectures.In this section we present some examples.4.3.1 Double-Check ReadsA common programming idiom involves testing a read valuebefore and after an operation, to detect whether a concur-rent write operation has intervened. Such an idiom has been

applied, for example, under the name \Double-Check Lock-ing", to the problem of exactly once initialization [23]. Thee�cacy of this sort of idiom might suggest an argument thata restricted language such as Guava prevents programmersfrom writing explicit code to reduce locking overhead.We counter this argument by noting: (1) that the condi-tions enabling this idiom to be correct require considerablee�ort and skill to detect and prove; (2) that they only workwhen the underlying multiprocessor coherence model meetscertain conditions; and (3) a compiler for Guava can gen-erate the double-check code automatically when it detectsthat the appropriate conditions are met, as we will showbelow.Let us consider the method get of class BucketList usedby SharedHashtable in the example from Section 2. Wehave already discussed that there can be one lock per in-stance of BucketList rather than one lock for the entireSharedHashtable. However, when there are some pathsthrough the method that are read-only (in this case, the onewhere the return is from the interior of the while loop), wecan delay acquiring the write-lock, and transform the codein which read-only operations in e�ect perform optimisticlocking as shown in Figure 11.In our suggested implementation, which is a generaliza-tion of double-check locking derived from optimistic concur-rency control [18] the version number is increased by oneeach time a write lock is acquired, and also each time oneis released. The readers check that the version is even (un-locked) and equal to the version read at the beginning of theoperation.The algorithm works 3 provided:1. The version number is read atomically (volatile)2. The coherence model guarantees that any writes pre-ceding the update to the version number become visi-ble to any reader who sees that the version number isin the even (unlocked) state.3. The read operations being performed optimisticallyare all local, as determined by Guava rules,4. Any updates to update parameters are bu�ered anddelayed until after the �nal version number check, and5. The optimistic code checks explicitly to break in�niteloops or catch exceptions that might occur as a resultof reading incorrect values, as shown in the examplecode where every LIM times around the loop the version3If there is a single potentially interfering write, and an unambigu-ous test to distinguish the old from the new value, then a separateversion number �eld is unneeded, and the algorithm reduces to themore familiar double-check idiom14

volatile int version = 0;Mobile get(Mobile key) fint v = version;int ct = 0;try ffor (Bucket b = head; b != null; b = b.next)f if (ct++ % LIM == 0 &&(v != version || v % 2 == 1))throw new OptimizerException();else if (b.key.equals(key)) fif (v != version || v % 2 == 1)throw new OptimizerException();else return b.data;ggif (v != version || v % 2 == 1)throw new OptimizerException();else return null;g catch (Exception e) f// redo the operationggsynchronized void put(Mobile key, Mobile data) fversion++;head = new Bucket(key, data, head);version++;gFigure 11: Optimization of BucketList.get() using double-check readsnumber is rechecked, and the code is enclosed in a tryblock. 44.3.2 Synchronization OptimizationsThe semantics of the Java unlock operation specify that allmodi�cations made by the executing thread must becomeglobally visible. This makes synchronized methods expen-sive since all dirty cache lines must be synchronized. InGuava, because ownership is explicit, the unlock operationneed only make visible the changes to the current monitor(and any newly allocated monitors). This localization of thesynchronization requirements makes it possible for Guavaprograms to use lower-cost synchronization operations thatonly a�ect single cache lines or words of storage.In particular, a monitor with only one word of data whosemethods are all �nite and non-blocking can be implemented4In the shared hashtable example, this might require unrolling thewhile loop and checking the lock after each N iterations.

using atomic update operations (such as lwarx/stwcx onthe PowerPC, or CMPXCHG on the Pentium). The need for aseparate lock is eliminated. Furthermore, on weakly orderedprocessors like the PowerPC, there is no need for memorybarrier instructions, which are by far the most costly partof synchronized operations.This is illustrated in Figure 12, which shows how anAtomicInteger class in both Java and Guava that imple-ments a fetchAndAdd() method, and a possible compilationinto optimized PowerPC assembly code.One reason that more optimization is possible in Guavais that it is not possible to synchronize on arbitrary objects;even for monitors, all synchronizing operations are de�nedby the monitor itself. This makes it possible for us to omit alock �eld from the AtomicInteger object entirely | we donot need to accomodate synchronized() blocks that mightlock the object. Since we can see by inspecting its class bodythat AtomicInteger never performs a wait() operations, wecan compile a notify() method which does nothing and await()method which raises IllegalMonitorStateException.4.4 Prescient StoresGuava does not have the problem of when it is safe to reordercode accessing purely local stack and instance variables. Forexample, the following problem was brought up by the sec-ond author of this paper during the review of the originalJava Language Speci�cation:public class Prescient fprivate int x = 0;private int y = 0;public int m1 () fint a = x;y = 2;return(a);gpublic int m2 () fint b = y;x = 2;return(b);ggSuppose that some instance of class Prescient is acces-sible to two threads, and that one thread calls method m1,and the other m2. At one time, the Java language speci-�cation required that the execution in which both threadsreturned the value 2 could not occur. Later, the speci�ca-tion was relaxed, but in general not all possible re-orderingsby a compiler are permitted.15

final class AtomicInteger fprivate int value = 0;public synchronized int fetchAndAdd(int addend) fint x = value;value += addend;return x;gg (a) Java program to implement synchronized shared integer; r3 points to the AtomicInteger object; r4 contains the addend; r29 contains thread idaddi r1, r3, lockOffset ; compute address of lock wordlwarx r2, 0, r1 ; load lock and reservecmpwi r2, 0 ; is object unlocked?bne expensiveLocking ; no. go to slow pathstwcx. r29, 0, r1 ; lock object by storing thread idbne .-16 ; if reservation lost, retryisync ; ensure lock held before accessing datalw r1, r3 ; load value fieldadd r2, r1, r4 ; add addend to valuestw r2, 0(r3) ; store updated valuesync ; make updates visible to other processorsclr r2 ; set r2 to 0stw r2, lockOffset(r3) ; unlock object by setting lock word to 0mr r3, r1 ; return old value in r3(b) Optimized JIT compilation of Java code for PowerPCfinal class AtomicInteger extends Monitor fprivate int value = 0;public int fetchAndAdd(int addend) fint x = value;value += addend;return x;gg (c) Guava program to implement synchronized shared integer; r3 points to the AtomicInteger object; r4 contains the addendlwarx r1, 0, r3 ; load value field and reserveadd r2, r1, r4 ; add addend to valuestwcx. r2, 0, r3 ; if reservation still held, store new valuebne .-12 ; on reservation failure, retrymr r3, r1 ; return old value in r3(d) Optimized JIT compilation of Guava code for PowerPCFigure 12: Synchronization optimizations possible due to Guava's localized synchronization semantics.16

In Guava, if Prescient is a Monitor, the methods (an-notated as update) will be serialized, and if Prescient isLocal, it is impossible for multiple threads to access anyinstance. Reordering is always safe.4.5 The Reads Kill ProblemBill Pugh [21] points out a common situation in which non-local reads inhibit the optimization of saving a value in aregister. In his example, p and q are references to an objectwith a public �eld x:int i = p.x;int j = q.x;int k = p.x;In Pugh's example, q might be an alias of p, unknownto the compiler; this is not strictly necessary | q mightinstead be an object that another thread updates strictlyafter updating p.x. Whichever is the case, in any executionin which j receives the updated value of q.x, it is incorrectfor i and k to both receive the old value of p.x. But thismeans that the optimization of saving i to a register mustbe suppressed! This is a real disaster for Java, in whichevery array and every \struct" appears to be a potentiallyshared object: the e�ectiveness of common subexpressionelimination is greatly reduced.The problem is even worse when p and q are arrays: inmany cases, Java compilers can not perform scalar replace-ment of loop-invariant array elements.In Guava, the \Reads Kill" problem will never arise forarrays (which are Values or Objects), and will only arise for\structs" if the instance is explicitly declared as a monitor,and if the �eld x is not final. In short, this will have to bethe case of a truly shared variable, not merely one that thecompiler has to conservatively assume might be shared.4.6 Object Model ImprovementsIn Java, every object may potentially be locked, and lockingis extremely frequent, so most high-performance implemen-tations include a lock �eld in the object header. In Guava,only monitors require lock �elds; objects and values, whichmake up the vast majority of all instances, require no spaceoverhead for locking.A further space optimization that can be performed inGuava is that small instances of final value classes can berepresented without a class pointer and can even be storedin registers. This is possible because there is no aliasing ofvalue instances.It is always safe to allocate large values out of line, andto pass them by reference. (It is unidiomatic in Guava topass values as update parameters, since the updated valuewill not be returned to the caller.)

5 Related WorkJava has been criticized [5] for not providing monitors inthe original strict sense of the term (e.g. [2, 13]). However,the earliest uses of monitors involved highly static languageswithout dynamic loading, object orientation, and other fea-tures of Java.While monitors in their original form have the advan-tage of preventing dangerous concurrent access, the earlyattempts to embed monitors into programming languageswere applied to very static and restricted languages. Inparticular, monitors in Concurrent Pascal [3] were restrictedto passing scalars and arrays of scalars, and programs werestatically loaded, statically instantiated. While such a lan-guage was su�cient to build a small-scale demonstration ofan operating system on a uniprocessor, as was done withSolo [4], it is not su�cient for building complex object-oriented applications on modern multiprocessors.Subsequent languages that included monitors, includingModula [27] and Concurrent Euclid [16] did not provideguaranteed mutual exclusion, either because it was not pos-sible for complex data structures, or (in the case of Mod-ula) because it was deemed too restrictive. Indeed, Wirthstates \we have not adopted [enforcement of mutual exclu-sion] because...there are legitimate applications where sucha language rule would be unnecessarily restrictive. In mostcases it is not, and therefore the rule that accesses to sharedvariables be collected in an interface module is highly recom-mended as a programming principle (rather than a strictlyenforced law)" [26].Wirth's position has been adopted to one degree or an-other by most subsequent concurrent languages, includingJava [12] and Modula-3 [6].The major alternative to monitors has been the message-passing languages. However, these languages are also gener-ally restricted in the data types they can communicate. Theoccam language [17], which is based on Hoare's notation forCommunicating Sequential Processes [14], like ConcurrentPascal, only supports transmission of scalars and arrays ofscalars (in fact, occam does not even guarantee mutual ex-clusion { it requires but does not enforce that two processesdo not concurrently access the same data).Hermes [24] is a message-passing concurrent program-ming language designed and implemented by a group includ-ing the �rst two authors of this paper. Hermes eliminatedpointers altogether: there were processes (monitor objects),ports (capabilities to invoke a method of an object), and arich set of compiler-de�ned value types, including lists andkeyed relations. However, Hermes did not allow users tode�ne value types, or to build the usual types of pointer-based data structures. Such structures had to be simulated17

with keys and keyed relations. Aliasing still had to be de-tected, and the keys were e�ectively untyped and thereforeultimately less satisfactory than pointers.Meanwhile, many researchers have attacked the problemfrom the standpoint of detecting the absence of aliasing andside e�ects to enable automatic parallelization. Reynolds[22] described a language in which interference between pro-cedures could be detected based on bindings of identi�ers.His technique was relatively simple but led to overly conser-vative restrictions and reduced parallelism.Lucassen and Gi�ord [19] describe polymorphic e�ectsystems, which, like Guava, include speci�cations of bothside-e�ects and regions. However, programming in their lan-guage, MFX, was cumbersome and object-oriented featureswere not supported. Furthermore, a monitor abstractionwas not provided.Most similar to our work are the Islands of Hogg [15]. Is-lands are similar to the Value and Monitor types in Guava,in that external references to the internal objects are disal-lowed syntactically. Hogg eliminated the use of region spec-i�cations because he believed them to be too complex; how-ever, we believe that such an approach sacri�ces too muchexpressiveness in the language. As with the FX work, Is-lands did not include a monitor abstraction to prevent dataraces.The annotations used in Guava to prevent races are sim-ilar in nature to annotations used for other purposes. Whatsuch annotations have in common is that they all all ex-tend the typical type information provided in declarationswith added semantic information that may be checked atcompile-time.Promises [7] is an annotation method for Java that ex-presses properties that enable structural changes in a sys-tem. Promises support e�ect analysis by expressing poten-tial read and write e�ects of a method in a manner similarto Guava's. However, their purpose was to allow structuralchanges based on those e�ects, while Guava requires knowl-edge of these e�ects in order to control synchronized accessesappropriately. Promises also has an annotation limitedthat is similar to lent parameters in Guava. Since raceprevention was not a concern, Promises does not considerannotations that indicate potential cross-assignments, suchas Guava's regions.Extended Static Checking [8] implements an annotationscheme that allows checking for various programming errorsincluding races. In this approach the annotations specifywhich locks protect which shared variables, and a check atcompile-time ensures that variables are never accessed with-out holding the corresponding locks. This approach to racedetection is not comprehensive in that it does not certifythat a program is race-free as in Guava.

Flanagan and Abadi [9, 10] de�ne a type system whichtracks permissions and supports functions to be parame-terized by lock types. The region type checking system ofGuava is essentially a specialization of this type system.Flanagan and Freund [11] have a recent paper intro-ducing a dialect RaceFreeJava with similar objectives toGuava. Their dialect has more �ne-grained annotationsthan Guava: individual variables are annotated by the lockthat guards them; locks must be acquired explicitly withsynchronized statements. In contrast, Guava uses broadcategories of monitor, value, and object, with general rulesapplying to all members of the category. Flanagan and Fre-und discuss properties of a tool that checks unannotatedJava programs for races and heuristically suggests annota-tions.6 ConclusionsWe have described Guava, a programming language simi-lar to Java that provides a true monitor abstraction thatguarantees that all programs are free of data races. Guavasigni�cantly extends previous work on monitors by allowingmuch richer data types to be exchanged between monitors,and by providing for concurrent execution of reader meth-ods.Guava programmers must divide class instances into mon-itors, values, and objects, and must provide additional pro-gram annotations not required by Java. However, program-mers writing single-threaded programs do not need to pro-vide any concurrency-related annotations | the complexi-ties and the performance penalties related to multi-threadingare only imposed on programmers writing multi-threadedcode. The compiler and underlying hardware can also ag-gressively reorder operations within single-threaded code,with concern for the e�ects of other threads, providing ahigher level of performance in the common case.For the programmer of multithreaded applications, Guavao�ers a simpler programming model than Java, replacing theconcepts of synchronized memory, volatile memory, andunsynchronized memory with a single safe monitor model.We believe that this tradeo� bene�ts both programmers andJava implementers. The simpler model makes it easier forthe programmer to get the synchronization right, and alsoprovides much stronger information that the compiler canuse as a basis for optimizations like multiple-reader paral-lelism, double-check reads, and multi-granularity locking.ACKNOWLEDGMENTSWe thank Joshua Auerbach, Deepak Goyal, David Grove,Chet Murthy, and V.C. Sreedhar for their helpful sugges-tions with this work.18

A Changes to Java SyntaxThe di�erence between Java syntax and Guava syntax areminimal. The keywords synchronized and volatile areremoved; the keywords update, global, lent, and in areadded.We present the syntax using the informal notation ofmain body of the Java Language Speci�cation [12], whichcan be straightforwardly converted into a LALR(1) gram-mar as in Chapter 19 of the speci�cation.The SynchronizedStatement is eliminated, and the fol-lowing elements of the Java grammar are changed:FieldModi�er: one ofpublic protected privatestatic final transientMethodModi�er: one ofpublic protected private abstract static nativefinal read update local globalConstructorModi�er: one ofpublic protected private globalFormalParameter:FormalModi�ersopt Type VariableDeclaratorId RegionoptFormalModi�ers:FormalModi�erFormalModi�ers FormalModi�erFormalModi�er: one ofread update kept lent finalRegion:in Identi�erResultType:Type Regionoptnew TypevoidB Type ConversionsCertain changes to the compile-time type conversion rulesfollow in a straightforward way from the builtin class hier-archy, and from the following two language changes:1. It is permissible for interfaces to extend classes, inwhich case any instance implementing the interfacemust be of that class or a subclass. Only the built-in category classes Object, Monitor, and Value can beso extended.

2. Arrays are subclasses of Value or Object, and maytherefore be cast to this type, or to the type of the in-terfaces they implement (Local and Mobile, or Localand Reference, respectively).As a consequence, the following are the changed rulesfor assignment conversion from source S to target T, whichare also the rules for when a cast conversion is known to belegal at compile-time without a run-time check. These rulesgenerally apply to the category classes (Object, Monitor,and Value).1. If S is an interface extending a category class, and T aclass, then T must be the category class or Instance.2. If S is a Value (or Object) array type, and T is a classor interface type, then T must be Value (or Object),or an interface implemented by Value (or Object).The following are the changed rules for when a cast con-version is illegal at compile time:1. If S is a class type, and T an interface extending acategory class then a conversion is illegal unless S isthe category class or one of its subclasses.2. If S is an interface type extending a category class andT is a class type that is not �nal, then a conversionis illegal unless T is the category class or one of itssubclasses.3. If S and T are both interface types extending a cate-gory class, then the conversion is illegal if either thetwo interfaces have methods with the same name thatcan't coexist in any class, or if they don't extend thesame category class.4. If S is an Object array type, and T is a class type, thenthe conversion is illegal unless T is Instance, Object,Reference, or Local. Similarly, if S is a Value arraytype, and T is a class type, then the conversion is illegalunless T is Instance, Value, Mobile, or Local.5. If S is a class type and T an Object array type, thenS must be Interface, Object, Local, or Reference.Similarly, if S is a class type and T is a Value arraytype, then S must be Interface, Value, Mobile, orLocal.C Translating to Java BytecodesIn this section, we describe how to translate Guava to stan-dard Java class �les. For presentation purposes, we describethe translation as a source-to-source translation.19

For the most part, the translation simply duplicates theJava source code; the annotations (side-e�ect modi�ers, re-gion annotations, and so on) trigger additional compile-timechecking and are stored in the bytecodes as annotations.C.1 Guava ClassesThe classes Instance, Object, Monitor, Value, and the in-terfaces Local, Mobile, and Reference are all representedas interfaces because the standard Java class hierarchy can-not represent multiple inheritance. The dollar sign is usedto distinguish names introduced by translation ($Instance,$Object, etc). The synchronization method calls are un-available, however, on any class that does not implementthe interface $Monitor. The interface $Instance introducesthe methods $copy(), $eq(), and $this().C.2 User-De�ned Classes and InterfacesWe use the abstract classes $MonitorImpl, $ObjectImpl and$ValueImpl implement the corresponding interface. De�ni-tions of user-de�ned classes that extend Object, Monitor,or Value are translated into de�nitions of subclasses of thecorresponding implementation class. User-de�ned classesthat extend other user-de�ned classes are translated withoutmodifying their extends or implements clauses.For exampleclass Foo extends Value f ... gclass Bar extends Foo f ... gis translated intoclass Foo extends $ValueImpl f ... gclass Bar extends Foo f ... gDe�nitions of user-de�ned interfaces extending a cate-gory class are translated into interfaces that extend the in-terface implemented by that class. For instance,interface X extends Value, Y f ... gis translated intointerface X extends $Value, Y f ... gC.3 Copying and Equality TestingEach subclass of Value and Monitor supports a copy()method,which is automatically generated by the translator. Assign-ment of a Value automatically invokes the copy() method.Assignment of a Monitor does not; the copy() method mustbe invoked explicitly. Assignment of a Mobile or Instancetests the category at run time and invokes either Java as-signment or the copy() method.The copy() method creates a new instance of the Valueor Monitor as follows:

1. Each contained value is recursively copied.2. Each contained monitor is copied by reference.3. Each contained object is conditionally copied: thatis, the �rst time a reference is encountered, a copy ismade; subsequent copies of that reference are trans-formed into references to the initial copy.To achieve this, each value and monitor class is given amethod$Instance $copy() f return $doCopy(new $Env());gIn addition, each value class has the following method gen-erated:$Instance $copy($Env e) f return $copy(); gand each monitor class has the following method generated:$Instance $copy($Env e) f return this; gAnd each object class is given a method$Instance $copy($Env e) f$Object o = e.lookup(this);if (o != null) return o;return $doCopy(e);gEach class Foo is given a method with the signatureFoo $doCopy($Env e)which performs the following steps:1. The clone() method is invoked to create the new in-stance thecopy and replicate the �elds from the oldobject.2. If the class being copied is an Object, it performse.register(this, thecopy).3. For each instance variable x of type T that is neither amonitor nor a primitive typeif (x != null) thecopy.x = (T) x.$copy(e);4. For each variable of array type, inline the array copysince there are no methods for arrays.Finally, a copy() method is added which returns an ob-ject of the class type by invoking the $doCopy() method.The generation of the $eq() method that is used to im-plement equality testing of values is performed in an analo-gous manner.Note that the copy operation must preserve the hashcodes of objects across the copy. Otherwise, it might bepossible to copy a value containing a hash table of objects20

such that the objects in the hash table would no longer bein their proper hash buckets. This does not adversely af-fect hash code distribution, since objects inside of one valuedomain are isolated from objects in any other value domain.C.4 ArraysIn Guava, arrays are subclasses either of Object or of Value.However, if we translate Guava arrays to Java arrays, theconventional type checking doesn't work, since Java arrayscan only be subclasses of java.lang.Object, and can onlyimplement the interface Cloneable. We could perform theGuava checking at compile-time, but the run-time checks(that is, for casts and ArrayStoreExceptions) would notwork and Java would not allow an array to be passed to aformal parameter of type $Value, for instance.Therefore, arrays are represented as arrays when they arein variables of array type, and are enclosed in \wrapper" ob-jects when they are in variables of type Instance, Local,Mobile, Reference, Value, or Object. This means thatcasts (including implicit casts) between arrays and thesetypes involve an active operation. For each array type, thereis a wrapper. For instance, the wrapper for arrays of theprimitive type int is:class Wrapper$int implements $Value fpublic int[] array;Wrapper$int (int[] a) f array = a; gpublic Object $this() f return array; gpublic $Value $copy() f ... gpublic boolean $eq($Instance x) f ... ggAll other value objects have method $this returningthis. Before invoking the operations instanceof and caststo array, the $this() method must �rst be invoked. TheGuava codeInstance v;int[] ia;if (v instanceof int[])ia = (int[]) v;is translated into Java as$Instance v;int[] ia;if (v instanceof $Value &&(($ValueImpl)v).$this() instanceof int[])ia = ((int[])(($ValueImpl) v.$this())).copy();Objects can be similarly wrapped. Testing for == on ob-jects that might be wrappers for arrays requires that $thisbe invoked �rst, because we need to test equality of the ref-erence to the wrapped arrays, not equality of the wrappers.

C.5 Other Translations� value-- becomes temp = value; value = null;� value1 = value2 becomes value1 = value2.copy().A translator that performs liveness analysis can elimi-nate copying by converting the Guava assignment to aJava assignment whenever value2 is dead following theassignment. This e�ectively converts a copy to a movewhen the variable being copied is no longer needed.� value1 == value2 becomes value1.$eq(value2).C.6 SynchronizationIn general, all monitor methods must be synchronized. How-ever, when private or protected monitor methods are onlydispatched through this (either explicitly or implicitly),then those methods can be generated without the synchronizedmodi�er, because they will always be synchronized by theenclosing caller.Accesses to non-final �elds of monitors other than throughthis must be enclosed in a synchronized() block that locksthe monitor object.References[1] Alpha Architecture Committee. Alpha Architec-ture Reference Manual, third ed. Digital Press, 1998.[2] Brinch Hansen, P. Structured multiprogramming.Commun. ACM 15, 7 (July 1972), 574{578.[3] Brinch Hansen, P. The programming language Con-current Pascal. IEEE Trans. Softw. Eng. SE-1, 2 (June1975), 199{207.[4] Brinch Hansen, P. The Solo operating system. Soft-ware { Practice and Experience 6, 2 (Apr.{June 1976),141{200.[5] Brinch Hansen, P. Java's insecure parallelism. SIG-PLAN Notices 34, 4 (Apr. 1999), 38{45.[6] Cardelli, L., Donahue, J., Glassman, L., Jordan,M., Kalsow, B., and Nelson, G. Modula-3 report.Tech. Rep. ORC-1, Olivetti Research Center, 1988.[7] Chan, E. C., Boyland, J. T., and Scherlis, W. L.Promises: Limited speci�cations for analysis and ma-nipulation. In Proceedings of the IEEE InternationalConference on Software Engineering (ICSE 98) (Apr.1998), pp. 167{176.[8] Detlefs, D. L., Leino, K. R. M., Nelson,G., and Saxe, J. B. Extended static checking.Tech. Rep. 159, Compaq Systems Research Center,http://www.research.digital.com/SRC, 1998.21

[9] Flanagan, C., and Abadi, M. Object types againstraces. In CONCUR '99: Concurrency Theory, 10th In-ternational Conference (1999), Springer, pp. 288{303.[10] Flanagan, C., and Abadi, M. Types for safe lock-ing. In Proceedings of the 8th European Symposium onProgramming, ESOP '99 (Mar. 1999), Springer-Verlag,pp. 91{108.[11] Flanagan, C., and Freund, S. N. Type-based racedetection for java. In Proceedings of the ACM SIG-PLAN 2000 Conference on Programming Language De-sign and Implementation (June 2000).[12] Gosling, J., Joy, B., and Steele, G. The Java Lan-guage Speci�cation. Addison-Wesley, Reading, Mass.,1996.[13] Hoare, C. A. R. Monitors: An operating systemstructuring concept. Commun. ACM 17, 10 (Oct.1974), 549{557.[14] Hoare, C. A. R. Communicating Sequential Processes.Prentice-Hall, Englewood Cli�s, New Jersey, 1985.[15] Hogg, J. Islands: Aliasing protection in object-oriented languages. In Proceedings of the 1991 ACMConference on Object Oriented Programming Systems,Languages, and Applications (OOPSLA) (Phoenix,Arizona, Oct. 1991), ACM Press, New York, New York.[16] Holt, R. C. Concurrent Euclid, the Unix System, andTunis. Addison-Wesley, Reading, Mass., 1983.[17] Jones, G. Programming in occam. Prentice-Hall, En-glewood Cli�s, New Jersey, 1987.[18] Kung, H. T., and Robinson, J. T. On optimisticmethods for concurrency control. ACM Transactionson Database Systems 6, 2 (June 1981).[19] Lucassen, J. M., and Gifford, D. K. Polymorphice�ect systems. In Conference Record of the FifteenthAnnual ACM Symposium on Principles of Program-ming Languages (Jan. 1988), ACM SIGPLAN Notices,ACM Press.[20] May, C., Silha, E., Simpson, R., and Warren, H.,Eds. The PowerPC Architecture. Morgan Kaufmann,1994.[21] Pugh, W. Fixing the Java memory model. In Proceed-ings of the ACM Java Grande Conference (San Fran-cisco, 1999).

[22] Reynolds, J. C. Synactic control of interference. InConference Record of the Fifth ACM Symposium onPrinciples of Programming Languages (Tucson, Ari-zona, Jan. 1978), ACM Press, New York, New York,pp. 39{46.[23] Schmidt, D. C., and Harrison, T. Double-checkedlocking. In Pattern Languages of Program Design 3(Reading, Mass., 1998), Addison-Wesley, pp. 363{375.[24] Strom, R. E., Bacon, D. F., Goldberg, A.,Lowry, A., Yellin, D., and Yemini, S. A. Her-mes: A Language for Distributed Computing. Series inInnovative Technology. Prentice-Hall, Englewood Cli�s,New Jersey, 1991.[25] Weaver, D. L., and Germond, T., Eds. The SPARCArchitecture Manual, Version 9. Prentice Hall, 1994.[26] Wirth, N. Design and implementation of Modula.Software { Practice and Experience 7, 1 (Jan.{Feb.1977), 67{84.[27] Wirth, N. Modula: a language for modular multi-programming. Software { Practice and Experience 7, 1(Jan.{Feb. 1977), 3{35.

22