ccs.beetree
Class BeeLine

java.lang.Object
  extended by ccs.beetree.BeeLine
All Implemented Interfaces:
java.util.Comparator<BeeTree>

public class BeeLine
extends java.lang.Object
implements java.util.Comparator<BeeTree>

Where a thread must obtain locks on multiple objects, it's essential that the locks are always obtained in the same order to avoid the risk of deadlock. Deadlocks typically occur quasi-randomly, making them very difficult to debug, and, since the interaction cross-section is typically rather small, they usually turn up in the field (where the volume of transactions eventually causes the law of everages to catch up with you) rather than during testing.

BeeLine is a utility class to help avoid these types of deadlock in the case of thread-locking multiple BeeTrees. (All BeeTree operations lock the BeeTree and its disk file, but only thread-locking generates a persistent lock; this allows a thread to hold locks on multiple BeeTrees at the same time, which is necessary to cause a deadlock.)

BeeLine works by allowing you to define a canonical order for BeeTree locking; once such an order has been defined, BeeTree itself will disallow attempts to gain locks out of order. It also provides utility methods to thread-lock a set of BeeTrees in the correct order.

Typically one or more BeeTrees are grouped into an application-level "database", access to which is mediated by an application-level object. Each such group should be ordered using a BeeLine, either at application startup, or where databases are loaded dynamically, as part of the load process. Once you've used the BeeLine to impose the ordering, you can either continue to use it to do locking and unlocking, or have the application-level object call threadLock / threadUnlock on the BeeTrees directly, whichever is most convenient. NB. in this regime, it's possible to engineer a deadlock by using BeeTrees from different BeeLines. It should be easy enough to isolate the groups via application-level encapsulating objects to prevent this happening. Alternatively you could put all BeeTrees from all databases into the same BeeLine; this would prevent deadlock at the cost of imposing an artificial ordering constraint on databases that (maybe) ought to be independent. This seems likely to be a bad tradeoff.

An attempt to gain locks out of order will throw IllegalAccessError. Throwing Error rather than an exception is perhaps harsh, but this situation always indicates a programming error which needs to be fixed, and it's too messy to recover gracefully.

As indicated above, any operation on a BeeTree will lock it for the duration of the operation; lock ordering is enforced for these "transient" locks as well as for persistent thread-lock locks. In any case, it is bad practice and usually a bug to perform non-threadlocked operations on one BeeTree while performing non-threadlocked operations on another; either it's a transaction or it isn't.

A basic analysis suggests that it is more efficient to lock the most contended BeeTree first. With this approach, a thread which is blocked waiting for a highly-contended BeeTree will not be holding locks to less-contended BeeTrees, and thus denying them to other threads while being unable to use them itself.

Where multiple applications attach to the same data files, it is essential that they all use the same canonical order. BeeLine does not protect against inter-VM deadlocks.


Constructor Summary
BeeLine()
           
 
Method Summary
 int compare(BeeTree bt1, BeeTree bt2)
           
 void register(BeeTree bt)
           Register a BeeTree object with the BeeLine.
 void threadLock(BeeTree... trees)
          Threadlock a set of BeeTrees, RW, in canonical order.
 void threadLockRO(BeeTree... trees)
          Threadlock a set of BeeTrees, in canonical order, read-only.
 void threadUnlock()
          Releases all RW threadlocks held by the current thread, on BeeTrees which are registered with this BeeLine.
 void threadUnlockRO()
          Releases all RO threadlocks held by the current thread, on BeeTrees which are registered with this BeeLine.
 
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
 
Methods inherited from interface java.util.Comparator
equals
 

Constructor Detail

BeeLine

public BeeLine()
Method Detail

register

public void register(BeeTree bt)
              throws java.lang.IllegalStateException

Register a BeeTree object with the BeeLine. Lock ordering is managed at the disk file level: if you have multiple BeeTree objects which reference the same file (eg. to update using one, while iterating through partial-key matches with the other) only register one of them. NOTE: this requirement is the reverse of that in Kernel 5.3.0 and earlier.

If two separate beetree files happen to have the same file ID code, you will get a spurious exception when you attempt to register the second into a BeeLine. This should be very uncommon. If it happens to you, and you're really sure that it's not your bug, write a one-off utility program to apply method BeeTree.changeFileID to one of the affected files. You must be certain that the file isn't in use by anything at that point; take whatever precautions are appropriate.

Generally, it's most efficient to register the most highly-contended beetree first, if you can predict which that is.

MT-UNSAFE.Register all your BeeTrees as part of your application startup code, before starting threads which might access the BeeLine.

Parameters:
bt - The BeeTree to register.
Throws:
java.lang.IllegalStateException - if bt is already registered with another BeeLine.

threadLock

public void threadLock(BeeTree... trees)
                throws java.io.IOException
Threadlock a set of BeeTrees, RW, in canonical order. MT-safe. Nestable.

Parameters:
trees - The BeeTrees to lock. The supplied array will be sorted into order (the method alters its parameter).
Throws:
java.io.IOException

threadUnlock

public void threadUnlock()
                  throws java.io.IOException
Releases all RW threadlocks held by the current thread, on BeeTrees which are registered with this BeeLine. Every threadLock call requires a corresponding threadUnlock call.

Throws:
java.io.IOException

threadLockRO

public void threadLockRO(BeeTree... trees)
                  throws java.io.IOException
Threadlock a set of BeeTrees, in canonical order, read-only. It is safe to grab the RO lock while holding the RW lock, but NOT vice-versa - the latter attempt will always deadlock. MT-safe.

Parameters:
trees - The BeeTrees to lock. The supplied array will be sorted into order (the method alters its parameter).
Throws:
java.io.IOException

threadUnlockRO

public void threadUnlockRO()
                    throws java.io.IOException
Releases all RO threadlocks held by the current thread, on BeeTrees which are registered with this BeeLine. Every threadLockRO call requires a corresponding threadUnlockRO call. Where RO-locks occur within an RW lock (which is permitted), RO and RW unlocks must occur in the reverse order that the locks were gained, ie. proper nesting.

Throws:
java.io.IOException

compare

public int compare(BeeTree bt1,
                   BeeTree bt2)
Specified by:
compare in interface java.util.Comparator<BeeTree>