mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-28 15:24:43 +02:00
8210832: Remove sneaky locking in class Monitor
Removed sneaky locking and simplified vm monitors implementation Co-authored-by: David Holmes <david.holmes@oracle.com> Reviewed-by: rehn, dcubed, pliden, dholmes, coleenp
This commit is contained in:
parent
cd9b1aabb0
commit
c94cdddbdd
17 changed files with 509 additions and 1157 deletions
|
@ -29,50 +29,10 @@
|
|||
#include "runtime/os.hpp"
|
||||
#include "utilities/histogram.hpp"
|
||||
|
||||
// The SplitWord construct allows us to colocate the contention queue
|
||||
// (cxq) with the lock-byte. The queue elements are ParkEvents, which are
|
||||
// always aligned on 256-byte addresses - the least significant byte of
|
||||
// a ParkEvent is always 0. Colocating the lock-byte with the queue
|
||||
// allows us to easily avoid what would otherwise be a race in lock()
|
||||
// if we were to use two completely separate fields for the contention queue
|
||||
// and the lock indicator. Specifically, colocation renders us immune
|
||||
// from the race where a thread might enqueue itself in the lock() slow-path
|
||||
// immediately after the lock holder drops the outer lock in the unlock()
|
||||
// fast-path.
|
||||
//
|
||||
// Colocation allows us to use a fast-path unlock() form that uses
|
||||
// A MEMBAR instead of a CAS. MEMBAR has lower local latency than CAS
|
||||
// on many platforms.
|
||||
//
|
||||
// See:
|
||||
// + http://blogs.sun.com/dave/entry/biased_locking_in_hotspot
|
||||
// + http://blogs.sun.com/dave/resource/synchronization-public2.pdf
|
||||
//
|
||||
// Note that we're *not* using word-tearing the classic sense.
|
||||
// The lock() fast-path will CAS the lockword and the unlock()
|
||||
// fast-path will store into the lock-byte colocated within the lockword.
|
||||
// We depend on the fact that all our reference platforms have
|
||||
// coherent and atomic byte accesses. More precisely, byte stores
|
||||
// interoperate in a safe, sane, and expected manner with respect to
|
||||
// CAS, ST and LDs to the full-word containing the byte.
|
||||
// If you're porting HotSpot to a platform where that isn't the case
|
||||
// then you'll want change the unlock() fast path from:
|
||||
// STB;MEMBAR #storeload; LDN
|
||||
// to a full-word CAS of the lockword.
|
||||
|
||||
|
||||
union SplitWord { // full-word with separately addressable LSB
|
||||
volatile intptr_t FullWord ;
|
||||
volatile void * Address ;
|
||||
volatile jbyte Bytes [sizeof(intptr_t)] ;
|
||||
} ;
|
||||
|
||||
class ParkEvent ;
|
||||
|
||||
// See orderAccess.hpp. We assume throughout the VM that mutex lock and
|
||||
// try_lock do fence-lock-acquire, and that unlock does a release-unlock,
|
||||
// *in that order*. If their implementations change such that these
|
||||
// assumptions are violated, a whole lot of code will break.
|
||||
// A Mutex/Monitor is a simple wrapper around a native lock plus condition
|
||||
// variable that supports lock ownership tracking, lock ranking for deadlock
|
||||
// detection and coordinates with the safepoint protocol.
|
||||
|
||||
// The default length of monitor name was originally chosen to be 64 to avoid
|
||||
// false sharing. Now, PaddedMonitor is available for this purpose.
|
||||
|
@ -118,22 +78,10 @@ class Monitor : public CHeapObj<mtInternal> {
|
|||
native = max_nonleaf + 1
|
||||
};
|
||||
|
||||
// The WaitSet and EntryList linked lists are composed of ParkEvents.
|
||||
// I use ParkEvent instead of threads as ParkEvents are immortal and
|
||||
// type-stable, meaning we can safely unpark() a possibly stale
|
||||
// list element in the unlock()-path.
|
||||
|
||||
protected: // Monitor-Mutex metadata
|
||||
SplitWord _LockWord ; // Contention queue (cxq) colocated with Lock-byte
|
||||
Thread * volatile _owner; // The owner of the lock
|
||||
// Consider sequestering _owner on its own $line
|
||||
// to aid future synchronization mechanisms.
|
||||
ParkEvent * volatile _EntryList ; // List of threads waiting for entry
|
||||
ParkEvent * volatile _OnDeck ; // heir-presumptive
|
||||
volatile intptr_t _WaitLock [1] ; // Protects _WaitSet
|
||||
ParkEvent * volatile _WaitSet ; // LL of ParkEvents
|
||||
volatile bool _snuck; // Used for sneaky locking (evil).
|
||||
char _name[MONITOR_NAME_LEN]; // Name of mutex
|
||||
os::PlatformMonitor _lock; // Native monitor implementation
|
||||
char _name[MONITOR_NAME_LEN]; // Name of mutex/monitor
|
||||
|
||||
// Debugging fields for naming, deadlock detection, etc. (some only used in debug mode)
|
||||
#ifndef PRODUCT
|
||||
|
@ -149,8 +97,8 @@ class Monitor : public CHeapObj<mtInternal> {
|
|||
void set_owner_implementation(Thread* owner) PRODUCT_RETURN;
|
||||
void check_prelock_state (Thread* thread, bool safepoint_check) PRODUCT_RETURN;
|
||||
void check_block_state (Thread* thread) PRODUCT_RETURN;
|
||||
void assert_owner (Thread* expected) NOT_DEBUG_RETURN;
|
||||
|
||||
// platform-dependent support code can go here (in os_<os_family>.cpp)
|
||||
public:
|
||||
enum {
|
||||
_no_safepoint_check_flag = true,
|
||||
|
@ -164,6 +112,9 @@ class Monitor : public CHeapObj<mtInternal> {
|
|||
// consistent checking for each lock.
|
||||
// A few existing locks will sometimes have a safepoint check and
|
||||
// sometimes not, but these locks are set up in such a way to avoid deadlocks.
|
||||
// Note: monitors that may be shared between JavaThreads and the VMThread
|
||||
// should never encounter a safepoint check whilst they are held, else a
|
||||
// deadlock with the VMThread can occur.
|
||||
enum SafepointCheckRequired {
|
||||
_safepoint_check_never, // Monitors with this value will cause errors
|
||||
// when acquired with a safepoint check.
|
||||
|
@ -176,22 +127,6 @@ class Monitor : public CHeapObj<mtInternal> {
|
|||
|
||||
NOT_PRODUCT(SafepointCheckRequired _safepoint_check_required;)
|
||||
|
||||
enum WaitResults {
|
||||
CONDVAR_EVENT, // Wait returned because of condition variable notification
|
||||
INTERRUPT_EVENT, // Wait returned because waiting thread was interrupted
|
||||
NUMBER_WAIT_RESULTS
|
||||
};
|
||||
|
||||
private:
|
||||
int TrySpin (Thread * Self) ;
|
||||
int TryLock () ;
|
||||
int TryFast () ;
|
||||
int AcquireOrPush (ParkEvent * ev) ;
|
||||
void IUnlock (bool RelaxAssert) ;
|
||||
void ILock (Thread * Self) ;
|
||||
int IWait (Thread * Self, jlong timo);
|
||||
int ILocked () ;
|
||||
|
||||
protected:
|
||||
static void ClearMonitor (Monitor * m, const char* name = NULL) ;
|
||||
Monitor() ;
|
||||
|
@ -208,8 +143,8 @@ class Monitor : public CHeapObj<mtInternal> {
|
|||
bool wait(bool no_safepoint_check = !_no_safepoint_check_flag,
|
||||
long timeout = 0,
|
||||
bool as_suspend_equivalent = !_as_suspend_equivalent_flag);
|
||||
bool notify();
|
||||
bool notify_all();
|
||||
void notify();
|
||||
void notify_all();
|
||||
|
||||
|
||||
void lock(); // prints out warning if VM thread blocks
|
||||
|
@ -219,6 +154,8 @@ class Monitor : public CHeapObj<mtInternal> {
|
|||
|
||||
bool try_lock(); // Like lock(), but unblocking. It returns false instead
|
||||
|
||||
void release_for_safepoint();
|
||||
|
||||
// Lock without safepoint check. Should ONLY be used by safepoint code and other code
|
||||
// that is guaranteed not to block while running inside the VM.
|
||||
void lock_without_safepoint_check();
|
||||
|
@ -290,9 +227,6 @@ class PaddedMonitor : public Monitor {
|
|||
// there may have been some benefit to having distinct mutexes and monitors, but that time
|
||||
// has past.
|
||||
//
|
||||
// The Mutex/Monitor design parallels that of Java-monitors, being based on
|
||||
// thread-specific park-unpark platform-specific primitives.
|
||||
|
||||
|
||||
class Mutex : public Monitor { // degenerate Monitor
|
||||
public:
|
||||
|
@ -300,8 +234,8 @@ class Mutex : public Monitor { // degenerate Monitor
|
|||
SafepointCheckRequired safepoint_check_required = _safepoint_check_always);
|
||||
// default destructor
|
||||
private:
|
||||
bool notify () { ShouldNotReachHere(); return false; }
|
||||
bool notify_all() { ShouldNotReachHere(); return false; }
|
||||
void notify () { ShouldNotReachHere(); }
|
||||
void notify_all() { ShouldNotReachHere(); }
|
||||
bool wait (bool no_safepoint_check, long timeout, bool as_suspend_equivalent) {
|
||||
ShouldNotReachHere() ;
|
||||
return false ;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue