@PublicAPI(stability=UNCOMMITTED, mayInstantiate=false, mayExtend=false, mayInvoke=true) public final class LockManager extends Object
tryReadLockEntry(Dn)
tryWriteLockEntry(Dn)
. Updates are typically performed using a read-modify-write cycle,
so the write lock should be acquired before performing the initial read in order to ensure
consistency
tryWriteLockEntry(Dn)
. The parent entry will automatically be protected from deletion by
an implicit subtree read lock on the parent
tryWriteLockSubtree(Dn)
tryWriteLockSubtree(Dn)
. Care should be taken
to avoid deadlocks, e.g. by locking the DN which sorts first.
Implementation Notes
The lock table is conceptually a cache of locks keyed on DN, i.e. a Map<DN, DNLock>
.
Locks must be kept in the cache while they are locked, but may be removed once they are no longer
locked by any threads. Locks are represented using a pair of read-write locks: the first lock is
the "subtree" lock and the second is the "entry" lock.
In order to lock an entry for read or write a subtree read lock is first acquired on each of the parent entries from the root DN down to the immediate parent of the entry to be locked. Then the appropriate read or write entry lock is acquired for the target entry. Subtree write locking is performed by acquiring a subtree read lock on each of the parent entries from the root DN down to the immediate parent of the subtree to be locked. Then a subtree write lock is acquired for the target subtree.
The lock table itself is not represented using a ConcurrentHashMap
because the JDK6/7
APIs do not provide the ability to atomically add-and-lock or unlock-and-remove locks (this
capability is provided in JDK8). Instead, we provide our own implementation comprising of a fixed
number of buckets, a bucket being a LinkedList
of DNLock
s. In addition, it is
important to be able to efficiently iterate up and down a chain of hierarchically related locks,
so each lock maintains a reference to its parent lock. Modern directories tend to have a flat
structure so it is also important to avoid contention on "hot" parent DNs. Typically, a lock
attempt against a DN will involve a cache miss for the target DN and a cache hit for the parent,
but the parent will be the same parent for all lock requests, resulting in a lot of contention on
the same lock bucket. To avoid this the lock manager maintains a small-thread local cache of
locks, so that parent locks can be acquired using a lock-free algorithm.
Since the thread local cache may reference locks which are not actively locked by anyone, a reference counting mechanism is used in order to prevent cached locks from being removed from the underlying lock table. The reference counting mechanism is also used for references between a lock and its parent lock. To summarize, locking a DN involves the following steps:
Modifier and Type | Class and Description |
---|---|
class |
LockManager.DNLock
A lock on an entry or subtree.
|
Constructor and Description |
---|
LockManager()
Creates a new lock manager with a lock timeout of 9 seconds and an automatically chosen number
of lock table buckets based on the number of processors.
|
LockManager(long lockTimeout,
TimeUnit lockTimeoutUnit)
Creates a new lock manager with the specified lock timeout and an automatically chosen number
of lock table buckets based on the number of processors.
|
Modifier and Type | Method and Description |
---|---|
String |
toString() |
LockManager.DNLock |
tryReadLockEntry(org.forgerock.opendj.ldap.Dn entry)
Acquires the read lock for the specified entry.
|
LockManager.DNLock |
tryWriteLockEntry(org.forgerock.opendj.ldap.Dn entry)
Acquires the write lock for the specified entry.
|
LockManager.DNLock |
tryWriteLockSubtree(org.forgerock.opendj.ldap.Dn subtree)
Acquires the write lock for the specified subtree.
|
public LockManager()
public LockManager(long lockTimeout, TimeUnit lockTimeoutUnit)
lockTimeout
- The lock timeout.lockTimeoutUnit
- The lock timeout units.public LockManager.DNLock tryReadLockEntry(org.forgerock.opendj.ldap.Dn entry)
entry
- The entry whose read lock is required.null
if the lock attempt timed out.public LockManager.DNLock tryWriteLockEntry(org.forgerock.opendj.ldap.Dn entry)
entry
- The entry whose write lock is required.null
if the lock attempt timed out.public LockManager.DNLock tryWriteLockSubtree(org.forgerock.opendj.ldap.Dn subtree)
subtree
- The subtree whose write lock is required.null
if the lock attempt timed out.Copyright © 2010-2017 ForgeRock AS. All Rights Reserved.