src/os/solaris/vm/os_solaris.cpp
Print this page
rev 4773 : 8005849: JEP 167: Event-Based JVM Tracing
Reviewed-by: acorn, coleenp, sla
Contributed-by: Karen Kinnear <karen.kinnear@oracle.com>, Bengt Rutisson <bengt.rutisson@oracle.com>, Calvin Cheung <calvin.cheung@oracle.com>, Erik Gahlin <erik.gahlin@oracle.com>, Erik Helin <erik.helin@oracle.com>, Jesper Wilhelmsson <jesper.wilhelmsson@oracle.com>, Keith McGuigan <keith.mcguigan@oracle.com>, Mattias Tobiasson <mattias.tobiasson@oracle.com>, Markus Gronlund <markus.gronlund@oracle.com>, Mikael Auno <mikael.auno@oracle.com>, Nils Eliasson <nils.eliasson@oracle.com>, Nils Loodin <nils.loodin@oracle.com>, Rickard Backman <rickard.backman@oracle.com>, Staffan Larsen <staffan.larsen@oracle.com>, Stefan Karlsson <stefan.karlsson@oracle.com>, Yekaterina Kantserova <yekaterina.kantserova@oracle.com>
*** 238,247 ****
--- 238,249 ----
extern "C" {
static int pthread_mutex_default_init(mutex_t *mx, int scope, void *arg) { memset(mx, 0, sizeof(mutex_t)); return 0; }
static int pthread_cond_default_init(cond_t *cv, int scope, void *arg){ memset(cv, 0, sizeof(cond_t)); return 0; }
}
+ static void unpackTime(timespec* absTime, bool isAbsolute, jlong time);
+
// Thread Local Storage
// This is common to all Solaris platforms so it is defined here,
// in this common file.
// The declarations are in the os_cpu threadLS*.hpp files.
//
*** 2578,2587 ****
--- 2580,2640 ----
void* os::user_handler() {
return CAST_FROM_FN_PTR(void*, UserHandler);
}
+ class Semaphore : public StackObj {
+ public:
+ Semaphore();
+ ~Semaphore();
+ void signal();
+ void wait();
+ bool trywait();
+ bool timedwait(unsigned int sec, int nsec);
+ private:
+ sema_t _semaphore;
+ };
+
+
+ Semaphore::Semaphore() {
+ sema_init(&_semaphore, 0, NULL, NULL);
+ }
+
+ Semaphore::~Semaphore() {
+ sema_destroy(&_semaphore);
+ }
+
+ void Semaphore::signal() {
+ sema_post(&_semaphore);
+ }
+
+ void Semaphore::wait() {
+ sema_wait(&_semaphore);
+ }
+
+ bool Semaphore::trywait() {
+ return sema_trywait(&_semaphore) == 0;
+ }
+
+ bool Semaphore::timedwait(unsigned int sec, int nsec) {
+ struct timespec ts;
+ unpackTime(&ts, false, (sec * NANOSECS_PER_SEC) + nsec);
+
+ while (1) {
+ int result = sema_timedwait(&_semaphore, &ts);
+ if (result == 0) {
+ return true;
+ } else if (errno == EINTR) {
+ continue;
+ } else if (errno == ETIME) {
+ return false;
+ } else {
+ return false;
+ }
+ }
+ }
+
extern "C" {
typedef void (*sa_handler_t)(int);
typedef void (*sa_sigaction_t)(int, siginfo_t *, void *);
}
*** 4162,4171 ****
--- 4215,4286 ----
// Void return because it's a hint and can fail.
void os::hint_no_preempt() {
schedctl_start(schedctl_init());
}
+ static void resume_clear_context(OSThread *osthread) {
+ osthread->set_ucontext(NULL);
+ }
+
+ static void suspend_save_context(OSThread *osthread, ucontext_t* context) {
+ osthread->set_ucontext(context);
+ }
+
+ static Semaphore sr_semaphore;
+
+ void os::Solaris::SR_handler(Thread* thread, ucontext_t* uc) {
+ // Save and restore errno to avoid confusing native code with EINTR
+ // after sigsuspend.
+ int old_errno = errno;
+
+ OSThread* osthread = thread->osthread();
+ assert(thread->is_VM_thread() || thread->is_Java_thread(), "Must be VMThread or JavaThread");
+
+ os::SuspendResume::State current = osthread->sr.state();
+ if (current == os::SuspendResume::SR_SUSPEND_REQUEST) {
+ suspend_save_context(osthread, uc);
+
+ // attempt to switch the state, we assume we had a SUSPEND_REQUEST
+ os::SuspendResume::State state = osthread->sr.suspended();
+ if (state == os::SuspendResume::SR_SUSPENDED) {
+ sigset_t suspend_set; // signals for sigsuspend()
+
+ // get current set of blocked signals and unblock resume signal
+ thr_sigsetmask(SIG_BLOCK, NULL, &suspend_set);
+ sigdelset(&suspend_set, os::Solaris::SIGasync());
+
+ sr_semaphore.signal();
+ // wait here until we are resumed
+ while (1) {
+ sigsuspend(&suspend_set);
+
+ os::SuspendResume::State result = osthread->sr.running();
+ if (result == os::SuspendResume::SR_RUNNING) {
+ sr_semaphore.signal();
+ break;
+ }
+ }
+
+ } else if (state == os::SuspendResume::SR_RUNNING) {
+ // request was cancelled, continue
+ } else {
+ ShouldNotReachHere();
+ }
+
+ resume_clear_context(osthread);
+ } else if (current == os::SuspendResume::SR_RUNNING) {
+ // request was cancelled, continue
+ } else if (current == os::SuspendResume::SR_WAKEUP_REQUEST) {
+ // ignore
+ } else {
+ // ignore
+ }
+
+ errno = old_errno;
+ }
+
+
void os::interrupt(Thread* thread) {
assert(Thread::current() == thread || Threads_lock->owned_by_self(), "possibility of dangling Thread pointer");
OSThread* osthread = thread->osthread();
*** 4245,4276 ****
while (::read(0, buf, sizeof(buf)) <= 0) { ::sleep(100); }
return buf[0] == 'y' || buf[0] == 'Y';
}
// A lightweight implementation that does not suspend the target thread and
// thus returns only a hint. Used for profiling only!
ExtendedPC os::get_thread_pc(Thread* thread) {
// Make sure that it is called by the watcher and the Threads lock is owned.
assert(Thread::current()->is_Watcher_thread(), "Must be watcher and own Threads_lock");
// For now, is only used to profile the VM Thread
assert(thread->is_VM_thread(), "Can only be called for VMThread");
! ExtendedPC epc;
!
! GetThreadPC_Callback cb(ProfileVM_lock);
! OSThread *osthread = thread->osthread();
! const int time_to_wait = 400; // 400ms wait for initial response
! int status = cb.interrupt(thread, time_to_wait);
!
! if (cb.is_done() ) {
! epc = cb.addr();
! } else {
! DEBUG_ONLY(tty->print_cr("Failed to get pc for thread: %d got %d status",
! osthread->thread_id(), status););
! // epc is already NULL
! }
! return epc;
}
// This does not do anything on Solaris. This is basically a hook for being
// able to use structured exception handling (thread-local exception filters) on, e.g., Win32.
--- 4360,4489 ----
while (::read(0, buf, sizeof(buf)) <= 0) { ::sleep(100); }
return buf[0] == 'y' || buf[0] == 'Y';
}
+ static int sr_notify(OSThread* osthread) {
+ int status = thr_kill(osthread->thread_id(), os::Solaris::SIGasync());
+ assert_status(status == 0, status, "thr_kill");
+ return status;
+ }
+
+ // "Randomly" selected value for how long we want to spin
+ // before bailing out on suspending a thread, also how often
+ // we send a signal to a thread we want to resume
+ static const int RANDOMLY_LARGE_INTEGER = 1000000;
+ static const int RANDOMLY_LARGE_INTEGER2 = 100;
+
+ static bool do_suspend(OSThread* osthread) {
+ assert(osthread->sr.is_running(), "thread should be running");
+ assert(!sr_semaphore.trywait(), "semaphore has invalid state");
+
+ // mark as suspended and send signal
+ if (osthread->sr.request_suspend() != os::SuspendResume::SR_SUSPEND_REQUEST) {
+ // failed to switch, state wasn't running?
+ ShouldNotReachHere();
+ return false;
+ }
+
+ if (sr_notify(osthread) != 0) {
+ ShouldNotReachHere();
+ }
+
+ // managed to send the signal and switch to SUSPEND_REQUEST, now wait for SUSPENDED
+ while (true) {
+ if (sr_semaphore.timedwait(0, 2000 * NANOSECS_PER_MILLISEC)) {
+ break;
+ } else {
+ // timeout
+ os::SuspendResume::State cancelled = osthread->sr.cancel_suspend();
+ if (cancelled == os::SuspendResume::SR_RUNNING) {
+ return false;
+ } else if (cancelled == os::SuspendResume::SR_SUSPENDED) {
+ // make sure that we consume the signal on the semaphore as well
+ sr_semaphore.wait();
+ break;
+ } else {
+ ShouldNotReachHere();
+ return false;
+ }
+ }
+ }
+
+ guarantee(osthread->sr.is_suspended(), "Must be suspended");
+ return true;
+ }
+
+ static void do_resume(OSThread* osthread) {
+ assert(osthread->sr.is_suspended(), "thread should be suspended");
+ assert(!sr_semaphore.trywait(), "invalid semaphore state");
+
+ if (osthread->sr.request_wakeup() != os::SuspendResume::SR_WAKEUP_REQUEST) {
+ // failed to switch to WAKEUP_REQUEST
+ ShouldNotReachHere();
+ return;
+ }
+
+ while (true) {
+ if (sr_notify(osthread) == 0) {
+ if (sr_semaphore.timedwait(0, 2 * NANOSECS_PER_MILLISEC)) {
+ if (osthread->sr.is_running()) {
+ return;
+ }
+ }
+ } else {
+ ShouldNotReachHere();
+ }
+ }
+
+ guarantee(osthread->sr.is_running(), "Must be running!");
+ }
+
+ void os::SuspendedThreadTask::internal_do_task() {
+ if (do_suspend(_thread->osthread())) {
+ SuspendedThreadTaskContext context(_thread, _thread->osthread()->ucontext());
+ do_task(context);
+ do_resume(_thread->osthread());
+ }
+ }
+
+ class PcFetcher : public os::SuspendedThreadTask {
+ public:
+ PcFetcher(Thread* thread) : os::SuspendedThreadTask(thread) {}
+ ExtendedPC result();
+ protected:
+ void do_task(const os::SuspendedThreadTaskContext& context);
+ private:
+ ExtendedPC _epc;
+ };
+
+ ExtendedPC PcFetcher::result() {
+ guarantee(is_done(), "task is not done yet.");
+ return _epc;
+ }
+
+ void PcFetcher::do_task(const os::SuspendedThreadTaskContext& context) {
+ Thread* thread = context.thread();
+ OSThread* osthread = thread->osthread();
+ if (osthread->ucontext() != NULL) {
+ _epc = os::Solaris::ucontext_get_pc((ucontext_t *) context.ucontext());
+ } else {
+ // NULL context is unexpected, double-check this is the VMThread
+ guarantee(thread->is_VM_thread(), "can only be called for VMThread");
+ }
+ }
+
// A lightweight implementation that does not suspend the target thread and
// thus returns only a hint. Used for profiling only!
ExtendedPC os::get_thread_pc(Thread* thread) {
// Make sure that it is called by the watcher and the Threads lock is owned.
assert(Thread::current()->is_Watcher_thread(), "Must be watcher and own Threads_lock");
// For now, is only used to profile the VM Thread
assert(thread->is_VM_thread(), "Can only be called for VMThread");
! PcFetcher fetcher(thread);
! fetcher.run();
! return fetcher.result();
}
// This does not do anything on Solaris. This is basically a hook for being
// able to use structured exception handling (thread-local exception filters) on, e.g., Win32.