10.3.2. LockManager

The concurrency controller is implemented by the class LockManager, which provides sensible default behavior that you can override if necessary. The setlock method is the primary interface to the concurrency controller. By default, the JBoss Transaction Service runtime system enforces strict two-phase locking, following a multiple reader, single writer policy on a per-object basis. You, as the programmer, control lock acquisition, since the LockManager class cannot predict whether an operation needs a read or write lock. Lock release, however, is normally under control of the system, requiring no action by the programmer.
The LockManager class manages requests to set a lock on an object or to release a lock. However, since it is derived from StateManager, it can also control invocation of some of the inherited facilities. For example, if a request to set a write lock is granted, then LockManager invokes the modified method directly, since setting a write lock implies that the invoking method is about to modify the object. This may cause recovery information to be saved, if the object is recoverable. Successful lock acquisition also triggers invocation of the activate.
Therefore, LockManager activates and deactivates persistent objects, and also registers Resources used for managing concurrency control. By driving the StateManager class, it also registers Resources for persistent and recoverable state manipulation and object recovery. You only set the appropriate locks, start and end transactions, and extend the save_state and restore_state methods of the StateManager class.

Example 10.9. LockResult Example

public class LockResult
{
    public static final int GRANTED;
    public static final int REFUSED;
    public static final int RELEASED;
};

public class ConflictType
{
    public static final int CONFLICT;
    public static final int COMPATIBLE;
    public static final int PRESENT;
};

public abstract class LockManager extends StateManager
{
    public static final int defaultTimeout;
    public static final int defaultRetry;
    public static final int waitTotalTimeout;

    public synchronized int setlock (Lock l);
    public synchronized int setlock (Lock l, int retry);
    public synchronized int setlock (Lock l, int retry, int sleepTime);
    public synchronized boolean releaselock (Uid uid);

    /* abstract methods inherited from StateManager */

    public boolean restore_state (InputObjectState os, int ObjectType);
    public boolean save_state (OutputObjectState os, int ObjectType);
    public String type ();

    protected LockManager ();
    protected LockManager (int ObjectType, ObjectName attr);
    protected LockManager (Uid storeUid);
    protected LockManager (Uid storeUid, int ObjectType, ObjectName attr);
    . . .
};
You need to pass the type of lock required and the number of retries to acquire the lock,as parameters to the setlock method. The type is either READ or WRITE. If a lock conflict occurs, one of the following scenarios will take place:
  • If the retry value is equal to LockManager.waitTotalTimeout, the thread which called the setlock method is blocked until the lock is released, or the total timeout specified has elapsed. In the case of a time-out, a value of REFUSED is returned.
  • If the lock cannot be obtained initially,LockManager retries the specified number of times, waiting for the specified timeout value between each failed attempt. The default is 100 attempts, each attempt being separated by a 0.25 seconds delay.
If a lock conflict occurs, the lock request is timed out, to prevent deadlocks. A full deadlock detection scheme is not provided. If the requested lock is obtained, the setlock method returns a value of GRANTED. Otherwise, a value of REFUSED is returned. You need to ensure that the remainder of the code for an operation is only executed if a lock request is granted. Refer to Example 10.10, “setlock Example” for a working example.

Example 10.10. setlock Example

res = setlock(new Lock(WRITE), 10); 
// Attempts to set a write
// lock 11 times (10 retries)
// before giving up.
      
res = setlock(new Lock(READ), 0); 
// Attempts to set a read lock
// 1 time (no retries) before
// giving up.
      
res = setlock(new Lock(WRITE); 
// Attempts to set a write lock
// 101 times (default of 100
// retries) before giving up.
The concurrency control mechanism is integrated into the atomic action mechanism to ensure that as locks are granted on an object, appropriate information is registered with the currently running atomic action. This guarantees that the locks are released at the correct time, and removes the need to explicitly free locks which were acquired within atomic actions. However, if locks are acquired on an object outside of the scope of an atomic action, you must use the releaselock method to release the locks.