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.