< prev index next >
src/os/bsd/vm/os_bsd.cpp
Print this page
*** 1,7 ****
/*
! * Copyright (c) 1999, 2015, Oracle and/or its affiliates. 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.
--- 1,7 ----
/*
! * Copyright (c) 1999, 2016, Oracle and/or its affiliates. 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.
*** 4040,4104 ****
// -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
//
- // Beware -- Some versions of NPTL embody a flaw where pthread_cond_timedwait() can
- // hang indefinitely. For instance NPTL 0.60 on 2.4.21-4ELsmp is vulnerable.
- // For specifics regarding the bug see GLIBC BUGID 261237 :
- // http://www.mail-archive.com/debian-glibc@lists.debian.org/msg10837.html.
- // Briefly, pthread_cond_timedwait() calls with an expiry time that's not in the future
- // will either hang or corrupt the condvar, resulting in subsequent hangs if the condvar
- // is used. (The simple C test-case provided in the GLIBC bug report manifests the
- // hang). The JVM is vulernable via sleep(), Object.wait(timo), LockSupport.parkNanos()
- // and monitorenter when we're using 1-0 locking. All those operations may result in
- // calls to pthread_cond_timedwait(). Using LD_ASSUME_KERNEL to use an older version
- // of libpthread avoids the problem, but isn't practical.
- //
- // Possible remedies:
- //
- // 1. Establish a minimum relative wait time. 50 to 100 msecs seems to work.
- // This is palliative and probabilistic, however. If the thread is preempted
- // between the call to compute_abstime() and pthread_cond_timedwait(), more
- // than the minimum period may have passed, and the abstime may be stale (in the
- // past) resultin in a hang. Using this technique reduces the odds of a hang
- // but the JVM is still vulnerable, particularly on heavily loaded systems.
- //
- // 2. Modify park-unpark to use per-thread (per ParkEvent) pipe-pairs instead
- // of the usual flag-condvar-mutex idiom. The write side of the pipe is set
- // NDELAY. unpark() reduces to write(), park() reduces to read() and park(timo)
- // reduces to poll()+read(). This works well, but consumes 2 FDs per extant
- // thread.
- //
- // 3. Embargo pthread_cond_timedwait() and implement a native "chron" thread
- // that manages timeouts. We'd emulate pthread_cond_timedwait() by enqueuing
- // a timeout request to the chron thread and then blocking via pthread_cond_wait().
- // This also works well. In fact it avoids kernel-level scalability impediments
- // on certain platforms that don't handle lots of active pthread_cond_timedwait()
- // timers in a graceful fashion.
- //
- // 4. When the abstime value is in the past it appears that control returns
- // correctly from pthread_cond_timedwait(), but the condvar is left corrupt.
- // Subsequent timedwait/wait calls may hang indefinitely. Given that, we
- // can avoid the problem by reinitializing the condvar -- by cond_destroy()
- // followed by cond_init() -- after all calls to pthread_cond_timedwait().
- // It may be possible to avoid reinitialization by checking the return
- // value from pthread_cond_timedwait(). In addition to reinitializing the
- // condvar we must establish the invariant that cond_signal() is only called
- // within critical sections protected by the adjunct mutex. This prevents
- // cond_signal() from "seeing" a condvar that's in the midst of being
- // reinitialized or that is corrupt. Sadly, this invariant obviates the
- // desirable signal-after-unlock optimization that avoids futile context switching.
- //
- // I'm also concerned that some versions of NTPL might allocate an auxilliary
- // structure when a condvar is used or initialized. cond_destroy() would
- // release the helper structure. Our reinitialize-after-timedwait fix
- // put excessive stress on malloc/free and locks protecting the c-heap.
- //
- // We currently use (4). See the WorkAroundNTPLTimedWaitHang flag.
- // It may be possible to refine (4) by checking the kernel and NTPL verisons
- // and only enabling the work-around for vulnerable environments.
// utility to compute the abstime argument to timedwait:
// millis is the relative timeout time
// abstime will be the absolute timeout time
// TODO: replace compute_abstime() with unpackTime()
--- 4040,4049 ----
*** 4106,4116 ****
static struct timespec* compute_abstime(struct timespec* abstime,
jlong millis) {
if (millis < 0) millis = 0;
struct timeval now;
int status = gettimeofday(&now, NULL);
! assert(status == 0, "gettimeofday");
jlong seconds = millis / 1000;
millis %= 1000;
if (seconds > 50000000) { // see man cond_timedwait(3T)
seconds = 50000000;
}
--- 4051,4061 ----
static struct timespec* compute_abstime(struct timespec* abstime,
jlong millis) {
if (millis < 0) millis = 0;
struct timeval now;
int status = gettimeofday(&now, NULL);
! assert_status(status == 0, status, "gettimeofday");
jlong seconds = millis / 1000;
millis %= 1000;
if (seconds > 50000000) { // see man cond_timedwait(3T)
seconds = 50000000;
}
*** 4206,4219 ****
// TODO: properly differentiate simultaneous notify+interrupt.
// In that case, we should propagate the notify to another waiter.
while (_Event < 0) {
status = pthread_cond_timedwait(_cond, _mutex, &abst);
- if (status != 0 && WorkAroundNPTLTimedWaitHang) {
- pthread_cond_destroy(_cond);
- pthread_cond_init(_cond, NULL);
- }
assert_status(status == 0 || status == EINTR ||
status == ETIMEDOUT,
status, "cond_timedwait");
if (!FilterSpuriousWakeups) break; // previous semantics
if (status == ETIMEDOUT) break;
--- 4151,4160 ----
*** 4253,4266 ****
// Wait for the thread associated with the event to vacate
int status = pthread_mutex_lock(_mutex);
assert_status(status == 0, status, "mutex_lock");
int AnyWaiters = _nParked;
assert(AnyWaiters == 0 || AnyWaiters == 1, "invariant");
- if (AnyWaiters != 0 && WorkAroundNPTLTimedWaitHang) {
- AnyWaiters = 0;
- pthread_cond_signal(_cond);
- }
status = pthread_mutex_unlock(_mutex);
assert_status(status == 0, status, "mutex_unlock");
if (AnyWaiters != 0) {
// Note that we signal() *after* dropping the lock for "immortal" Events.
// This is safe and avoids a common class of futile wakeups. In rare
--- 4194,4203 ----
*** 4309,4319 ****
static void unpackTime(struct timespec* absTime, bool isAbsolute, jlong time) {
assert(time > 0, "convertTime");
struct timeval now;
int status = gettimeofday(&now, NULL);
! assert(status == 0, "gettimeofday");
time_t max_secs = now.tv_sec + MAX_SECS;
if (isAbsolute) {
jlong secs = time / 1000;
--- 4246,4256 ----
static void unpackTime(struct timespec* absTime, bool isAbsolute, jlong time) {
assert(time > 0, "convertTime");
struct timeval now;
int status = gettimeofday(&now, NULL);
! assert_status(status == 0, status, "gettimeofday");
time_t max_secs = now.tv_sec + MAX_SECS;
if (isAbsolute) {
jlong secs = time / 1000;
*** 4389,4399 ****
int status;
if (_counter > 0) { // no wait needed
_counter = 0;
status = pthread_mutex_unlock(_mutex);
! assert(status == 0, "invariant");
// Paranoia to ensure our locked and lock-free paths interact
// correctly with each other and Java-level accesses.
OrderAccess::fence();
return;
}
--- 4326,4336 ----
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;
}
*** 4412,4425 ****
if (time == 0) {
status = pthread_cond_wait(_cond, _mutex);
} else {
status = pthread_cond_timedwait(_cond, _mutex, &absTime);
- if (status != 0 && WorkAroundNPTLTimedWaitHang) {
- pthread_cond_destroy(_cond);
- pthread_cond_init(_cond, NULL);
- }
}
assert_status(status == 0 || status == EINTR ||
status == ETIMEDOUT,
status, "cond_timedwait");
--- 4349,4358 ----
*** 4440,4467 ****
}
}
void Parker::unpark() {
int status = pthread_mutex_lock(_mutex);
! assert(status == 0, "invariant");
const int s = _counter;
_counter = 1;
if (s < 1) {
- if (WorkAroundNPTLTimedWaitHang) {
- status = pthread_cond_signal(_cond);
- assert(status == 0, "invariant");
- status = pthread_mutex_unlock(_mutex);
- assert(status == 0, "invariant");
- } else {
status = pthread_mutex_unlock(_mutex);
! assert(status == 0, "invariant");
status = pthread_cond_signal(_cond);
! assert(status == 0, "invariant");
! }
} else {
pthread_mutex_unlock(_mutex);
! assert(status == 0, "invariant");
}
}
// Darwin has no "environ" in a dynamic library.
--- 4373,4393 ----
}
}
void Parker::unpark() {
int status = pthread_mutex_lock(_mutex);
! assert_status(status == 0, status, "invariant");
const int s = _counter;
_counter = 1;
if (s < 1) {
status = pthread_mutex_unlock(_mutex);
! assert_status(status == 0, status, "invariant");
status = pthread_cond_signal(_cond);
! assert_status(status == 0, status, "invariant");
} else {
pthread_mutex_unlock(_mutex);
! assert_status(status == 0, status, "invariant");
}
}
// Darwin has no "environ" in a dynamic library.
< prev index next >