mirror of
https://github.com/openjdk/jdk.git
synced 2026-03-23 22:29:55 +00:00
8020701: Avoid crashes in WatcherThread
Reviewed-by: acorn, dcubed, dsimms
This commit is contained in:
parent
71ca890b66
commit
2d77390705
@ -259,3 +259,52 @@ const char* os::get_current_directory(char *buf, size_t buflen) {
|
||||
FILE* os::open(int fd, const char* mode) {
|
||||
return ::fdopen(fd, mode);
|
||||
}
|
||||
|
||||
os::WatcherThreadCrashProtection::WatcherThreadCrashProtection() {
|
||||
assert(Thread::current()->is_Watcher_thread(), "Must be WatcherThread");
|
||||
}
|
||||
|
||||
/*
|
||||
* See the caveats for this class in os_posix.hpp
|
||||
* Protects the callback call so that SIGSEGV / SIGBUS jumps back into this
|
||||
* method and returns false. If none of the signals are raised, returns true.
|
||||
* The callback is supposed to provide the method that should be protected.
|
||||
*/
|
||||
bool os::WatcherThreadCrashProtection::call(os::CrashProtectionCallback& cb) {
|
||||
assert(Thread::current()->is_Watcher_thread(), "Only for WatcherThread");
|
||||
assert(!WatcherThread::watcher_thread()->has_crash_protection(),
|
||||
"crash_protection already set?");
|
||||
|
||||
if (sigsetjmp(_jmpbuf, 1) == 0) {
|
||||
// make sure we can see in the signal handler that we have crash protection
|
||||
// installed
|
||||
WatcherThread::watcher_thread()->set_crash_protection(this);
|
||||
cb.call();
|
||||
// and clear the crash protection
|
||||
WatcherThread::watcher_thread()->set_crash_protection(NULL);
|
||||
return true;
|
||||
}
|
||||
// this happens when we siglongjmp() back
|
||||
WatcherThread::watcher_thread()->set_crash_protection(NULL);
|
||||
return false;
|
||||
}
|
||||
|
||||
void os::WatcherThreadCrashProtection::restore() {
|
||||
assert(WatcherThread::watcher_thread()->has_crash_protection(),
|
||||
"must have crash protection");
|
||||
|
||||
siglongjmp(_jmpbuf, 1);
|
||||
}
|
||||
|
||||
void os::WatcherThreadCrashProtection::check_crash_protection(int sig,
|
||||
Thread* thread) {
|
||||
|
||||
if (thread != NULL &&
|
||||
thread->is_Watcher_thread() &&
|
||||
WatcherThread::watcher_thread()->has_crash_protection()) {
|
||||
|
||||
if (sig == SIGSEGV || sig == SIGBUS) {
|
||||
WatcherThread::watcher_thread()->crash_protection()->restore();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -37,5 +37,24 @@ protected:
|
||||
|
||||
};
|
||||
|
||||
/*
|
||||
* Crash protection for the watcher thread. Wrap the callback
|
||||
* with a sigsetjmp and in case of a SIGSEGV/SIGBUS we siglongjmp
|
||||
* back.
|
||||
* To be able to use this - don't take locks, don't rely on destructors,
|
||||
* don't make OS library calls, don't allocate memory, don't print,
|
||||
* don't call code that could leave the heap / memory in an inconsistent state,
|
||||
* or anything else where we are not in control if we suddenly jump out.
|
||||
*/
|
||||
class WatcherThreadCrashProtection : public StackObj {
|
||||
public:
|
||||
WatcherThreadCrashProtection();
|
||||
bool call(os::CrashProtectionCallback& cb);
|
||||
|
||||
static void check_crash_protection(int signal, Thread* thread);
|
||||
private:
|
||||
void restore();
|
||||
sigjmp_buf _jmpbuf;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@ -4684,6 +4684,34 @@ void os::pause() {
|
||||
}
|
||||
}
|
||||
|
||||
os::WatcherThreadCrashProtection::WatcherThreadCrashProtection() {
|
||||
assert(Thread::current()->is_Watcher_thread(), "Must be WatcherThread");
|
||||
}
|
||||
|
||||
/*
|
||||
* See the caveats for this class in os_windows.hpp
|
||||
* Protects the callback call so that raised OS EXCEPTIONS causes a jump back
|
||||
* into this method and returns false. If no OS EXCEPTION was raised, returns
|
||||
* true.
|
||||
* The callback is supposed to provide the method that should be protected.
|
||||
*/
|
||||
bool os::WatcherThreadCrashProtection::call(os::CrashProtectionCallback& cb) {
|
||||
assert(Thread::current()->is_Watcher_thread(), "Only for WatcherThread");
|
||||
assert(!WatcherThread::watcher_thread()->has_crash_protection(),
|
||||
"crash_protection already set?");
|
||||
|
||||
bool success = true;
|
||||
__try {
|
||||
WatcherThread::watcher_thread()->set_crash_protection(this);
|
||||
cb.call();
|
||||
} __except(EXCEPTION_EXECUTE_HANDLER) {
|
||||
// only for protection, nothing to do
|
||||
success = false;
|
||||
}
|
||||
WatcherThread::watcher_thread()->set_crash_protection(NULL);
|
||||
return success;
|
||||
}
|
||||
|
||||
// An Event wraps a win32 "CreateEvent" kernel handle.
|
||||
//
|
||||
// We have a number of choices regarding "CreateEvent" win32 handle leakage:
|
||||
|
||||
@ -102,6 +102,20 @@ class win32 {
|
||||
static LONG WINAPI serialize_fault_filter(struct _EXCEPTION_POINTERS* e);
|
||||
};
|
||||
|
||||
/*
|
||||
* Crash protection for the watcher thread. Wrap the callback
|
||||
* with a __try { call() }
|
||||
* To be able to use this - don't take locks, don't rely on destructors,
|
||||
* don't make OS library calls, don't allocate memory, don't print,
|
||||
* don't call code that could leave the heap / memory in an inconsistent state,
|
||||
* or anything else where we are not in control if we suddenly jump out.
|
||||
*/
|
||||
class WatcherThreadCrashProtection : public StackObj {
|
||||
public:
|
||||
WatcherThreadCrashProtection();
|
||||
bool call(os::CrashProtectionCallback& cb);
|
||||
};
|
||||
|
||||
class PlatformEvent : public CHeapObj<mtInternal> {
|
||||
private:
|
||||
double CachePad [4] ; // increase odds that _Event is sole occupant of cache line
|
||||
|
||||
@ -401,6 +401,10 @@ JVM_handle_bsd_signal(int sig,
|
||||
|
||||
Thread* t = ThreadLocalStorage::get_thread_slow();
|
||||
|
||||
// Must do this before SignalHandlerMark, if crash protection installed we will longjmp away
|
||||
// (no destructors can be run)
|
||||
os::WatcherThreadCrashProtection::check_crash_protection(sig, t);
|
||||
|
||||
SignalHandlerMark shm(t);
|
||||
|
||||
// Note: it's not uncommon that JNI code uses signal/sigset to install
|
||||
|
||||
@ -553,6 +553,10 @@ JVM_handle_linux_signal(int sig,
|
||||
|
||||
Thread* t = ThreadLocalStorage::get_thread_slow();
|
||||
|
||||
// Must do this before SignalHandlerMark, if crash protection installed we will longjmp away
|
||||
// (no destructors can be run)
|
||||
os::WatcherThreadCrashProtection::check_crash_protection(sig, t);
|
||||
|
||||
SignalHandlerMark shm(t);
|
||||
|
||||
// Note: it's not uncommon that JNI code uses signal/sigset to install
|
||||
|
||||
@ -225,6 +225,10 @@ JVM_handle_linux_signal(int sig,
|
||||
|
||||
Thread* t = ThreadLocalStorage::get_thread_slow();
|
||||
|
||||
// Must do this before SignalHandlerMark, if crash protection installed we will longjmp away
|
||||
// (no destructors can be run)
|
||||
os::WatcherThreadCrashProtection::check_crash_protection(sig, t);
|
||||
|
||||
SignalHandlerMark shm(t);
|
||||
|
||||
// Note: it's not uncommon that JNI code uses signal/sigset to install
|
||||
|
||||
@ -315,6 +315,10 @@ JVM_handle_solaris_signal(int sig, siginfo_t* info, void* ucVoid,
|
||||
|
||||
Thread* t = ThreadLocalStorage::get_thread_slow();
|
||||
|
||||
// Must do this before SignalHandlerMark, if crash protection installed we will longjmp away
|
||||
// (no destructors can be run)
|
||||
os::WatcherThreadCrashProtection::check_crash_protection(sig, t);
|
||||
|
||||
SignalHandlerMark shm(t);
|
||||
|
||||
if(sig == SIGPIPE || sig == SIGXFSZ) {
|
||||
|
||||
@ -374,6 +374,10 @@ JVM_handle_solaris_signal(int sig, siginfo_t* info, void* ucVoid,
|
||||
|
||||
Thread* t = ThreadLocalStorage::get_thread_slow(); // slow & steady
|
||||
|
||||
// Must do this before SignalHandlerMark, if crash protection installed we will longjmp away
|
||||
// (no destructors can be run)
|
||||
os::WatcherThreadCrashProtection::check_crash_protection(sig, t);
|
||||
|
||||
SignalHandlerMark shm(t);
|
||||
|
||||
if(sig == SIGPIPE || sig == SIGXFSZ) {
|
||||
|
||||
@ -1370,6 +1370,10 @@ void Monitor::check_prelock_state(Thread *thread) {
|
||||
debug_only(if (rank() != Mutex::special) \
|
||||
thread->check_for_valid_safepoint_state(false);)
|
||||
}
|
||||
if (thread->is_Watcher_thread()) {
|
||||
assert(!WatcherThread::watcher_thread()->has_crash_protection(),
|
||||
"locking not allowed when crash protection is set");
|
||||
}
|
||||
}
|
||||
|
||||
void Monitor::check_block_state(Thread *thread) {
|
||||
|
||||
@ -595,6 +595,22 @@ void* os::malloc(size_t size, MEMFLAGS memflags, address caller) {
|
||||
NOT_PRODUCT(inc_stat_counter(&num_mallocs, 1));
|
||||
NOT_PRODUCT(inc_stat_counter(&alloc_bytes, size));
|
||||
|
||||
#ifdef ASSERT
|
||||
// checking for the WatcherThread and crash_protection first
|
||||
// since os::malloc can be called when the libjvm.{dll,so} is
|
||||
// first loaded and we don't have a thread yet.
|
||||
// try to find the thread after we see that the watcher thread
|
||||
// exists and has crash protection.
|
||||
WatcherThread *wt = WatcherThread::watcher_thread();
|
||||
if (wt != NULL && wt->has_crash_protection()) {
|
||||
Thread* thread = ThreadLocalStorage::get_thread_slow();
|
||||
if (thread == wt) {
|
||||
assert(!wt->has_crash_protection(),
|
||||
"Can't malloc with crash protection from WatcherThread");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (size == 0) {
|
||||
// return a valid pointer if size is zero
|
||||
// if NULL is returned the calling functions assume out of memory.
|
||||
|
||||
@ -32,15 +32,18 @@
|
||||
#include "utilities/top.hpp"
|
||||
#ifdef TARGET_OS_FAMILY_linux
|
||||
# include "jvm_linux.h"
|
||||
# include <setjmp.h>
|
||||
#endif
|
||||
#ifdef TARGET_OS_FAMILY_solaris
|
||||
# include "jvm_solaris.h"
|
||||
# include <setjmp.h>
|
||||
#endif
|
||||
#ifdef TARGET_OS_FAMILY_windows
|
||||
# include "jvm_windows.h"
|
||||
#endif
|
||||
#ifdef TARGET_OS_FAMILY_bsd
|
||||
# include "jvm_bsd.h"
|
||||
# include <setjmp.h>
|
||||
#endif
|
||||
|
||||
// os defines the interface to operating system; this includes traditional
|
||||
@ -730,6 +733,10 @@ class os: AllStatic {
|
||||
#include "runtime/os_ext.hpp"
|
||||
|
||||
public:
|
||||
class CrashProtectionCallback : public StackObj {
|
||||
public:
|
||||
virtual void call() = 0;
|
||||
};
|
||||
|
||||
// Platform dependent stuff
|
||||
#ifdef TARGET_OS_FAMILY_linux
|
||||
@ -908,6 +915,7 @@ class os: AllStatic {
|
||||
char pathSep);
|
||||
static bool set_boot_path(char fileSep, char pathSep);
|
||||
static char** split_path(const char* path, int* n);
|
||||
|
||||
};
|
||||
|
||||
// Note that "PAUSE" is almost always used with synchronization
|
||||
|
||||
@ -1226,7 +1226,7 @@ WatcherThread* WatcherThread::_watcher_thread = NULL;
|
||||
bool WatcherThread::_startable = false;
|
||||
volatile bool WatcherThread::_should_terminate = false;
|
||||
|
||||
WatcherThread::WatcherThread() : Thread() {
|
||||
WatcherThread::WatcherThread() : Thread(), _crash_protection(NULL) {
|
||||
assert(watcher_thread() == NULL, "we can only allocate one WatcherThread");
|
||||
if (os::create_thread(this, os::watcher_thread)) {
|
||||
_watcher_thread = this;
|
||||
|
||||
@ -733,6 +733,8 @@ class WatcherThread: public Thread {
|
||||
|
||||
static bool _startable;
|
||||
volatile static bool _should_terminate; // updated without holding lock
|
||||
|
||||
os::WatcherThreadCrashProtection* _crash_protection;
|
||||
public:
|
||||
enum SomeConstants {
|
||||
delay_interval = 10 // interrupt delay in milliseconds
|
||||
@ -760,6 +762,14 @@ class WatcherThread: public Thread {
|
||||
// Otherwise the first task to enroll will trigger the start
|
||||
static void make_startable();
|
||||
|
||||
void set_crash_protection(os::WatcherThreadCrashProtection* crash_protection) {
|
||||
assert(Thread::current()->is_Watcher_thread(), "Can only be set by WatcherThread");
|
||||
_crash_protection = crash_protection;
|
||||
}
|
||||
|
||||
bool has_crash_protection() const { return _crash_protection != NULL; }
|
||||
os::WatcherThreadCrashProtection* crash_protection() const { return _crash_protection; }
|
||||
|
||||
private:
|
||||
int sleep() const;
|
||||
};
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user