From 7873ee98f77824da6de23cc58e0f13239c5d7fa7 Mon Sep 17 00:00:00 2001 From: Michael Bayne Date: Sat, 4 Aug 2001 00:39:44 +0000 Subject: [PATCH] Added facilities for creating named locks on distributed objects that will allow services to prevent repeated invocations of themselves from stepping on one another because of the serial nature of event processing. git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@171 542714f4-19e9-0310-aa3c-eee0fc999fb1 --- .../com/threerings/presents/dobj/DObject.java | 107 +++++++++++++++++- .../presents/dobj/ReleaseLockEvent.java | 20 ++-- 2 files changed, 117 insertions(+), 10 deletions(-) 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; }