8072439: fix for 8047720 may need more work

Cleanup PeriodTask_lock usage. Also reviewed by varming@gmail.com.

Co-authored-by: Carsten Varming <varming@gmail.com>
Reviewed-by: dholmes, dcubed, mgronlun, coleenp
This commit is contained in:
Daniel D. Daugherty 2015-03-02 16:31:25 -08:00
parent 6465239986
commit 12cd46edc9
5 changed files with 59 additions and 40 deletions

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -421,9 +421,11 @@ void before_exit(JavaThread * thread) {
os::infinite_sleep(); os::infinite_sleep();
} }
// Terminate watcher thread - must before disenrolling any periodic task // Stop the WatcherThread. We do this before disenrolling various
if (PeriodicTask::num_tasks() > 0) // PeriodicTasks to reduce the likelihood of races.
if (PeriodicTask::num_tasks() > 0) {
WatcherThread::stop(); WatcherThread::stop();
}
// Print statistics gathered (profiling ...) // Print statistics gathered (profiling ...)
if (Arguments::has_profile()) { if (Arguments::has_profile()) {

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -47,6 +47,8 @@ void PeriodicTask::print_intervals() {
#endif #endif
void PeriodicTask::real_time_tick(int delay_time) { void PeriodicTask::real_time_tick(int delay_time) {
assert(Thread::current()->is_Watcher_thread(), "must be WatcherThread");
#ifndef PRODUCT #ifndef PRODUCT
if (ProfilerCheckIntervals) { if (ProfilerCheckIntervals) {
_ticks++; _ticks++;
@ -60,6 +62,8 @@ void PeriodicTask::real_time_tick(int delay_time) {
#endif #endif
{ {
// The WatcherThread does not participate in the safepoint protocol
// for the PeriodicTask_lock because it is not a JavaThread.
MutexLockerEx ml(PeriodicTask_lock, Mutex::_no_safepoint_check_flag); MutexLockerEx ml(PeriodicTask_lock, Mutex::_no_safepoint_check_flag);
int orig_num_tasks = _num_tasks; int orig_num_tasks = _num_tasks;
@ -74,8 +78,7 @@ void PeriodicTask::real_time_tick(int delay_time) {
} }
int PeriodicTask::time_to_wait() { int PeriodicTask::time_to_wait() {
MutexLockerEx ml(PeriodicTask_lock->owned_by_self() ? assert(PeriodicTask_lock->owned_by_self(), "PeriodicTask_lock required");
NULL : PeriodicTask_lock, Mutex::_no_safepoint_check_flag);
if (_num_tasks == 0) { if (_num_tasks == 0) {
return 0; // sleep until shutdown or a task is enrolled return 0; // sleep until shutdown or a task is enrolled
@ -98,14 +101,19 @@ PeriodicTask::PeriodicTask(size_t interval_time) :
} }
PeriodicTask::~PeriodicTask() { PeriodicTask::~PeriodicTask() {
// This PeriodicTask may have already been disenrolled by a call
// to disenroll() before the PeriodicTask was deleted.
disenroll(); disenroll();
} }
/* enroll could be called from a JavaThread, so we have to check for // enroll the current PeriodicTask
* safepoint when taking the lock to avoid deadlocking */
void PeriodicTask::enroll() { void PeriodicTask::enroll() {
MutexLockerEx ml(PeriodicTask_lock->owned_by_self() ? // Follow normal safepoint aware lock enter protocol if the caller does
NULL : PeriodicTask_lock); // not already own the PeriodicTask_lock. Otherwise, we don't try to
// enter it again because VM internal Mutexes do not support recursion.
//
MutexLockerEx ml(PeriodicTask_lock->owned_by_self() ? NULL
: PeriodicTask_lock);
if (_num_tasks == PeriodicTask::max_tasks) { if (_num_tasks == PeriodicTask::max_tasks) {
fatal("Overflow in PeriodicTask table"); fatal("Overflow in PeriodicTask table");
@ -113,18 +121,21 @@ void PeriodicTask::enroll() {
_tasks[_num_tasks++] = this; _tasks[_num_tasks++] = this;
WatcherThread* thread = WatcherThread::watcher_thread(); WatcherThread* thread = WatcherThread::watcher_thread();
if (thread) { if (thread != NULL) {
thread->unpark(); thread->unpark();
} else { } else {
WatcherThread::start(); WatcherThread::start();
} }
} }
/* disenroll could be called from a JavaThread, so we have to check for // disenroll the current PeriodicTask
* safepoint when taking the lock to avoid deadlocking */
void PeriodicTask::disenroll() { void PeriodicTask::disenroll() {
MutexLockerEx ml(PeriodicTask_lock->owned_by_self() ? // Follow normal safepoint aware lock enter protocol if the caller does
NULL : PeriodicTask_lock); // not already own the PeriodicTask_lock. Otherwise, we don't try to
// enter it again because VM internal Mutexes do not support recursion.
//
MutexLockerEx ml(PeriodicTask_lock->owned_by_self() ? NULL
: PeriodicTask_lock);
int index; int index;
for(index = 0; index < _num_tasks && _tasks[index] != this; index++) for(index = 0; index < _num_tasks && _tasks[index] != this; index++)

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -54,6 +54,7 @@ class PeriodicTask: public CHeapObj<mtInternal> {
static int _num_tasks; static int _num_tasks;
static PeriodicTask* _tasks[PeriodicTask::max_tasks]; static PeriodicTask* _tasks[PeriodicTask::max_tasks];
// Can only be called by the WatcherThread
static void real_time_tick(int delay_time); static void real_time_tick(int delay_time);
#ifndef PRODUCT #ifndef PRODUCT
@ -98,6 +99,7 @@ class PeriodicTask: public CHeapObj<mtInternal> {
// Calculate when the next periodic task will fire. // Calculate when the next periodic task will fire.
// Called by the WatcherThread's run method. // Called by the WatcherThread's run method.
// Requires the PeriodicTask_lock.
static int time_to_wait(); static int time_to_wait();
// The task to perform at each period // The task to perform at each period

View file

@ -1197,8 +1197,15 @@ WatcherThread::WatcherThread() : Thread(), _crash_protection(NULL) {
} }
int WatcherThread::sleep() const { int WatcherThread::sleep() const {
// The WatcherThread does not participate in the safepoint protocol
// for the PeriodicTask_lock because it is not a JavaThread.
MutexLockerEx ml(PeriodicTask_lock, Mutex::_no_safepoint_check_flag); MutexLockerEx ml(PeriodicTask_lock, Mutex::_no_safepoint_check_flag);
if (_should_terminate) {
// check for termination before we do any housekeeping or wait
return 0; // we did not sleep.
}
// remaining will be zero if there are no tasks, // remaining will be zero if there are no tasks,
// causing the WatcherThread to sleep until a task is // causing the WatcherThread to sleep until a task is
// enrolled // enrolled
@ -1211,8 +1218,9 @@ int WatcherThread::sleep() const {
jlong time_before_loop = os::javaTimeNanos(); jlong time_before_loop = os::javaTimeNanos();
for (;;) { while (true) {
bool timedout = PeriodicTask_lock->wait(Mutex::_no_safepoint_check_flag, remaining); bool timedout = PeriodicTask_lock->wait(Mutex::_no_safepoint_check_flag,
remaining);
jlong now = os::javaTimeNanos(); jlong now = os::javaTimeNanos();
if (remaining == 0) { if (remaining == 0) {
@ -1253,7 +1261,7 @@ void WatcherThread::run() {
this->initialize_thread_local_storage(); this->initialize_thread_local_storage();
this->set_native_thread_name(this->name()); this->set_native_thread_name(this->name());
this->set_active_handles(JNIHandleBlock::allocate_block()); this->set_active_handles(JNIHandleBlock::allocate_block());
while (!_should_terminate) { while (true) {
assert(watcher_thread() == Thread::current(), "thread consistency check"); assert(watcher_thread() == Thread::current(), "thread consistency check");
assert(watcher_thread() == this, "thread consistency check"); assert(watcher_thread() == this, "thread consistency check");
@ -1289,6 +1297,11 @@ void WatcherThread::run() {
} }
} }
if (_should_terminate) {
// check for termination before posting the next tick
break;
}
PeriodicTask::real_time_tick(time_waited); PeriodicTask::real_time_tick(time_waited);
} }
@ -1319,27 +1332,19 @@ void WatcherThread::make_startable() {
} }
void WatcherThread::stop() { void WatcherThread::stop() {
// Get the PeriodicTask_lock if we can. If we cannot, then the {
// WatcherThread is using it and we don't want to block on that lock // Follow normal safepoint aware lock enter protocol since the
// here because that might cause a safepoint deadlock depending on // WatcherThread is stopped by another JavaThread.
// what the current WatcherThread tasks are doing. MutexLocker ml(PeriodicTask_lock);
bool have_lock = PeriodicTask_lock->try_lock(); _should_terminate = true;
_should_terminate = true;
OrderAccess::fence(); // ensure WatcherThread sees update in main loop
if (have_lock) {
WatcherThread* watcher = watcher_thread(); WatcherThread* watcher = watcher_thread();
if (watcher != NULL) { if (watcher != NULL) {
// If we managed to get the lock, then we should unpark the // unpark the WatcherThread so it can see that it should terminate
// WatcherThread so that it can see we want it to stop.
watcher->unpark(); watcher->unpark();
} }
PeriodicTask_lock->unlock();
} }
// it is ok to take late safepoints here, if needed
MutexLocker mu(Terminator_lock); MutexLocker mu(Terminator_lock);
while (watcher_thread() != NULL) { while (watcher_thread() != NULL) {
@ -1359,9 +1364,7 @@ void WatcherThread::stop() {
} }
void WatcherThread::unpark() { void WatcherThread::unpark() {
MutexLockerEx ml(PeriodicTask_lock->owned_by_self() assert(PeriodicTask_lock->owned_by_self(), "PeriodicTask_lock required");
? NULL
: PeriodicTask_lock, Mutex::_no_safepoint_check_flag);
PeriodicTask_lock->notify(); PeriodicTask_lock->notify();
} }
@ -3558,8 +3561,8 @@ jint Threads::create_vm(JavaVMInitArgs* args, bool* canTryAgain) {
} }
{ {
MutexLockerEx ml(PeriodicTask_lock, Mutex::_no_safepoint_check_flag); MutexLocker ml(PeriodicTask_lock);
// Make sure the watcher thread can be started by WatcherThread::start() // Make sure the WatcherThread can be started by WatcherThread::start()
// or by dynamic enrollment. // or by dynamic enrollment.
WatcherThread::make_startable(); WatcherThread::make_startable();
// Start up the WatcherThread if there are any periodic tasks // Start up the WatcherThread if there are any periodic tasks

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -701,7 +701,8 @@ class WatcherThread: public Thread {
static WatcherThread* _watcher_thread; static WatcherThread* _watcher_thread;
static bool _startable; static bool _startable;
volatile static bool _should_terminate; // updated without holding lock // volatile due to at least one lock-free read
volatile static bool _should_terminate;
os::WatcherThreadCrashProtection* _crash_protection; os::WatcherThreadCrashProtection* _crash_protection;
public: public: