public final class SpinLock extends Object
Alternative (to Java monitors) light-weight synchronization
mechanism to implement Java monitors Lock
. These locks
should not be used where Java monitors would suffice, or where
an adaptive mutex is required. They are
intended to be held only briefly!
Normally, contending RVMThread
s will spin on
this processor lock's latestContender
field. If
MCS_Locking
is set, the processors spin on processor
local data. This is loosely based on an idea in Mellor-Crummey and
Scott's paper in ASPLOS-IV (1991).
1. Possible project: determine those conditions under which MCS
locking performs better than spinning on a global address.
Acquiring or releasing a lock involves atomically reading and
setting the lock's latestContender
field. If this
field is null, the lock is unowned. Otherwise, the field points to
the thread that owns the lock, or, if MCS locking is
being used, to the last thread on a circular queue of threads
spinning until they get the lock, or, if MCS locking is
being used and the circular spin queue is being updated, to
IN_FLUX
.
Contention is best handled by doing something else. To support
this, tryLock
obtains the lock (and returns true) if
the lock is unowned (and there is no spurious microcontention).
Otherwise, it returns false.
Only when "doing something else" is not an attractive option (locking global thread queues, unlocking a thick lock with threads waiting, avoiding starvation on a thread that has issued several tryLocks, etc.) should lock() be called. Here, any remaining contention is handled by spinning on a local flag.
To add itself to the circular waiting queue, a thread must succeed in setting the latestContender field to IN_FLUX. A backoff strategy is used to reduce contention for this field. This strategy has both a pseudo-random (to prevent two or more threads from backing off in lock step) and an exponential component (to deal with really high contention).
Releasing a lock entails either atomically setting the latestContender field to null (if this thread is the latestContender), or releasing the first thread on the circular spin queue. In the latter case, the latestContender field must be set to IN_FLUX. To give unlock() priority over lock(), the backoff strategy is not used for unlocking: if a thread fails to set set the field to IN_FLUX, it tries again immediately.
Usage: system locks should only be used when synchronized methods cannot. Do not do anything, (such as trigger a type cast, allocate an object, or call any method of a class that does not implement Uninterruptible) that might allow a thread switch or trigger a garbage collection between lock and unlock.
Modifier and Type | Field and Description |
---|---|
private static int |
delayBase |
private static int[] |
delayCount |
private static int |
delayIndex |
private static int |
delayMultiplier |
private static Address |
IN_FLUX
For MCS locking, indicates that another processor is changing the
state of the circular waiting queue.
|
(package private) RVMThread |
latestContender
The state of the thread lock.
|
private static boolean |
MCS_Locking
Should contending
RVMThread s spin on thread local addresses (true)
or on a globally shared address (false). |
Constructor and Description |
---|
SpinLock() |
Modifier and Type | Method and Description |
---|---|
private void |
handleMicrocontention(int n)
An attempt to lock or unlock a processor lock has failed,
presumably due to contention with another processor.
|
void |
lock()
Acquire a lock.
|
void |
lock(String s) |
boolean |
lockHeld() |
boolean |
tryLock()
Conditionally acquire a lock.
|
void |
unlock()
Release a lock.
|
private static final boolean MCS_Locking
RVMThread
s spin on thread local addresses (true)
or on a globally shared address (false).RVMThread latestContender
null
, if the lock is not owned;
IN_FLUX
, if the circular chain is being edited.
private static final int delayMultiplier
private static final int delayBase
private static int delayIndex
private static final int[] delayCount
public SpinLock()
public boolean lockHeld()
public void lock()
public boolean tryLock()
public void unlock()
private void handleMicrocontention(int n)
n
- the number of attempts