First pass at a distributed lock system. Barbie says, "Concurrency is
hard!" git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@4533 542714f4-19e9-0310-aa3c-eee0fc999fb1
This commit is contained in:
@@ -0,0 +1,139 @@
|
||||
//
|
||||
// $Id$
|
||||
|
||||
package com.threerings.presents.peer.data;
|
||||
|
||||
import com.samskivert.util.ObjectUtil;
|
||||
|
||||
import com.threerings.io.SimpleStreamableObject;
|
||||
|
||||
import com.threerings.presents.dobj.DSet;
|
||||
|
||||
/**
|
||||
* Describes a lock held by one of the peers.
|
||||
*/
|
||||
public class Lock extends SimpleStreamableObject
|
||||
implements DSet.Entry
|
||||
{
|
||||
/**
|
||||
* Identifies a locked resource by its type and type-specific identifier.
|
||||
*/
|
||||
public static class Name extends SimpleStreamableObject
|
||||
implements Comparable
|
||||
{
|
||||
/**
|
||||
* Creates a new lock name.
|
||||
*
|
||||
* @param type the type of resource. Only names with the same type will have their ids
|
||||
* compared
|
||||
* @param id the resource instance identifier. Can be <code>null</code> for resources with
|
||||
* only one instance
|
||||
*/
|
||||
public Name (String type, Comparable id)
|
||||
{
|
||||
_type = type;
|
||||
_id = id;
|
||||
}
|
||||
|
||||
public Name ()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the resource type.
|
||||
*/
|
||||
public String getType ()
|
||||
{
|
||||
return _type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the resource instance identifier.
|
||||
*/
|
||||
public Comparable getId ()
|
||||
{
|
||||
return _id;
|
||||
}
|
||||
|
||||
// documentation inherited from interface Comparable
|
||||
public int compareTo (Object other)
|
||||
{
|
||||
Name oname = (Name)other;
|
||||
int v1 = _type.compareTo(oname._type);
|
||||
if (v1 != 0 || _id == null) {
|
||||
return v1;
|
||||
}
|
||||
@SuppressWarnings("unchecked") int v2 = _id.compareTo(oname._id);
|
||||
return v2;
|
||||
}
|
||||
|
||||
@Override // documentation inherited
|
||||
public int hashCode ()
|
||||
{
|
||||
return _type.hashCode() + (_id == null ? 0 : _id.hashCode());
|
||||
}
|
||||
|
||||
@Override // documentation inherited
|
||||
public boolean equals (Object other)
|
||||
{
|
||||
Name oname = (Name)other;
|
||||
return _type.equals(oname._type) &&
|
||||
ObjectUtil.equals(_id, oname._id);
|
||||
}
|
||||
|
||||
protected String _type;
|
||||
protected Comparable _id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a newly acquired lock.
|
||||
*/
|
||||
public Lock (Name name, String owner)
|
||||
{
|
||||
_name = name;
|
||||
_owner = owner;
|
||||
}
|
||||
|
||||
/**
|
||||
* No-arg constructor for deserialization.
|
||||
*/
|
||||
public Lock ()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the locked resource.
|
||||
*/
|
||||
public Name getName ()
|
||||
{
|
||||
return _name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the owner of the lock.
|
||||
*/
|
||||
public String getOwner ()
|
||||
{
|
||||
return _owner;
|
||||
}
|
||||
|
||||
// documentation inherited from interface DSet.Entry
|
||||
public Comparable getKey ()
|
||||
{
|
||||
return _name;
|
||||
}
|
||||
|
||||
@Override // documentation inherited
|
||||
public boolean equals (Object other)
|
||||
{
|
||||
Lock olock = (Lock)other;
|
||||
return olock._name.equals(_name) &&
|
||||
olock._owner.equals(_owner);
|
||||
}
|
||||
|
||||
/** Identifies what is locked. */
|
||||
protected Name _name;
|
||||
|
||||
/** The name of the node that owns the lock. */
|
||||
protected String _owner;
|
||||
}
|
||||
@@ -31,6 +31,23 @@ import com.threerings.presents.dobj.DSet;
|
||||
*/
|
||||
public class NodeObject extends DObject
|
||||
{
|
||||
// AUTO-GENERATED: FIELDS START
|
||||
/** The field name of the <code>clients</code> field. */
|
||||
public static final String CLIENTS = "clients";
|
||||
|
||||
/** The field name of the <code>locks</code> field. */
|
||||
public static final String LOCKS = "locks";
|
||||
|
||||
/** The field name of the <code>acquiringLock</code> field. */
|
||||
public static final String ACQUIRING_LOCK = "acquiringLock";
|
||||
|
||||
/** The field name of the <code>releasingLock</code> field. */
|
||||
public static final String RELEASING_LOCK = "releasingLock";
|
||||
|
||||
/** The field name of the <code>cacheData</code> field. */
|
||||
public static final String CACHE_DATA = "cacheData";
|
||||
// AUTO-GENERATED: FIELDS END
|
||||
|
||||
/** Used for informing peers of changes to persistent data. */
|
||||
public static class CacheData implements Streamable
|
||||
{
|
||||
@@ -49,20 +66,21 @@ public class NodeObject extends DObject
|
||||
this.cache = cache;
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// AUTO-GENERATED: FIELDS START
|
||||
/** The field name of the <code>clients</code> field. */
|
||||
public static final String CLIENTS = "clients";
|
||||
|
||||
/** The field name of the <code>cacheData</code> field. */
|
||||
public static final String CACHE_DATA = "cacheData";
|
||||
// AUTO-GENERATED: FIELDS END
|
||||
|
||||
/** Contains information on all clients connected to this node. */
|
||||
public DSet<ClientInfo> clients = new DSet<ClientInfo>();
|
||||
|
||||
/** This node's view of the peer lock set. A lock is acquired only when it has been added to
|
||||
* all peers' sets, and is released only when it has been removed from all peers' sets. */
|
||||
public DSet<Lock> locks = new DSet<Lock>();
|
||||
|
||||
/** Used to broadcast a node's desire to acquire a lock. */
|
||||
public Lock.Name acquiringLock;
|
||||
|
||||
/** Used to broadcast a node's desire to release a lock. */
|
||||
public Lock.Name releasingLock;
|
||||
|
||||
/** A field we use to broadcast changes to possible cached data. */
|
||||
public CacheData cacheData;
|
||||
|
||||
@@ -115,6 +133,86 @@ public class NodeObject extends DObject
|
||||
this.clients = clone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests that the specified entry be added to the
|
||||
* <code>locks</code> set. The set will not change until the event is
|
||||
* actually propagated through the system.
|
||||
*/
|
||||
public void addToLocks (Lock elem)
|
||||
{
|
||||
requestEntryAdd(LOCKS, locks, elem);
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests that the entry matching the supplied key be removed from
|
||||
* the <code>locks</code> set. The set will not change until the
|
||||
* event is actually propagated through the system.
|
||||
*/
|
||||
public void removeFromLocks (Comparable key)
|
||||
{
|
||||
requestEntryRemove(LOCKS, locks, key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests that the specified entry be updated in the
|
||||
* <code>locks</code> set. The set will not change until the event is
|
||||
* actually propagated through the system.
|
||||
*/
|
||||
public void updateLocks (Lock elem)
|
||||
{
|
||||
requestEntryUpdate(LOCKS, locks, elem);
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests that the <code>locks</code> field be set to the
|
||||
* specified value. Generally one only adds, updates and removes
|
||||
* entries of a distributed set, but certain situations call for a
|
||||
* complete replacement of the set value. The local value will be
|
||||
* updated immediately and an event will be propagated through the
|
||||
* system to notify all listeners that the attribute did
|
||||
* change. Proxied copies of this object (on clients) will apply the
|
||||
* value change when they received the attribute changed notification.
|
||||
*/
|
||||
public void setLocks (DSet<com.threerings.presents.peer.data.Lock> value)
|
||||
{
|
||||
requestAttributeChange(LOCKS, value, this.locks);
|
||||
@SuppressWarnings("unchecked") DSet<com.threerings.presents.peer.data.Lock> clone =
|
||||
(value == null) ? null : value.typedClone();
|
||||
this.locks = clone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests that the <code>acquiringLock</code> field be set to the
|
||||
* specified value. The local value will be updated immediately and an
|
||||
* event will be propagated through the system to notify all listeners
|
||||
* that the attribute did change. Proxied copies of this object (on
|
||||
* clients) will apply the value change when they received the
|
||||
* attribute changed notification.
|
||||
*/
|
||||
public void setAcquiringLock (Lock.Name value)
|
||||
{
|
||||
Lock.Name ovalue = this.acquiringLock;
|
||||
requestAttributeChange(
|
||||
ACQUIRING_LOCK, value, ovalue);
|
||||
this.acquiringLock = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests that the <code>releasingLock</code> field be set to the
|
||||
* specified value. The local value will be updated immediately and an
|
||||
* event will be propagated through the system to notify all listeners
|
||||
* that the attribute did change. Proxied copies of this object (on
|
||||
* clients) will apply the value change when they received the
|
||||
* attribute changed notification.
|
||||
*/
|
||||
public void setReleasingLock (Lock.Name value)
|
||||
{
|
||||
Lock.Name ovalue = this.releasingLock;
|
||||
requestAttributeChange(
|
||||
RELEASING_LOCK, value, ovalue);
|
||||
this.releasingLock = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests that the <code>cacheData</code> field be set to the
|
||||
* specified value. The local value will be updated immediately and an
|
||||
|
||||
Reference in New Issue
Block a user