src/share/classes/sun/rmi/server/Activation.java

Print this page
rev 3756 : 6896297: (rmi) fix ConcurrentModificationException causing TCK failure
Reviewed-by: XXX

*** 28,37 **** --- 28,38 ---- import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; + import java.io.ObjectOutputStream; import java.io.OutputStream; import java.io.PrintStream; import java.io.PrintWriter; import java.io.Serializable; import java.lang.Process;
*** 132,141 **** --- 133,192 ---- * @author Ann Wollrath * @since 1.2 */ public class Activation implements Serializable { + /* + * Synchronization occurs over several objects in this code. + * These objects are: + * + * - startupLock + * - initLock + * - Activation.this + * - instances of GroupEntry + * - log (a ReliableLog) + * - idTable (a HashMap) + * - groupTable (a HashMap) + * + * The startupLock and initLock locks are used only during + * initialization and shutdown. The lock on Activation.this + * is used to guard groupSemaphore and to guard writes to the + * shuttingDown flag (which is volatile and which is read + * without synchronization). + * + * Several operations on GroupEntry instances hold a lock on + * that instance while performing logging operations and while + * while manipulating idTable or groupTable. + * + * Log operations, notably addLogRecord(), hold a lock on the + * log instance. (Methods of the log itself also take locks + * on the log object.) The log.snapshot() operation serializes + * the log, including instances of Activation (this class), + * which in turn must lock idTable and groupTable to prevent + * concurrent modification during serialization. + * + * The idTable and groupTable HashMaps are locked while simple + * operations are performed on them. Note that we cannot + * straightforwardly convert these to ConcurrentHashMap, + * because it has a different serialized form from HashMap. + * + * During serialization, the writeObject() method of this class + * takes locks on groupTable and idTable in that order. + * + * Based on the above paths through the code, a partial + * ordering for the above locks must be as follows: + * + * 1. GroupEntry instances + * 2. log + * 3. groupTable + * 4. idTable + * + * For example, code may lock the log while holding the + * lock for a GroupEntry, but code must not lock the log + * while holding a lock on groupTable. + */ + /** indicate compatibility with JDK 1.2 version of class */ private static final long serialVersionUID = 2921265612698155191L; private static final byte MAJOR_VERSION = 1; private static final byte MINOR_VERSION = 0;
*** 272,281 **** --- 323,345 ---- e.printStackTrace(); } } } + private void writeObject(ObjectOutputStream ostream) throws IOException { + // Synchronize on groupTable and idTable while writing them to the + // object stream in order to avoid ConcurrentModificationException. It + // might be preferable to synchronize them individually, but then we'd + // have to write the fields individually instead of using + // defaultWriteObject(). + synchronized (groupTable) { + synchronized (idTable) { + ostream.defaultWriteObject(); + } + } + } + private static class SystemRegistryImpl extends RegistryImpl { private static final String NAME = ActivationSystem.class.getName(); private final ActivationSystem systemStub;
*** 513,527 **** checkShutdown(); RegistryImpl.checkAccess("ActivationSystem.unregisterGroup"); // remove entry before unregister so state is updated before // logged synchronized (groupTable) { ! GroupEntry entry = getGroupEntry(id); groupTable.remove(id); - entry.unregisterGroup(true); } } public ActivationDesc setActivationDesc(ActivationID id, ActivationDesc desc) throws ActivationException, UnknownObjectException, RemoteException --- 577,592 ---- checkShutdown(); RegistryImpl.checkAccess("ActivationSystem.unregisterGroup"); // remove entry before unregister so state is updated before // logged + GroupEntry entry; synchronized (groupTable) { ! entry = getGroupEntry(id); groupTable.remove(id); } + entry.unregisterGroup(true); } public ActivationDesc setActivationDesc(ActivationID id, ActivationDesc desc) throws ActivationException, UnknownObjectException, RemoteException