< prev index next >

src/share/vm/runtime/objectMonitor.cpp

Print this page

        

@@ -37,10 +37,11 @@
 #include "runtime/stubRoutines.hpp"
 #include "runtime/thread.inline.hpp"
 #include "services/threadService.hpp"
 #include "trace/tracing.hpp"
 #include "trace/traceMacros.hpp"
+#include "evtrace/traceEvents.hpp"
 #include "utilities/dtrace.hpp"
 #include "utilities/macros.hpp"
 #include "utilities/preserveException.hpp"
 #ifdef TARGET_OS_FAMILY_linux
 # include "os_linux.inline.hpp"

@@ -313,11 +314,11 @@
     _recursions++;
     return true;
   }
 }
 
-void ATTR ObjectMonitor::enter(TRAPS) {
+void ATTR ObjectMonitor::enter(int after_wait, TRAPS) {
   // The following code is ordered to check the most common cases first
   // and to reduce RTS->RTO cache line upgrades on SPARC and IA32 processors.
   Thread * const Self = THREAD ;
   void * cur ;
 

@@ -376,13 +377,18 @@
   // Ensure the object-monitor relationship remains stable while there's contention.
   Atomic::inc_ptr(&_count);
 
   EventJavaMonitorEnter event;
 
+  int trace_flags = 0;
   { // Change java thread status to indicate blocked on monitor enter.
     JavaThreadBlockedOnMonitorEnterState jtbmes(jt, this);
 
+    if (EnableEventTracing) {
+      TraceEvents::write_monitor_contended_enter(this, (TraceTypes::monitor_enter_wait) after_wait);
+    }
+
     DTRACE_MONITOR_PROBE(contended__enter, this, object(), jt);
     if (JvmtiExport::should_post_monitor_contended_enter()) {
       JvmtiExport::post_monitor_contended_enter(jt, this);
 
       // The current thread does not yet own the monitor and does not

@@ -401,11 +407,11 @@
     for (;;) {
       jt->set_suspend_equivalent();
       // cleared by handle_special_suspend_equivalent_condition()
       // or java_suspend_self()
 
-      EnterI (THREAD) ;
+      trace_flags |= EnterI (THREAD) ;
 
       if (!ExitSuspendEquivalent(jt)) break ;
 
       //
       // We have acquired the contended monitor, but while we were

@@ -451,10 +457,14 @@
   // per-monitor aggregation) and defer reporting until a more opportune
   // time -- such as next time some thread encounters contention but has
   // yet to acquire the lock.  While spinning that thread could
   // spinning we could increment JVMStat counters, etc.
 
+  if (EnableEventTracing) {
+    TraceEvents::write_monitor_contended_entered(this, (TraceTypes::monitor_entered_flags) trace_flags);
+  }
+
   DTRACE_MONITOR_PROBE(contended__entered, this, object(), jt);
   if (JvmtiExport::should_post_monitor_contended_entered()) {
     JvmtiExport::post_monitor_contended_entered(jt, this);
 
     // The current thread already owns the monitor and is not going to

@@ -497,21 +507,23 @@
       // Retry doesn't make as much sense because the lock was just acquired.
       if (true) return -1 ;
    }
 }
 
-void ATTR ObjectMonitor::EnterI (TRAPS) {
+int ATTR ObjectMonitor::EnterI (TRAPS) {
+    int trace_flags = 0;
+
     Thread * Self = THREAD ;
     assert (Self->is_Java_thread(), "invariant") ;
     assert (((JavaThread *) Self)->thread_state() == _thread_blocked   , "invariant") ;
 
     // Try the lock - TATAS
     if (TryLock (Self) > 0) {
         assert (_succ != Self              , "invariant") ;
         assert (_owner == Self             , "invariant") ;
         assert (_Responsible != Self       , "invariant") ;
-        return ;
+        return trace_flags;
     }
 
     DeferredInitialize () ;
 
     // We try one round of spinning *before* enqueueing Self.

@@ -523,11 +535,11 @@
 
     if (TrySpin (Self) > 0) {
         assert (_owner == Self        , "invariant") ;
         assert (_succ != Self         , "invariant") ;
         assert (_Responsible != Self  , "invariant") ;
-        return ;
+        return trace_flags;
     }
 
     // The Spin failed -- Enqueue and park the thread ...
     assert (_succ  != Self            , "invariant") ;
     assert (_owner != Self            , "invariant") ;

@@ -561,14 +573,16 @@
         // As an optional optimization we retry the lock.
         if (TryLock (Self) > 0) {
             assert (_succ != Self         , "invariant") ;
             assert (_owner == Self        , "invariant") ;
             assert (_Responsible != Self  , "invariant") ;
-            return ;
+            return trace_flags;
         }
     }
 
+    trace_flags |= TraceTypes::entered_queued;
+
     // Check for cxq|EntryList edge transition to non-null.  This indicates
     // the onset of contention.  While contention persists exiting threads
     // will use a ST:MEMBAR:LD 1-1 exit protocol.  When contention abates exit
     // operations revert to the faster 1-0 mode.  This enter operation may interleave
     // (race) a concurrent 1-0 exit operation, resulting in stranding, so we

@@ -630,10 +644,12 @@
         } else {
             TEVENT (Inflated enter - park UNTIMED) ;
             Self->_ParkEvent->park() ;
         }
 
+        trace_flags |= TraceTypes::entered_parked;
+
         if (TryLock(Self) > 0) break ;
 
         // The lock is still contested.
         // Keep a tally of the # of futile wakeups.
         // Note that the counter is not protected by a lock or updated by atomics.

@@ -735,11 +751,11 @@
     // execute a serializing instruction.
 
     if (SyncFlags & 8) {
        OrderAccess::fence() ;
     }
-    return ;
+    return trace_flags;
 }
 
 // ReenterI() is a specialized inline form of the latter half of the
 // contended slow-path from EnterI().  We use ReenterI() only for
 // monitor reentry in wait().

@@ -951,11 +967,11 @@
 // any one time.  (more precisely, we want to minimize timer-seconds, which is
 // the integral of the # of active timers at any instant over time).
 // Both impinge on OS scalability.  Given that, at most one thread parked on
 // a monitor will use a timer.
 
-void ATTR ObjectMonitor::exit(bool not_suspended, TRAPS) {
+void ATTR ObjectMonitor::exit(intptr_t *exit_stack_id_for_wait, bool not_suspended, TRAPS) {
    Thread * Self = THREAD ;
    if (THREAD != _owner) {
      if (THREAD->is_lock_owned((address) _owner)) {
        // Transmute _owner from a BasicLock pointer to a Thread address.
        // We don't need to hold _mutex for this transition.

@@ -996,15 +1012,42 @@
    if (not_suspended && Tracing::is_event_enabled(TraceJavaMonitorEnterEvent)) {
      _previous_owner_tid = SharedRuntime::get_java_tid(Self);
    }
 #endif
 
+   TraceEventMonitorContendedExited event(this);
+   if (exit_stack_id_for_wait != NULL) {
+     // This is a temporary exit for Object.wait().
+     // We don't want to use the current stack trace as the lock site, so if we
+     // end up writing the event, we allocate a stack id that we resolve later
+     // when the monitor is really exited. When there are multiple waits, we
+     // reuse the first preallocated stack id.
+     event.set_use_or_preallocate_stack_id_at((TraceTypes::stack_id *) exit_stack_id_for_wait);
+     event.set_resolve_stack(false);
+   } else {
+     // true exit
+     event.set_resolve_stack(true);
+     if (_trace_exit_stack != 0) {
+       event.set_use_stack_id(_trace_exit_stack);
+       event.enable(); // always write the exit event to resolve the stack
+     }
+   }
+   if ((intptr_t(_EntryList) | intptr_t(_cxq)) != 0) {
+      // there are queued threads -- we are definitely writing a trace event
+      event.enable();
+   }
+
+   _trace_exit_stack = 0;
+
    for (;;) {
       assert (THREAD == _owner, "invariant") ;
 
+      //
+      // NOTE: we have removed all code paths for ExitPolicy != 0 and QMode != 0
+      //       knob values for simplicity of event tracing.
+      //
 
-      if (Knob_ExitPolicy == 0) {
          // release semantics: prior loads and stores from within the critical section
          // must not float (reorder) past the following store that drops the lock.
          // On SPARC that requires MEMBAR #loadstore|#storestore.
          // But of course in TSO #loadstore|#storestore is not required.
          // I'd like to write one of the following:

@@ -1015,11 +1058,17 @@
          // in massive wasteful coherency traffic on classic SMP systems.
          // Instead, I use release_store(), which is implemented as just a simple
          // ST on x64, x86 and SPARC.
          OrderAccess::release_store_ptr (&_owner, NULL) ;   // drop the lock
          OrderAccess::storeload() ;                         // See if we need to wake a successor
-         if ((intptr_t(_EntryList)|intptr_t(_cxq)) == 0 || _succ != NULL) {
+      bool queues_empty = ((intptr_t(_EntryList) | intptr_t(_cxq)) == 0);
+      bool have_succ = (_succ != NULL);
+      if (!queues_empty) {
+          // some thread might have entered itself on _cxq in the meantime
+          event.enable();
+      }
+      if (queues_empty || have_succ) {
             TEVENT (Inflated exit - simple egress) ;
             return ;
          }
          TEVENT (Inflated exit - complex egress) ;
 

@@ -1062,129 +1111,14 @@
          //
          if (Atomic::cmpxchg_ptr (THREAD, &_owner, NULL) != NULL) {
             return ;
          }
          TEVENT (Exit - Reacquired) ;
-      } else {
-         if ((intptr_t(_EntryList)|intptr_t(_cxq)) == 0 || _succ != NULL) {
-            OrderAccess::release_store_ptr (&_owner, NULL) ;   // drop the lock
-            OrderAccess::storeload() ;
-            // Ratify the previously observed values.
-            if (_cxq == NULL || _succ != NULL) {
-                TEVENT (Inflated exit - simple egress) ;
-                return ;
-            }
-
-            // inopportune interleaving -- the exiting thread (this thread)
-            // in the fast-exit path raced an entering thread in the slow-enter
-            // path.
-            // We have two choices:
-            // A.  Try to reacquire the lock.
-            //     If the CAS() fails return immediately, otherwise
-            //     we either restart/rerun the exit operation, or simply
-            //     fall-through into the code below which wakes a successor.
-            // B.  If the elements forming the EntryList|cxq are TSM
-            //     we could simply unpark() the lead thread and return
-            //     without having set _succ.
-            if (Atomic::cmpxchg_ptr (THREAD, &_owner, NULL) != NULL) {
-               TEVENT (Inflated exit - reacquired succeeded) ;
-               return ;
-            }
-            TEVENT (Inflated exit - reacquired failed) ;
-         } else {
-            TEVENT (Inflated exit - complex egress) ;
-         }
-      }
 
       guarantee (_owner == THREAD, "invariant") ;
 
       ObjectWaiter * w = NULL ;
-      int QMode = Knob_QMode ;
-
-      if (QMode == 2 && _cxq != NULL) {
-          // QMode == 2 : cxq has precedence over EntryList.
-          // Try to directly wake a successor from the cxq.
-          // If successful, the successor will need to unlink itself from cxq.
-          w = _cxq ;
-          assert (w != NULL, "invariant") ;
-          assert (w->TState == ObjectWaiter::TS_CXQ, "Invariant") ;
-          ExitEpilog (Self, w) ;
-          return ;
-      }
-
-      if (QMode == 3 && _cxq != NULL) {
-          // Aggressively drain cxq into EntryList at the first opportunity.
-          // This policy ensure that recently-run threads live at the head of EntryList.
-          // Drain _cxq into EntryList - bulk transfer.
-          // First, detach _cxq.
-          // The following loop is tantamount to: w = swap (&cxq, NULL)
-          w = _cxq ;
-          for (;;) {
-             assert (w != NULL, "Invariant") ;
-             ObjectWaiter * u = (ObjectWaiter *) Atomic::cmpxchg_ptr (NULL, &_cxq, w) ;
-             if (u == w) break ;
-             w = u ;
-          }
-          assert (w != NULL              , "invariant") ;
-
-          ObjectWaiter * q = NULL ;
-          ObjectWaiter * p ;
-          for (p = w ; p != NULL ; p = p->_next) {
-              guarantee (p->TState == ObjectWaiter::TS_CXQ, "Invariant") ;
-              p->TState = ObjectWaiter::TS_ENTER ;
-              p->_prev = q ;
-              q = p ;
-          }
-
-          // Append the RATs to the EntryList
-          // TODO: organize EntryList as a CDLL so we can locate the tail in constant-time.
-          ObjectWaiter * Tail ;
-          for (Tail = _EntryList ; Tail != NULL && Tail->_next != NULL ; Tail = Tail->_next) ;
-          if (Tail == NULL) {
-              _EntryList = w ;
-          } else {
-              Tail->_next = w ;
-              w->_prev = Tail ;
-          }
-
-          // Fall thru into code that tries to wake a successor from EntryList
-      }
-
-      if (QMode == 4 && _cxq != NULL) {
-          // Aggressively drain cxq into EntryList at the first opportunity.
-          // This policy ensure that recently-run threads live at the head of EntryList.
-
-          // Drain _cxq into EntryList - bulk transfer.
-          // First, detach _cxq.
-          // The following loop is tantamount to: w = swap (&cxq, NULL)
-          w = _cxq ;
-          for (;;) {
-             assert (w != NULL, "Invariant") ;
-             ObjectWaiter * u = (ObjectWaiter *) Atomic::cmpxchg_ptr (NULL, &_cxq, w) ;
-             if (u == w) break ;
-             w = u ;
-          }
-          assert (w != NULL              , "invariant") ;
-
-          ObjectWaiter * q = NULL ;
-          ObjectWaiter * p ;
-          for (p = w ; p != NULL ; p = p->_next) {
-              guarantee (p->TState == ObjectWaiter::TS_CXQ, "Invariant") ;
-              p->TState = ObjectWaiter::TS_ENTER ;
-              p->_prev = q ;
-              q = p ;
-          }
-
-          // Prepend the RATs to the EntryList
-          if (_EntryList != NULL) {
-              q->_next = _EntryList ;
-              _EntryList->_prev = q ;
-          }
-          _EntryList = w ;
-
-          // Fall thru into code that tries to wake a successor from EntryList
-      }
 
       w = _EntryList  ;
       if (w != NULL) {
           // I'd like to write: guarantee (w->_thread != Self).
           // But in practice an exiting thread may find itself on the EntryList.

@@ -1228,39 +1162,19 @@
       // and effectively lengthening the critical section.
       // Invariant: s chases t chases u.
       // TODO-FIXME: consider changing EntryList from a DLL to a CDLL so
       // we have faster access to the tail.
 
-      if (QMode == 1) {
-         // QMode == 1 : drain cxq to EntryList, reversing order
-         // We also reverse the order of the list.
-         ObjectWaiter * s = NULL ;
-         ObjectWaiter * t = w ;
-         ObjectWaiter * u = NULL ;
-         while (t != NULL) {
-             guarantee (t->TState == ObjectWaiter::TS_CXQ, "invariant") ;
-             t->TState = ObjectWaiter::TS_ENTER ;
-             u = t->_next ;
-             t->_prev = u ;
-             t->_next = s ;
-             s = t;
-             t = u ;
-         }
-         _EntryList  = s ;
-         assert (s != NULL, "invariant") ;
-      } else {
-         // QMode == 0 or QMode == 2
          _EntryList = w ;
          ObjectWaiter * q = NULL ;
          ObjectWaiter * p ;
          for (p = w ; p != NULL ; p = p->_next) {
              guarantee (p->TState == ObjectWaiter::TS_CXQ, "Invariant") ;
              p->TState = ObjectWaiter::TS_ENTER ;
              p->_prev = q ;
              q = p ;
          }
-      }
 
       // In 1-0 mode we need: ST EntryList; MEMBAR #storestore; ST _owner = NULL
       // The MEMBAR is satisfied by the release_store() operation in ExitEpilog().
 
       // See if we can abdicate to a spinner instead of waking a thread.

@@ -1366,11 +1280,11 @@
 // complete_exit/reenter operate as a wait without waiting
 // complete_exit requires an inflated monitor
 // The _owner field is not always the Thread addr even with an
 // inflated monitor, e.g. the monitor can be inflated by a non-owning
 // thread due to contention.
-intptr_t ObjectMonitor::complete_exit(TRAPS) {
+void ObjectMonitor::complete_exit(intptr_t *saved_recursions, intptr_t *saved_trace_exit_stack, TRAPS) {
    Thread * const Self = THREAD;
    assert(Self->is_Java_thread(), "Must be Java thread!");
    JavaThread *jt = (JavaThread *)THREAD;
 
    DeferredInitialize();

@@ -1383,29 +1297,30 @@
        OwnerIsThread = 1 ;
     }
    }
 
    guarantee(Self == _owner, "complete_exit not owner");
-   intptr_t save = _recursions; // record the old recursion count
-   _recursions = 0;        // set the recursion level to be 0
-   exit (true, Self) ;           // exit the monitor
+   // record old recursion level and exit stack
+   if (saved_recursions != NULL) *saved_recursions = _recursions;
+   if (saved_recursions != NULL) *saved_trace_exit_stack = _trace_exit_stack;
+   _recursions = 0;
+   exit(saved_trace_exit_stack, true, Self);
    guarantee (_owner != Self, "invariant");
-   return save;
 }
 
 // reenter() enters a lock and sets recursion count
 // complete_exit/reenter operate as a wait without waiting
-void ObjectMonitor::reenter(intptr_t recursions, TRAPS) {
+void ObjectMonitor::reenter(intptr_t saved_recursions, intptr_t saved_trace_exit_stack, TRAPS) {
    Thread * const Self = THREAD;
    assert(Self->is_Java_thread(), "Must be Java thread!");
    JavaThread *jt = (JavaThread *)THREAD;
 
    guarantee(_owner != Self, "reenter already owner");
    enter (THREAD);       // enter the monitor
    guarantee (_recursions == 0, "reenter recursion");
-   _recursions = recursions;
-   return;
+   _recursions = saved_recursions;
+   _trace_exit_stack = saved_trace_exit_stack;
 }
 
 
 // -----------------------------------------------------------------------------
 // A macro is used below because there may already be a pending

@@ -1522,14 +1437,15 @@
    Thread::SpinRelease (&_WaitSetLock) ;
 
    if ((SyncFlags & 4) == 0) {
       _Responsible = NULL ;
    }
-   intptr_t save = _recursions; // record the old recursion count
+   intptr_t saved_recursions = _recursions; // record the old recursion count
+   intptr_t saved_trace_exit_stack = _trace_exit_stack;
    _waiters++;                  // increment the number of waiters
    _recursions = 0;             // set the recursion level to be 1
-   exit (true, Self) ;                    // exit the monitor
+   exit(&saved_trace_exit_stack, true, Self); // exit, knows how to handle exit stack
    guarantee (_owner != Self, "invariant") ;
 
    // The thread is on the WaitSet list - now park() it.
    // On MP systems it's conceivable that a brief spin before we park
    // could be profitable.

@@ -1642,11 +1558,17 @@
      Self->_Stalled = 0 ;
 
      assert (_owner != Self, "invariant") ;
      ObjectWaiter::TStates v = node.TState ;
      if (v == ObjectWaiter::TS_RUN) {
-         enter (Self) ;
+         int after_wait = TraceTypes::enter_after_wait_other;
+         if (node._notified) {
+           after_wait = TraceTypes::enter_after_wait_notify;
+         } else if (ret == OS_TIMEOUT) {
+           after_wait = TraceTypes::enter_after_wait_timeout;
+         }
+         enter (after_wait, Self) ;
      } else {
          guarantee (v == ObjectWaiter::TS_ENTER || v == ObjectWaiter::TS_CXQ, "invariant") ;
          ReenterI (Self, &node) ;
          node.wait_reenter_end(this);
      }

@@ -1661,11 +1583,13 @@
    } // OSThreadWaitState()
 
    jt->set_current_waiting_monitor(NULL);
 
    guarantee (_recursions == 0, "invariant") ;
-   _recursions = save;     // restore the old recursion count
+   // restore the saved recursion count and exit stack
+   _recursions = saved_recursions;
+   _trace_exit_stack = saved_trace_exit_stack;
    _waiters--;             // decrement the number of waiters
 
    // Verify a few postconditions
    assert (_owner == Self       , "invariant") ;
    assert (_succ  != Self       , "invariant") ;

@@ -2527,10 +2451,13 @@
   SETKNOB(ResetEvent) ;
   SETKNOB(MoveNotifyee) ;
   SETKNOB(FastHSSEC) ;
   #undef SETKNOB
 
+  guarantee(Knob_ExitPolicy == 0, "Sorry, event tracing does not support non-default ExitPolicy");
+  guarantee(Knob_QMode == 0,      "Sorry, event tracing does not support non-default QMode");
+
   if (os::is_MP()) {
      BackOffMask = (1 << Knob_SpinBackOff) - 1 ;
      if (Knob_ReportSettings) ::printf ("BackOffMask=%X\n", BackOffMask) ;
      // CONSIDER: BackOffMask = ROUNDUP_NEXT_POWER2 (ncpus-1)
   } else {
< prev index next >