|
||||||||||
PREV CLASS NEXT CLASS | FRAMES NO FRAMES | |||||||||
SUMMARY: NESTED | FIELD | CONSTR | METHOD | DETAIL: FIELD | CONSTR | METHOD |
java.lang.Objectccs.beetree.BeeLine
public class BeeLine
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 BeeTree
s. (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 |
---|
public BeeLine()
Method Detail |
---|
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.
bt
- The BeeTree to register.
java.lang.IllegalStateException
- if bt is already registered with another BeeLine.public void threadLock(BeeTree... trees) throws java.io.IOException
trees
- The BeeTrees to lock. The supplied array will be sorted into
order (the method alters its parameter).
java.io.IOException
public void threadUnlock() throws java.io.IOException
threadLock
call requires
a corresponding threadUnlock
call.
java.io.IOException
public void threadLockRO(BeeTree... trees) throws java.io.IOException
trees
- The BeeTrees to lock. The supplied array will be sorted into
order (the method alters its parameter).
java.io.IOException
public void threadUnlockRO() throws java.io.IOException
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.
java.io.IOException
public int compare(BeeTree bt1, BeeTree bt2)
compare
in interface java.util.Comparator<BeeTree>
|
||||||||||
PREV CLASS NEXT CLASS | FRAMES NO FRAMES | |||||||||
SUMMARY: NESTED | FIELD | CONSTR | METHOD | DETAIL: FIELD | CONSTR | METHOD |