8220795: Rework naked_short_nanosleep on Windows to improve time-to-safepoint
authorrwestberg
Wed, 24 Apr 2019 14:03:20 +0200
changeset 54621 0b6dc5b93306
parent 54620 13b67c1420b8
child 54622 a8dcacf95bff
8220795: Rework naked_short_nanosleep on Windows to improve time-to-safepoint Reviewed-by: dholmes, rehn
src/hotspot/os/windows/os_windows.cpp
src/hotspot/share/runtime/safepoint.cpp
--- a/src/hotspot/os/windows/os_windows.cpp	Thu Apr 25 10:41:49 2019 +0100
+++ b/src/hotspot/os/windows/os_windows.cpp	Wed Apr 24 14:03:20 2019 +0200
@@ -3558,41 +3558,19 @@
   Sleep(ms);
 }
 
+// Windows does not provide sleep functionality with nanosecond resolution, so we
+// try to approximate this with spinning combined with yielding if another thread
+// is ready to run on the current processor.
 void os::naked_short_nanosleep(jlong ns) {
   assert(ns > -1 && ns < NANOUNITS, "Un-interruptable sleep, short time use only");
-  LARGE_INTEGER hundreds_nanos = { 0 };
-  HANDLE wait_timer = ::CreateWaitableTimer(NULL /* attributes*/,
-                                            true /* manual reset */,
-                                            NULL /* name */ );
-  if (wait_timer == NULL) {
-    log_warning(os)("Failed to CreateWaitableTimer: %u", GetLastError());
-    return;
-  }
-
-  // We need a minimum of one hundred nanos.
-  ns = ns > 100 ? ns : 100;
-
-  // Round ns to the nearst hundred of nanos.
-  // Negative values indicate relative time.
-  hundreds_nanos.QuadPart = -((ns + 50) / 100);
-
-  if (::SetWaitableTimer(wait_timer /* handle */,
-                         &hundreds_nanos /* due time */,
-                         0 /* period */,
-                         NULL /* comp func */,
-                         NULL /* comp func args */,
-                         FALSE /* resume */)) {
-    DWORD res = ::WaitForSingleObject(wait_timer /* handle */, INFINITE /* timeout */);
-    if (res != WAIT_OBJECT_0) {
-      if (res == WAIT_FAILED) {
-        log_warning(os)("Failed to WaitForSingleObject: %u", GetLastError());
-      } else {
-        log_warning(os)("Unexpected return from WaitForSingleObject: %s",
-                        res == WAIT_ABANDONED ? "WAIT_ABANDONED" : "WAIT_TIMEOUT");
-      }
+
+  int64_t start = os::javaTimeNanos();
+  do {
+    if (SwitchToThread() == 0) {
+      // Nothing else is ready to run on this cpu, spin a little
+      SpinPause();
     }
-  }
-  ::CloseHandle(wait_timer /* handle */);
+  } while (os::javaTimeNanos() - start < ns);
 }
 
 // Sleep forever; naked call to OS-specific sleep; use with CAUTION
--- a/src/hotspot/share/runtime/safepoint.cpp	Thu Apr 25 10:41:49 2019 +0100
+++ b/src/hotspot/share/runtime/safepoint.cpp	Wed Apr 24 14:03:20 2019 +0200
@@ -186,16 +186,14 @@
 }
 #endif // ASSERT
 
-static void back_off(int iteration) {
-  // iteration will be 1 the first time we enter this spin back-off.
-  // naked_short_nanosleep takes tenths of micros which means that
-  // number of nanoseconds is irrelevant if it's below that. We do
-  // 20 1 ns sleeps with a total cost of ~1 ms, then we do 1 ms sleeps.
-  jlong sleep_ns = 1;
-  if (iteration > 20) {
-    sleep_ns = NANOUNITS / MILLIUNITS;  // 1 ms
+static void back_off(int64_t start_time) {
+  // We start with fine-grained nanosleeping until a millisecond has
+  // passed, at which point we resort to plain naked_short_sleep.
+  if (os::javaTimeNanos() - start_time < NANOSECS_PER_MILLISEC) {
+    os::naked_short_nanosleep(10 * (NANOUNITS / MICROUNITS));
+  } else {
+    os::naked_short_sleep(1);
   }
-  os::naked_short_nanosleep(sleep_ns);
 }
 
 int SafepointSynchronize::synchronize_threads(jlong safepoint_limit_time, int nof_threads, int* initial_running)
@@ -229,9 +227,16 @@
 
   *initial_running = still_running;
 
-  int iterations = 1; // The first iteration is above.
+  // If there is no thread still running, we are already done.
+  if (still_running <= 0) {
+    assert(tss_head == NULL, "Must be empty");
+    return 1;
+  }
 
-  while (still_running > 0) {
+  int iterations = 1; // The first iteration is above.
+  int64_t start_time = os::javaTimeNanos();
+
+  do {
     // Check if this has taken too long:
     if (SafepointTimeout && safepoint_limit_time < os::javaTimeNanos()) {
       print_safepoint_timeout();
@@ -264,11 +269,11 @@
     DEBUG_ONLY(assert_list_is_valid(tss_head, still_running);)
 
     if (still_running > 0) {
-      back_off(iterations);
+      back_off(start_time);
     }
 
     iterations++;
-  }
+  } while (still_running > 0);
 
   assert(tss_head == NULL, "Must be empty");