diff src/share/vm/runtime/mutex.hpp @ 0:a61af66fc99e jdk7-b24

Initial load
author duke
date Sat, 01 Dec 2007 00:00:00 +0000
parents
children 2a8eb116ebbe
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/vm/runtime/mutex.hpp	Sat Dec 01 00:00:00 2007 +0000
@@ -0,0 +1,318 @@
+/*
+ * Copyright 1998-2006 Sun Microsystems, Inc.  All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ */
+
+// 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)] ;
+} ;
+
+// Endian-ness ... index of least-significant byte in SplitWord.Bytes[]
+#ifdef AMD64        // little
+ #define _LSBINDEX 0
+#else
+#if IA32            // little
+ #define _LSBINDEX 0
+#else
+#ifdef SPARC        // big
+ #define _LSBINDEX (sizeof(intptr_t)-1)
+#else
+ #error "unknown architecture"
+#endif
+#endif
+#endif
+
+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.
+
+class Monitor : public CHeapObj {
+
+ public:
+  // A special lock: Is a lock where you are guaranteed not to block while you are
+  // holding it, i.e., no vm operation can happen, taking other locks, etc.
+  // NOTE: It is critical that the rank 'special' be the lowest (earliest)
+  // (except for "event"?) for the deadlock dection to work correctly.
+  // The rank native is only for use in Mutex's created by JVM_RawMonitorCreate,
+  // which being external to the VM are not subject to deadlock detection.
+  // The rank safepoint is used only for synchronization in reaching a
+  // safepoint and leaving a safepoint.  It is only used for the Safepoint_lock
+  // currently.  While at a safepoint no mutexes of rank safepoint are held
+  // by any thread.
+  // The rank named "leaf" is probably historical (and should
+  // be changed) -- mutexes of this rank aren't really leaf mutexes
+  // at all.
+  enum lock_types {
+       event,
+       special,
+       suspend_resume,
+       leaf        = suspend_resume +   2,
+       safepoint   = leaf           +  10,
+       barrier     = safepoint      +   1,
+       nonleaf     = barrier        +   1,
+       max_nonleaf = nonleaf        + 900,
+       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
+  enum LockWordBits { _LBIT=1 } ;
+  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).
+  const char * _name;                    // Name of mutex
+  int NotifyCount ;                      // diagnostic assist
+  double pad [8] ;                       // avoid false sharing
+
+  // Debugging fields for naming, deadlock detection, etc. (some only used in debug mode)
+#ifndef PRODUCT
+  bool      _allow_vm_block;
+  debug_only(int _rank;)                 // rank (to avoid/detect potential deadlocks)
+  debug_only(Monitor * _next;)           // Used by a Thread to link up owned locks
+  debug_only(Thread* _last_owner;)       // the last thread to own the lock
+  debug_only(static bool contains(Monitor * locks, Monitor * lock);)
+  debug_only(static Monitor * get_least_ranked_lock(Monitor * locks);)
+  debug_only(Monitor * get_least_ranked_lock_besides_this(Monitor * locks);)
+#endif
+
+  void set_owner_implementation(Thread* owner)                        PRODUCT_RETURN;
+  void check_prelock_state     (Thread* thread)                       PRODUCT_RETURN;
+  void check_block_state       (Thread* thread)                       PRODUCT_RETURN;
+
+  // platform-dependent support code can go here (in os_<os_family>.cpp)
+ public:
+  enum {
+    _no_safepoint_check_flag    = true,
+    _allow_vm_block_flag        = true,
+    _as_suspend_equivalent_flag = true
+  };
+
+  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) ;
+   Monitor() ;
+
+ public:
+  Monitor(int rank, const char *name, bool allow_vm_block=false);
+  ~Monitor();
+
+  // Wait until monitor is notified (or times out).
+  // Defaults are to make safepoint checks, wait time is forever (i.e.,
+  // zero), and not a suspend-equivalent condition. Returns true if wait
+  // times out; otherwise returns false.
+  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 lock(); // prints out warning if VM thread blocks
+  void lock(Thread *thread); // overloaded with current thread
+  void unlock();
+  bool is_locked() const                     { return _owner != NULL; }
+
+  bool try_lock(); // Like lock(), but unblocking. It returns false instead
+
+  // 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();
+  void lock_without_safepoint_check (Thread * Self) ;
+
+  // Current owner - not not MT-safe. Can only be used to guarantee that
+  // the current running thread owns the lock
+  Thread* owner() const         { return _owner; }
+  bool owned_by_self() const;
+
+  // Support for JVM_RawMonitorEnter & JVM_RawMonitorExit. These can be called by
+  // non-Java thread. (We should really have a RawMonitor abstraction)
+  void jvm_raw_lock();
+  void jvm_raw_unlock();
+  const char *name() const                  { return _name; }
+
+  void print_on_error(outputStream* st) const;
+
+  #ifndef PRODUCT
+    void print_on(outputStream* st) const;
+    void print() const                      { print_on(tty); }
+    debug_only(int    rank() const          { return _rank; })
+    bool   allow_vm_block()                 { return _allow_vm_block; }
+
+    debug_only(Monitor *next()  const         { return _next; })
+    debug_only(void   set_next(Monitor *next) { _next = next; })
+  #endif
+
+  void set_owner(Thread* owner) {
+  #ifndef PRODUCT
+    set_owner_implementation(owner);
+    debug_only(void verify_Monitor(Thread* thr));
+  #else
+    _owner = owner;
+  #endif
+  }
+
+};
+
+// Normally we'd expect Monitor to extend Mutex in the sense that a monitor
+// constructed from pthreads primitives might extend a mutex by adding
+// a condvar and some extra metadata.  In fact this was the case until J2SE7.
+//
+// Currently, however, the base object is a monitor.  Monitor contains all the
+// logic for wait(), notify(), etc.   Mutex extends monitor and restricts the
+// visiblity of wait(), notify(), and notify_all().
+//
+// Another viable alternative would have been to have Monitor extend Mutex and
+// implement all the normal mutex and wait()-notify() logic in Mutex base class.
+// The wait()-notify() facility would be exposed via special protected member functions
+// (e.g., _Wait() and _Notify()) in Mutex.  Monitor would extend Mutex and expose wait()
+// as a call to _Wait().  That is, the public wait() would be a wrapper for the protected
+// _Wait().
+//
+// An even better alternative is to simply eliminate Mutex:: and use Monitor:: instead.
+// After all, monitors are sufficient for Java-level synchronization.   At one point in time
+// 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:
+   Mutex (int rank, const char *name, bool allow_vm_block=false);
+   ~Mutex () ;
+ private:
+   bool notify ()    { ShouldNotReachHere(); return false; }
+   bool notify_all() { ShouldNotReachHere(); return false; }
+   bool wait (bool no_safepoint_check, long timeout, bool as_suspend_equivalent) {
+     ShouldNotReachHere() ;
+     return false ;
+   }
+};
+
+/*
+ * Per-thread blocking support for JSR166. See the Java-level
+ * Documentation for rationale. Basically, park acts like wait, unpark
+ * like notify.
+ *
+ * 6271289 --
+ * To avoid errors where an os thread expires but the JavaThread still
+ * exists, Parkers are immortal (type-stable) and are recycled across
+ * new threads.  This parallels the ParkEvent implementation.
+ * Because park-unpark allow spurious wakeups it is harmless if an
+ * unpark call unparks a new thread using the old Parker reference.
+ *
+ * In the future we'll want to think about eliminating Parker and using
+ * ParkEvent instead.  There's considerable duplication between the two
+ * services.
+ *
+ */
+
+class Parker : public os::PlatformParker {
+private:
+  volatile int _counter ;
+  Parker * FreeNext ;
+  JavaThread * AssociatedWith ; // Current association
+
+public:
+  Parker() : PlatformParker() {
+    _counter       = 0 ;
+    FreeNext       = NULL ;
+    AssociatedWith = NULL ;
+  }
+protected:
+  ~Parker() { ShouldNotReachHere(); }
+public:
+  // For simplicity of interface with Java, all forms of park (indefinite,
+  // relative, and absolute) are multiplexed into one call.
+  void park(bool isAbsolute, jlong time);
+  void unpark();
+
+  // Lifecycle operators
+  static Parker * Allocate (JavaThread * t) ;
+  static void Release (Parker * e) ;
+private:
+  static Parker * volatile FreeList ;
+  static volatile int ListLock ;
+};