< prev index next >

src/os/posix/vm/os_posix.cpp

Print this page

        

@@ -29,17 +29,18 @@
 #include "runtime/interfaceSupport.hpp"
 #include "runtime/os.hpp"
 #include "utilities/macros.hpp"
 #include "utilities/vmError.hpp"
 
-#include <signal.h>
-#include <unistd.h>
-#include <sys/resource.h>
-#include <sys/utsname.h>
+#include <dlfcn.h>
 #include <pthread.h>
 #include <semaphore.h>
 #include <signal.h>
+#include <sys/resource.h>
+#include <sys/utsname.h>
+#include <time.h>
+#include <unistd.h>
 
 // Todo: provide a os::get_max_process_id() or similar. Number of processes
 // may have been configured, can be read more accurately from proc fs etc.
 #ifndef MAX_PID
 #define MAX_PID INT_MAX

@@ -1392,5 +1393,570 @@
     }
   }
 }
 
 #endif // __APPLE__
+
+
+// Shared pthread_mutex/cond based PlatformEvent implementation.
+// Not currently usable by Solaris
+
+#ifndef SOLARIS
+
+// Shared condattr object for use with relative timed-waits. Will be associated
+// with CLOCK_MONOTONIC if available to avoid issues with time-of-day changes,
+// but otherwise whatever default is used by the platform - generally the
+// time-of-day clock
+static pthread_condattr_t _condAttr[1];
+
+// Shared mutexattr to explicitly set the type to PTHREAD_MUTEX_NORMAL as not
+// all systems (e.g. FreeBSD) map the default to "normal".
+static pthread_mutexattr_t _mutexAttr[1];
+
+// common basic initialization that is always supported
+static void pthread_init_common(void) {
+  int status;
+  if ((status = pthread_condattr_init(_condAttr)) != 0) {
+    fatal("pthread_condattr_init: %s", os::strerror(status));
+  }
+  if ((status = pthread_mutexattr_init(_mutexAttr)) != 0) {
+    fatal("pthread_mutexattr_init: %s", os::strerror(status));
+  }
+  if ((status = pthread_mutexattr_settype(_mutexAttr, PTHREAD_MUTEX_NORMAL)) != 0) {
+    fatal("pthread_mutexattr_settype: %s", os::strerror(status));
+  }
+}
+
+// Not all POSIX types and API's are available on all notionally "posix"
+// platforms. If we have build-time support then we will check for actual
+// runtime support via dlopen/dlsym lookup. This allows for running on an
+// older OS version compared to the build platform. But if there is no
+// build time support then there can not be any runtime support as we do not
+// know what the runtime types would be (for example clockid_t might be an
+// int or int64_t.
+//
+#ifdef SUPPORTS_CLOCK_MONOTONIC
+
+// This means we have clockid_t, clock_gettime et al and CLOCK_MONOTONIC
+
+static int (*_clock_gettime)(clockid_t, struct timespec *);
+static int (*_pthread_condattr_setclock)(pthread_condattr_t *, clockid_t);
+
+static bool _use_clock_monotonic_condattr;
+
+// determine what POSIX API's are present and do appropriate
+// configuration
+void os::Posix::init(void) {
+
+  // NOTE: no logging available when this is called. Put logging
+  // statements in init_2().
+
+  // copied from os::Linux::clock_init(). The duplication is temporary.
+
+  // 1. Check for CLOCK_MONOTONIC support
+
+  void* handle = NULL;
+
+  // For linux we need librt, for other OS we can find
+  // this function in regular libc
+#ifdef NEEDS_LIBRT
+  // we do dlopen's in this particular order due to bug in linux
+  // dynamical loader (see 6348968) leading to crash on exit
+  handle = dlopen("librt.so.1", RTLD_LAZY);
+  if (handle == NULL) {
+    handle = dlopen("librt.so", RTLD_LAZY);
+  }
+#endif
+
+  if (handle == NULL) {
+    handle = RTLD_DEFAULT;
+  }
+
+  _clock_gettime = NULL;
+
+  int (*clock_getres_func)(clockid_t, struct timespec*) =
+    (int(*)(clockid_t, struct timespec*))dlsym(handle, "clock_getres");
+  int (*clock_gettime_func)(clockid_t, struct timespec*) =
+    (int(*)(clockid_t, struct timespec*))dlsym(handle, "clock_gettime");
+  if (clock_getres_func != NULL && clock_gettime_func != NULL) {
+    // we assume that if both clock_gettime and clock_getres support
+    // CLOCK_MONOTONIC then the OS provides true high-res monotonic clock
+    struct timespec res;
+    struct timespec tp;
+    if (clock_getres_func(CLOCK_MONOTONIC, &res) == 0 &&
+        clock_gettime_func(CLOCK_MONOTONIC, &tp)  == 0) {
+      // yes, monotonic clock is supported
+      _clock_gettime = clock_gettime_func;
+    } else {
+#ifdef NEEDS_LIBRT
+      // close librt if there is no monotonic clock
+      if (handle != RTLD_DEFAULT) {
+        dlclose(handle);
+      }
+#endif
+    }
+  }
+  
+  // 2. Check for pthread_condattr_setclock support
+  
+  _pthread_condattr_setclock = NULL;
+
+  // libpthread is already loaded
+  int (*condattr_setclock_func)(pthread_condattr_t*, clockid_t) =
+    (int (*)(pthread_condattr_t*, clockid_t))dlsym(RTLD_DEFAULT,
+                                                   "pthread_condattr_setclock");
+  if (condattr_setclock_func != NULL) {
+    _pthread_condattr_setclock = condattr_setclock_func;
+  }
+
+  // Now do general initialization
+
+  pthread_init_common();
+
+  int status;
+  if (_pthread_condattr_setclock != NULL &&
+      _clock_gettime != NULL) {
+    _use_clock_monotonic_condattr = true;
+
+    if ((status = _pthread_condattr_setclock(_condAttr, CLOCK_MONOTONIC)) != 0) {
+      if (status == EINVAL) {
+        _use_clock_monotonic_condattr = false;
+        warning("Unable to use monotonic clock with relative timed-waits" \
+                " - changes to the time-of-day clock may have adverse affects");
+      } else {
+        fatal("pthread_condattr_setclock: %s", os::strerror(status));
+      }
+    }
+  }
+  else {
+    _use_clock_monotonic_condattr = false;
+  }
+}
+
+void os::Posix::init_2(void) {
+  log_info(os)("Use of CLOCK_MONOTONIC is%s supported",
+               (_clock_gettime != NULL ? "" : " not"));
+  log_info(os)("Use of pthread_condattr_setclock is%s supported",
+               (_pthread_condattr_setclock != NULL ? "" : " not"));
+  log_info(os)("Relative timed-wait using pthread_cond_timedwait is associated with %s",
+               _use_clock_monotonic_condattr ? "CLOCK_MONOTONIC" : "the default clock");
+}
+
+#else // !SUPPORTS_CLOCK_MONOTONIC
+
+void os::Posix::init(void) {
+  pthread_init_common();
+}
+
+void os::Posix::init_2(void) {
+  log_info(os)("Use of CLOCK_MONOTONIC is not supported");
+  log_info(os)("Use of pthread_condattr_setclock is not supported");
+  log_info(os)("Relative timed-wait using pthread_cond_timedwait is associated with the default clock");
+}
+
+#endif // SUPPORTS_CLOCK_MONOTONIC
+
+os::PlatformEvent::PlatformEvent() {
+  int status = pthread_cond_init(_cond, _condAttr);
+  assert_status(status == 0, status, "cond_init");
+  status = pthread_mutex_init(_mutex, _mutexAttr);
+  assert_status(status == 0, status, "mutex_init");
+  _event   = 0;
+  _nParked = 0;
+}
+
+// Utility to convert the given timeout to an absolute timespec
+// (based on the appropriate clock) to use with pthread_cond_timewait.
+// The clock queried here must be the clock used to manage the 
+// timeout of the condition variable.
+//
+// The passed in timeout value is either a relative time in nanoseconds
+// or an absolute time in milliseconds. A relative timeout will be
+// associated with CLOCK_MONOTONIC if available; otherwise, or if absolute,
+// the default time-of-day clock will be used.
+
+// Given time is a 64-bit value and the time_t used in the timespec is
+// sometimes a signed-32-bit value we have to watch for overflow if times
+// way in the future are given. Further on Solaris versions
+// prior to 10 there is a restriction (see cond_timedwait) that the specified
+// number of seconds, in abstime, is less than current_time + 100,000,000.
+// As it will be over 20 years before "now + 100000000" will overflow we can
+// ignore overflow and just impose a hard-limit on seconds using the value
+// of "now + 100,000,000". This places a limit on the timeout of about 3.17
+// years from "now".
+//
+#define MAX_SECS 100000000
+
+static void to_abstime(timespec* abstime, jlong timeout, bool isAbsolute) {
+
+  if (timeout < 0)
+    timeout = 0;
+
+  time_t max_secs = 0;
+
+#ifdef SUPPORTS_CLOCK_MONOTONIC
+
+  if (_use_clock_monotonic_condattr && !isAbsolute) {
+    // relative timeout in nanoseconds
+    jlong seconds = timeout / NANOUNITS;
+    timeout %= NANOUNITS; // remaining nanos
+
+    struct timespec now;
+    int status = _clock_gettime(CLOCK_MONOTONIC, &now);
+    assert_status(status == 0, status, "clock_gettime");
+
+    max_secs = now.tv_sec + MAX_SECS;
+    if (seconds >= MAX_SECS) {
+      // More seconds than we can add, so pin to max_secs
+      abstime->tv_sec = max_secs;
+      abstime->tv_nsec = 0;
+    }
+    else {
+      abstime->tv_sec = now.tv_sec  + seconds;
+      long nsecs = now.tv_nsec + timeout;
+      if (nsecs >= NANOUNITS) { // overflow
+        abstime->tv_sec += 1;
+        nsecs -= NANOUNITS;
+      }
+      abstime->tv_nsec = nsecs;
+    }
+  }
+  else {
+
+#else
+
+  { // match the block scope
+
+#endif // SUPPORTS_CLOCK_MONOTONIC
+
+    // time-of-day clock is all we can reliably use
+    struct timeval now;
+    int status = gettimeofday(&now, NULL);
+    assert(status == 0, "gettimeofday");
+    max_secs = now.tv_sec + MAX_SECS;
+
+    if (isAbsolute) {
+      // absolute timeout in milliseconds
+      jlong seconds = timeout / MILLIUNITS;
+      timeout %= MILLIUNITS; // remaining millis
+
+      if (seconds >= max_secs) {
+        // Absolue seconds exceeds allow max, so pin to max_secs
+        abstime->tv_sec = max_secs;
+        abstime->tv_nsec = 0;
+      }
+      else {
+        abstime->tv_sec = seconds;
+        abstime->tv_nsec = timeout * (NANOUNITS/MILLIUNITS);
+      }
+    }
+    else {
+      // relative timeout in nanoseconds
+      jlong seconds = timeout / NANOUNITS;
+      timeout %= NANOUNITS; // remaining nanos
+
+      if (seconds >= MAX_SECS) {
+        // More seconds than we can add, so pin to max_secs
+        abstime->tv_sec = max_secs;
+        abstime->tv_nsec = 0;
+      }        
+      else {
+        abstime->tv_sec = now.tv_sec + seconds;
+        jlong micros_left = timeout / (NANOUNITS/MICROUNITS);
+        long usec = now.tv_usec + micros_left;
+        if (usec >= MICROUNITS) { // overflow
+          abstime->tv_sec += 1;
+          usec -= MICROUNITS;
+        }
+        abstime->tv_nsec = usec * (NANOUNITS/MICROUNITS);
+      }
+    }
+  }
+
+  assert(abstime->tv_sec >= 0, "tv_sec < 0");
+  assert(abstime->tv_sec <= max_secs, "tv_sec > max_secs");
+  assert(abstime->tv_nsec >= 0, "tv_nsec < 0");
+  assert(abstime->tv_nsec < NANOSECS_PER_SEC, "tv_nsec >= nanos_per_sec");
+  
+}
+
+// PlatformEvent
+//
+// Assumption:
+//    Only one parker can exist on an event, which is why we allocate
+//    them per-thread. Multiple unparkers can coexist.
+//
+// _event serves as a restricted-range semaphore.
+//   -1 : thread is blocked, i.e. there is a waiter
+//    0 : neutral: thread is running or ready,
+//        could have been signaled after a wait started
+//    1 : signaled - thread is running or ready
+//
+//    Having three states allows for some detection of bad usage - see
+//    comments on unpark().
+
+void os::PlatformEvent::park() {       // AKA "down()"
+  // Transitions for _event:
+  //   -1 => -1 : illegal
+  //    1 =>  0 : pass - return immediately
+  //    0 => -1 : block; then set _event to 0 before returning
+
+  // Invariant: Only the thread associated with the PlatformEvent
+  // may call park().
+  assert(_nParked == 0, "invariant");
+
+  int v;
+
+  // atomically decrement _event
+  for (;;) {
+    v = _event;
+    if (Atomic::cmpxchg(v-1, &_event, v) == v) break;
+  }
+  guarantee(v >= 0, "invariant");
+
+  if (v == 0) { // Do this the hard way by blocking ...
+    int status = pthread_mutex_lock(_mutex);
+    assert_status(status == 0, status, "mutex_lock");
+    guarantee(_nParked == 0, "invariant");
+    ++_nParked;
+    while (_event < 0) {
+      // OS-level "spurious wakeups" are ignored
+      status = pthread_cond_wait(_cond, _mutex);
+      assert_status(status == 0, status, "cond_wait");
+    }
+    --_nParked;
+
+    _event = 0;
+    status = pthread_mutex_unlock(_mutex);
+    assert_status(status == 0, status, "mutex_unlock");
+    // Paranoia to ensure our locked and lock-free paths interact
+    // correctly with each other.
+    OrderAccess::fence();
+  }
+  guarantee(_event >= 0, "invariant");
+}
+
+int os::PlatformEvent::park(jlong millis) {
+  // Transitions for _event:
+  //   -1 => -1 : illegal
+  //    1 =>  0 : pass - return immediately
+  //    0 => -1 : block; then set _event to 0 before returning
+
+  // Invariant: Only the thread associated with the Event/PlatformEvent
+  // may call park().
+  assert(_nParked == 0, "invariant");
+
+  int v;
+  // atomically decrement _event
+  for (;;) {
+    v = _event;
+    if (Atomic::cmpxchg(v-1, &_event, v) == v) break;
+  }
+  guarantee(v >= 0, "invariant");
+
+  if (v == 0) { // Do this the hard way by blocking ...
+    struct timespec abst;
+    to_abstime(&abst, millis * (NANOUNITS/MILLIUNITS), false);
+
+    int ret = OS_TIMEOUT;
+    int status = pthread_mutex_lock(_mutex);
+    assert_status(status == 0, status, "mutex_lock");
+    guarantee(_nParked == 0, "invariant");
+    ++_nParked;
+
+    while (_event < 0) {
+      status = pthread_cond_timedwait(_cond, _mutex, &abst);
+      assert_status(status == 0 || status == ETIMEDOUT,
+                    status, "cond_timedwait");
+      // OS-level "spurious wakeups" are ignored unless the archaic
+      // FilterSpuriousWakeups is set false. That flag should be obsoleted.
+      if (!FilterSpuriousWakeups) break;
+      if (status == ETIMEDOUT) break;
+    }
+    --_nParked;
+
+    if (_event >= 0) {
+      ret = OS_OK;
+    }
+
+    _event = 0;
+    status = pthread_mutex_unlock(_mutex);
+    assert_status(status == 0, status, "mutex_unlock");
+    // Paranoia to ensure our locked and lock-free paths interact
+    // correctly with each other.
+    OrderAccess::fence();
+    return ret;
+  }
+  return OS_OK;
+}
+
+void os::PlatformEvent::unpark() {
+  // Transitions for _event:
+  //    0 => 1 : just return
+  //    1 => 1 : just return
+  //   -1 => either 0 or 1; must signal target thread
+  //         That is, we can safely transition _event from -1 to either
+  //         0 or 1.
+  // See also: "Semaphores in Plan 9" by Mullender & Cox
+  //
+  // Note: Forcing a transition from "-1" to "1" on an unpark() means
+  // that it will take two back-to-back park() calls for the owning
+  // thread to block. This has the benefit of forcing a spurious return
+  // from the first park() call after an unpark() call which will help
+  // shake out uses of park() and unpark() without checking state conditions
+  // properly. This spurious return doesn't manifest itself in any user code
+  // but only in the correctly written condition checking loops of ObjectMonitor,
+  // Mutex/Monitor, Thread::muxAcquire and os::sleep
+
+  if (Atomic::xchg(1, &_event) >= 0) return;
+
+  int status = pthread_mutex_lock(_mutex);
+  assert_status(status == 0, status, "mutex_lock");
+  int anyWaiters = _nParked;
+  assert(anyWaiters == 0 || anyWaiters == 1, "invariant");
+  status = pthread_mutex_unlock(_mutex);
+  assert_status(status == 0, status, "mutex_unlock");
+
+  // Note that we signal() *after* dropping the lock for "immortal" Events.
+  // This is safe and avoids a common class of futile wakeups.  In rare
+  // circumstances this can cause a thread to return prematurely from
+  // cond_{timed}wait() but the spurious wakeup is benign and the victim
+  // will simply re-test the condition and re-park itself.
+  // This provides particular benefit if the underlying platform does not
+  // provide wait morphing.
+
+  if (anyWaiters != 0) {
+    status = pthread_cond_signal(_cond);
+    assert_status(status == 0, status, "cond_signal");
+  }
+}
+
+// JSR166 support
+
+ os::PlatformParker::PlatformParker() {
+  int status;
+  status = pthread_cond_init(&_cond[REL_INDEX], _condAttr);
+  assert_status(status == 0, status, "cond_init rel");
+  status = pthread_cond_init(&_cond[ABS_INDEX], NULL);
+  assert_status(status == 0, status, "cond_init abs");
+  status = pthread_mutex_init(_mutex, _mutexAttr);
+  assert_status(status == 0, status, "mutex_init");
+  _cur_index = -1; // mark as unused
+}
+
+// Parker::park decrements count if > 0, else does a condvar wait.  Unpark
+// sets count to 1 and signals condvar.  Only one thread ever waits
+// on the condvar. Contention seen when trying to park implies that someone
+// is unparking you, so don't wait. And spurious returns are fine, so there
+// is no need to track notifications.
+
+void Parker::park(bool isAbsolute, jlong time) {
+
+  // Optional fast-path check:
+  // Return immediately if a permit is available.
+  // We depend on Atomic::xchg() having full barrier semantics
+  // since we are doing a lock-free update to _counter.
+  if (Atomic::xchg(0, &_counter) > 0) return;
+
+  Thread* thread = Thread::current();
+  assert(thread->is_Java_thread(), "Must be JavaThread");
+  JavaThread *jt = (JavaThread *)thread;
+
+  // Optional optimization -- avoid state transitions if there's 
+  // an interrupt pending.
+  if (Thread::is_interrupted(thread, false)) {
+    return;
+  }
+
+  // Next, demultiplex/decode time arguments
+  struct timespec absTime;
+  if (time < 0 || (isAbsolute && time == 0)) { // don't wait at all
+    return;
+  }
+  if (time > 0) {
+    to_abstime(&absTime, time, isAbsolute);
+  }
+
+  // Enter safepoint region
+  // Beware of deadlocks such as 6317397.
+  // The per-thread Parker:: mutex is a classic leaf-lock.
+  // In particular a thread must never block on the Threads_lock while
+  // holding the Parker:: mutex.  If safepoints are pending both the
+  // the ThreadBlockInVM() CTOR and DTOR may grab Threads_lock.
+  ThreadBlockInVM tbivm(jt);
+
+  // Don't wait if cannot get lock since interference arises from
+  // unparking. Also re-check interrupt before trying wait.
+  if (Thread::is_interrupted(thread, false) || 
+      pthread_mutex_trylock(_mutex) != 0) {
+    return;
+  }
+
+  int status;
+  if (_counter > 0)  { // no wait needed
+    _counter = 0;
+    status = pthread_mutex_unlock(_mutex);
+    assert_status(status == 0, status, "invariant");
+    // Paranoia to ensure our locked and lock-free paths interact
+    // correctly with each other and Java-level accesses.
+    OrderAccess::fence();
+    return;
+  }
+
+  OSThreadWaitState osts(thread->osthread(), false /* not Object.wait() */);
+  jt->set_suspend_equivalent();
+  // cleared by handle_special_suspend_equivalent_condition() or java_suspend_self()
+
+  assert(_cur_index == -1, "invariant");
+  if (time == 0) {
+    _cur_index = REL_INDEX; // arbitrary choice when not timed
+    status = pthread_cond_wait(&_cond[_cur_index], _mutex);
+    assert_status(status == 0, status, "cond_timedwait");
+  } 
+  else {
+    _cur_index = isAbsolute ? ABS_INDEX : REL_INDEX;
+    status = pthread_cond_timedwait(&_cond[_cur_index], _mutex, &absTime);
+    assert_status(status == 0 || status == ETIMEDOUT,
+                  status, "cond_timedwait");
+  }
+  _cur_index = -1;
+
+  _counter = 0;
+  status = pthread_mutex_unlock(_mutex);
+  assert_status(status == 0, status, "invariant");
+  // Paranoia to ensure our locked and lock-free paths interact
+  // correctly with each other and Java-level accesses.
+  OrderAccess::fence();
+
+  // If externally suspended while waiting, re-suspend
+  if (jt->handle_special_suspend_equivalent_condition()) {
+    jt->java_suspend_self();
+  }
+}
+
+void Parker::unpark() {
+  int status = pthread_mutex_lock(_mutex);
+  assert_status(status == 0, status, "invariant");
+  const int s = _counter;
+  _counter = 1;
+  // must capture correct index before unlocking
+  int index = _cur_index;
+  status = pthread_mutex_unlock(_mutex);
+  assert_status(status == 0, status, "invariant");
+
+  // Note that we signal() *after* dropping the lock for "immortal" Events.
+  // This is safe and avoids a common class of futile wakeups.  In rare
+  // circumstances this can cause a thread to return prematurely from
+  // cond_{timed}wait() but the spurious wakeup is benign and the victim
+  // will simply re-test the condition and re-park itself.
+  // This provides particular benefit if the underlying platform does not
+  // provide wait morphing.
+
+  if (s < 1 && index != -1) {
+    // thread is definitely parked
+    status = pthread_cond_signal(&_cond[index]);
+    assert_status(status == 0, status, "invariant");
+  }
+}
+
+
+#endif // !SOLARIS
< prev index next >