diff --git a/src/java/com/threerings/presents/dobj/DObject.java b/src/java/com/threerings/presents/dobj/DObject.java index 3ae8dd5c4..3ed20f68e 100644 --- a/src/java/com/threerings/presents/dobj/DObject.java +++ b/src/java/com/threerings/presents/dobj/DObject.java @@ -1,5 +1,5 @@ // -// $Id: DObject.java,v 1.18 2001/08/03 02:11:40 mdb Exp $ +// $Id: DObject.java,v 1.19 2001/08/04 00:39:44 mdb Exp $ package com.threerings.cocktail.cher.dobj; @@ -129,6 +129,110 @@ public class DObject } } + /** + * At times, an entity on the server may need to ensure that events it + * has queued up have made it through the event queue and are applied + * to their respective objects before a service may safely be + * undertaken again. To make this possible, it can acquire a lock on a + * distributed object, generate the events in question and then + * release the lock (via a call to releaseLock) which + * will queue up a final event, the processing of which will release + * the lock. Thus the lock will not be released until all of the + * previously generated events have been processed. If the service is + * invoked again before that lock is released, the associated call to + * acquireLock will fail and the code can respond + * accordingly. An object may have any number of outstanding locks as + * long as they each have a unique name. + * + * @param name the name of the lock to acquire. + * + * @return true if the lock was acquired, false if the lock was not + * acquired because it has not yet been released from a previous + * acquisition. + * + * @see #releaseLock + */ + public boolean acquireLock (String name) + { + // create our lock array if we haven't already. we do all this + // jockeying rather than just use something like an ArrayList to + // be memory efficient because there may be very many distributed + // objects + if (_locks == null) { + _locks = new String[2]; + } + + // scan the lock array to see if this lock is already acquired + int slot = -1; + for (int i = 0; i < _locks.length; i++) { + if (_locks[i] == null) { + // keep track of this for later + slot = i; + } else if (name.equals(_locks[i])) { + return false; + } + } + + // if we didnt' find a blank slot in our previous scan, we'll have + // to expand the locks array + if (slot == -1) { + String[] locks = new String[_locks.length*2]; + System.arraycopy(_locks, 0, locks, 0, _locks.length); + slot = _locks.length; + } + + // place our lock in the array and let the user know that they + // acquired the lock + _locks[slot] = name; + return true; + } + + /** + * Queues up an event that when processed will release the lock of the + * specified name. + * + * @see #acquireLock + */ + public void releaseLock (String name) + { + // queue up a release lock event + ReleaseLockEvent event = new ReleaseLockEvent(_oid, name); + _mgr.postEvent(event); + } + + /** + * Don't call this function! It is called by a remove lock event when + * that event is processed and shouldn't be called at any other time. + * If you mean to release a lock that was acquired with + * acquireLock you should be using + * releaseLock. + * + * @see #acquireLock + * @see #releaseLock + */ + protected void clearLock (String name) + { + // track the lock index for reporting purposes + int lockidx = -1; + + // scan through and clear the lock in question + if (_locks != null) { + for (int i = 0; i < _locks.length; i++) { + if (name.equals(_locks[i])) { + _locks[i] = null; + lockidx = i; + break; + } + } + } + + // complain if we didn't find the lock + if (lockidx == -1) { + Log.info("Unable to clear non-existent lock [lock=" + name + + ", dobj=" + this + "]."); + } + } + /** * Checks to ensure that the specified subscriber has access to this * object. This will be called before satisfying a subscription @@ -304,4 +408,5 @@ public class DObject protected int _oid; protected DObjectManager _mgr; protected ArrayList _subscribers = new ArrayList(); + protected String[] _locks; } diff --git a/src/java/com/threerings/presents/dobj/ReleaseLockEvent.java b/src/java/com/threerings/presents/dobj/ReleaseLockEvent.java index 92236f249..90f10a3dd 100644 --- a/src/java/com/threerings/presents/dobj/ReleaseLockEvent.java +++ b/src/java/com/threerings/presents/dobj/ReleaseLockEvent.java @@ -1,5 +1,5 @@ // -// $Id: ReleaseLockEvent.java,v 1.1 2001/08/04 00:32:11 mdb Exp $ +// $Id: ReleaseLockEvent.java,v 1.2 2001/08/04 00:39:44 mdb Exp $ package com.threerings.cocktail.cher.dobj; @@ -11,10 +11,13 @@ import java.lang.reflect.Method; import com.threerings.cocktail.cher.dobj.io.ValueMarshaller; /** - * A release lock event is dispatched at the end of a chain of attribute - * change events to release a lock that is intended to prevent some - * application defined activity from happening on the distributed object - * until those events have been processed. + * A release lock event is dispatched at the end of a chain of events to + * release a lock that is intended to prevent some application defined + * activity from happening until those events have been processed. This is + * an entirely cooperative locking system, meaning that the application + * will have to explicitly attempt acquisition of the lock to find out if + * the lock has yet been released. These locks don't actually prevent any + * of the distributed object machinery from functioning. * * @see DObjectManager#postEvent */ @@ -53,13 +56,13 @@ public class ReleaseLockEvent extends TypedEvent } /** - * Applies this attribute change to the object. + * Applies this lock release to the object. */ public boolean applyToObject (DObject target) throws ObjectAccessException { - // pass the new value on to the object - target.setAttribute(_name, _value); + // clear this lock from the target object + target.clearLock(_name); return true; } @@ -90,5 +93,4 @@ public class ReleaseLockEvent extends TypedEvent } protected String _name; - protected Object _value; }