< prev index next >

src/hotspot/share/runtime/objectMonitor.cpp

Print this page
rev 54415 : 8222295: more baseline cleanups from Async Monitor Deflation project
rev 54416 : Checkpoint latest preliminary review patches for full OpenJDK review; merge with 8222295.patch.
rev 54417 : imported patch dcubed.monitor_deflate_conc.v2.01

@@ -298,13 +298,15 @@
 
   // Prevent deflation. See ObjectSynchronizer::deflate_monitor() and is_busy().
   // Ensure the object-monitor relationship remains stable while there's contention.
   const jint contentions = Atomic::add(1, &_contentions);
   if (contentions <= 0 && _owner == DEFLATER_MARKER) {
-    // Async deflation in progress. Help deflater thread install
-    // the mark word (in case deflater thread is slow).
-    install_displaced_markword_in_object();
+    // Async deflation is in progress. Attempt to restore the
+    // header/dmw to the object's header so that we only retry once
+    // if the deflater thread happens to be slow.
+    const oop obj = (oop) object();
+    install_displaced_markword_in_object(obj);
     Self->_Stalled = 0;
     return false;  // Caller should retry. Never mind about _contentions as this monitor has been deflated.
   }
   // The deflater thread will not deflate this monitor and the monitor is contended, continue.
 

@@ -424,69 +426,87 @@
   // We can either return -1 or retry.
   // Retry doesn't make as much sense because the lock was just acquired.
   return -1;
 }
 
-// Install the displaced markword of a deflated monitor into the object
-// associated with the monitor.
-// This method is idempotent and is executed by both mutators wanting to
-// acquire a monitor for an object and the thread deflating monitors.
-// A mutator trying to install a hash in the monitor's _header field can
-// also run in parallel to this method.
-void ObjectMonitor::install_displaced_markword_in_object() {
+// Install the displaced mark word (dmw) of a deflating ObjectMonitor
+// into the header of the object associated with the monitor. This
+// idempotent method is called by a thread that is deflating a
+// monitor and by other threads that have detected a race with the
+// deflation process.
+void ObjectMonitor::install_displaced_markword_in_object(const oop obj) {
+  // This function must only be called when (owner == DEFLATER_MARKER
+  // && contentions <= 0), but we can't guarantee that here because
+  // those values could change when the ObjectMonitor gets moved from
+  // the global free list to a per-thread free list.
+
+  guarantee(obj != NULL, "must be non-NULL");
+  if (object() != obj) {
+    // ObjectMonitor's object ref no longer refers to the target object
+    // so the object's header has already been restored.
+    return;
+  }
+
   markOop dmw = header();
   if (dmw == NULL) {
-    // The thread deflating monitors has won the race so we
-    // have nothing to do.
+    // ObjectMonitor's header/dmw has been cleared by the deflating
+    // thread so the object's header has already been restored.
     return;
   }
 
-  // A non-NULL dmw has to be either neutral or is participating in
-  // this restoration protocol.
+  // A non-NULL dmw has to be either neutral (not locked and not marked)
+  // or is already participating in this restoration protocol.
   assert(dmw->is_neutral() || (dmw->is_marked() && dmw->hash() == 0),
-         "failed precondition: is_neutral=%d, is_marked=%d, hash="
-         INTPTR_FORMAT, dmw->is_neutral(), dmw->is_marked(), dmw->hash());
+         "failed precondition: dmw=" INTPTR_FORMAT, p2i(dmw));
 
+  markOop marked_dmw = NULL;
   if (!dmw->is_marked() && dmw->hash() == 0) {
-    // This dmw is neutral and has not yet started the restoration
-    // protocol so we mark a copy of the dmw to begin the protocol.
-    markOop marked_dmw = dmw->set_marked();
-    assert(marked_dmw->is_marked() && marked_dmw->hash() == 0,
-           "sanity_check: is_marked=%d, hash=" INTPTR_FORMAT,
-           marked_dmw->is_marked(), marked_dmw->hash());
-
-    // There can be three different racers trying to update the _header
-    // field and the return dmw value will tell us what cleanup needs
-    // to be done (if any) after the race winner:
-    //   1)  A mutator trying to install a hash in the object.
-    //       Note: That mutator is not executing this code, but it is
-    //       trying to update the _header field.
-    //       If winner: dmw will contain the hash and be unmarked
-    //   2a) A mutator trying to acquire the monitor via enter():
-    //       If winner: dmw is marked and hash() == 0
-    //   2b) The thread deflating the monitor via deflate_monitor_using_JT():
-    //       If winner: dmw is marked and hash() == 0
+    // This dmw has not yet started the restoration protocol so we
+    // mark a copy of the dmw to begin the protocol.
+    // Note: A dmw with a hashcode does not take this code path.
+    marked_dmw = dmw->set_marked();
+
+    // All of the callers to this function can be racing with each
+    // other trying to update the _header field.
     dmw = (markOop) Atomic::cmpxchg(marked_dmw, &_header, dmw);
+    if (dmw == NULL) {
+      // ObjectMonitor's header/dmw has been cleared by the deflating
+      // thread so the object's header has already been restored.
+      return;
+    }
+    // The _header field is now marked. The winner's 'dmw' variable
+    // contains the original, unmarked header/dmw value and any
+    // losers have a marked header/dmw value that will be cleaned
+    // up below.
   }
 
   if (dmw->is_marked()) {
-    // The dmw copy is marked which means a hash was not set by a racing
-    // thread. Clear the mark from the copy in preparation for possible
-    // restoration from this thread.
-    assert(dmw->hash() == 0, "must be 0: hash=" INTPTR_FORMAT, dmw->hash());
+    // Clear the mark from the header/dmw copy in preparation for
+    // possible restoration from this thread.
+    assert(dmw->hash() == 0, "hashcode must be 0: dmw=" INTPTR_FORMAT,
+           p2i(dmw));
     dmw = dmw->set_unmarked();
   }
-  assert(dmw->is_neutral(), "must be a neutral markword");
+  assert(dmw->is_neutral(), "must be neutral: dmw=" INTPTR_FORMAT, p2i(dmw));
 
-  oop const obj = (oop) object();
-  // Install displaced markword if object markword still points to this
-  // monitor. Both the mutator trying to enter() and the thread deflating
-  // the monitor will reach this point, but only one can win.
-  // Note: If a mutator won the cmpxchg() race above and installed a hash
-  // in _header, then the updated dmw contains that hash and we'll install
-  // it in the object's markword here.
+  // Install displaced mark word if the object's header still points
+  // to this ObjectMonitor. All racing callers to this function will
+  // reach this point, but only one can win.
   obj->cas_set_mark(dmw, markOopDesc::encode(this));
+
+  // Note: It does not matter which thread restored the header/dmw
+  // into the object's header. The thread deflating the monitor just
+  // wanted the object's header restored and it is. The threads that
+  // detected a race with the deflation process also wanted the
+  // object's header restored before they retry their operation and
+  // because it is restored they will only retry once.
+
+  if (marked_dmw != NULL) {
+    // Clear _header to NULL if it is still marked_dmw so a racing
+    // install_displaced_markword_in_object() can bail out sooner.
+    Atomic::cmpxchg((markOop)NULL, &_header, marked_dmw);
+  }
 }
 
 #define MAX_RECHECK_INTERVAL 1000
 
 void ObjectMonitor::EnterI(TRAPS) {

@@ -501,16 +521,19 @@
     assert(_Responsible != Self, "invariant");
     return;
   }
 
   if (_owner == DEFLATER_MARKER) {
-    guarantee(0 < _contentions, "_owner == DEFLATER_MARKER && _contentions <= 0 should have been handled by the caller");
-    // Deflater thread tried to lock this monitor, but it failed to make _contentions negative and gave up.
-    // Try to acquire monitor.
+    // The deflation protocol finished the first part (setting _owner), but
+    // it failed the second part (making _contentions negative) and bailed.
+    // Because we're called from enter() we have at least one contention.
+    guarantee(_contentions > 0, "owner == DEFLATER_MARKER && contentions <= 0 "
+              "should have been handled by the caller: contentions=%d",
+              _contentions);
     if (Atomic::cmpxchg(Self, &_owner, DEFLATER_MARKER) == DEFLATER_MARKER) {
+      // Acquired the monitor.
       assert(_succ != Self, "invariant");
-      assert(_owner == Self, "invariant");
       assert(_Responsible != Self, "invariant");
       return;
     }
   }
 

@@ -629,12 +652,16 @@
     }
 
     if (TryLock(Self) > 0) break;
 
     if (_owner == DEFLATER_MARKER) {
-      guarantee(0 < _contentions, "_owner == DEFLATER_MARKER && _contentions <= 0 should have been handled by the caller");
-      // Deflater thread tried to lock this monitor, but it failed to make _contentions negative and gave up.
+      // The deflation protocol finished the first part (setting _owner), but
+      // it failed the second part (making _contentions negative) and bailed.
+      // Because we're called from enter() we have at least one contention.
+      guarantee(_contentions > 0, "owner == DEFLATER_MARKER && contentions <= 0 "
+                "should have been handled by the caller: contentions=%d",
+                _contentions);
       if (Atomic::cmpxchg(Self, &_owner, DEFLATER_MARKER) == DEFLATER_MARKER) {
         // Acquired the monitor.
         break;
       }
     }

@@ -759,11 +786,16 @@
 
     if (TryLock(Self) > 0) break;
     if (TrySpin(Self) > 0) break;
 
     if (_owner == DEFLATER_MARKER) {
-      guarantee(0 <= _contentions, "Impossible: _owner == DEFLATER_MARKER && _contentions < 0, monitor must not be owned by deflater thread here");
+      // The deflation protocol finished the first part (setting _owner),
+      // but it will observe _waiters != 0 and will bail out. Because we're
+      // called from wait() we may or may not have any contentions.
+      guarantee(_contentions >= 0, "owner == DEFLATER_MARKER && contentions < 0 "
+                "should have been handled by the caller: contentions=%d",
+                _contentions);
       if (Atomic::cmpxchg(Self, &_owner, DEFLATER_MARKER) == DEFLATER_MARKER) {
         // Acquired the monitor.
         break;
       }
     }

@@ -973,11 +1005,12 @@
       // see x86_32.ad Fast_Unlock() and the I1 and I2 properties.
       // Upon deeper reflection, however, in a properly run JVM the only
       // way we should encounter this situation is in the presence of
       // unbalanced JNI locking. TODO: CheckJNICalls.
       // See also: CR4414101
-      assert(false, "Non-balanced monitor enter/exit! Likely JNI locking");
+      assert(false, "Non-balanced monitor enter/exit! Likely JNI locking: "
+             "owner=" INTPTR_FORMAT, p2i(_owner));
       return;
     }
   }
 
   if (_recursions != 0) {

@@ -1465,11 +1498,11 @@
 
     assert(_owner != Self, "invariant");
     ObjectWaiter::TStates v = node.TState;
     if (v == ObjectWaiter::TS_RUN) {
       const bool success = enter(Self);
-      guarantee(success, "enter signaled for a retry, but monitor should not have been deflated as waiters > 0");
+      ADIM_guarantee(success, "enter signaled for a retry, but monitor should not have been deflated as waiters > 0");
     } else {
       guarantee(v == ObjectWaiter::TS_ENTER || v == ObjectWaiter::TS_CXQ, "invariant");
       ReenterI(Self, &node);
       node.wait_reenter_end(this);
     }

@@ -2054,30 +2087,34 @@
 // saved. This function returns false if we have lost a race with
 // async deflation; the caller should retry as appropriate.
 //
 bool ObjectMonitorHandle::save_om_ptr(oop object, markOop mark) {
   guarantee(mark->has_monitor(), "sanity check: mark=" INTPTR_FORMAT,
-            p2i((address)mark));
+            p2i(mark));
 
   ObjectMonitor * om_ptr = mark->monitor();
   om_ptr->inc_ref_count();
 
   if (AsyncDeflateIdleMonitors) {
     // Race here if monitor is not owned! The above ref_count bump
     // will cause subsequent async deflation to skip it. However,
     // previous or concurrent async deflation is a race.
-    if (om_ptr->_owner == DEFLATER_MARKER) {
-      // Async deflation won the race so we have to retry.
+    if (om_ptr->_owner == DEFLATER_MARKER && om_ptr->_contentions <= 0) {
+      // Async deflation is in progress. Attempt to restore the
+      // header/dmw to the object's header so that we only retry once
+      // if the deflater thread happens to be slow.
+      om_ptr->install_displaced_markword_in_object(object);
       om_ptr->dec_ref_count();
       return false;
     }
     // The ObjectMonitor could have been deflated and reused for
     // another object before we bumped the ref_count so make sure
     // our object still refers to this ObjectMonitor.
     const markOop tmp = object->mark();
     if (!tmp->has_monitor() || tmp->monitor() != om_ptr) {
       // Async deflation and reuse won the race so we have to retry.
+      // Skip object header restoration since that's already done.
       om_ptr->dec_ref_count();
       return false;
     }
   }
 

@@ -2087,13 +2124,15 @@
   return true;
 }
 
 // For internal use by ObjectSynchronizer::inflate().
 void ObjectMonitorHandle::set_om_ptr(ObjectMonitor * om_ptr) {
-  // Cannot guarantee() is_new() here. As soon as the ObjectMonitor*
-  // is attached to the object in inflate(), it can be used by other
-  // JavaThreads.
-  // guarantee(om_ptr->is_new(), "sanity check: allocation_state=%d",
-  //           int(om_ptr->allocation_state()));
+  if (_om_ptr == NULL) {
+    guarantee(om_ptr != NULL, "cannot clear an unset om_ptr");
   om_ptr->inc_ref_count();
   _om_ptr = om_ptr;
+  } else {
+    guarantee(om_ptr == NULL, "can only clear a set om_ptr");
+    _om_ptr->dec_ref_count();
+    _om_ptr = NULL;
+  }
 }
< prev index next >