@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(Dn entry)
Acquires the read lock for the specified entry.
|
LockManager.DNLock |
tryWriteLockEntry(Dn entry)
Acquires the write lock for the specified entry.
|
LockManager.DNLock |
tryWriteLockSubtree(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(Dn entry)
entry
- The entry whose read lock is required.null
if the lock attempt timed out.public LockManager.DNLock tryWriteLockEntry(Dn entry)
entry
- The entry whose write lock is required.null
if the lock attempt timed out.public LockManager.DNLock tryWriteLockSubtree(Dn subtree)
subtree
- The subtree whose write lock is required.null
if the lock attempt timed out.Copyright 2010-2020 ForgeRock AS.