src/os/linux/vm/os_linux.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>

*** 143,152 **** --- 143,155 ---- sigset_t SR_sigset; /* Used to protect dlsym() calls */ static pthread_mutex_t dl_mutex; + // Declarations + static void unpackTime(timespec* absTime, bool isAbsolute, jlong time); + #ifdef JAVASE_EMBEDDED class MemNotifyThread: public Thread { friend class VMStructs; public: virtual void run();
*** 2395,2404 **** --- 2398,2458 ---- 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: + sem_t _semaphore; + }; + + + Semaphore::Semaphore() { + sem_init(&_semaphore, 0, 0); + } + + Semaphore::~Semaphore() { + sem_destroy(&_semaphore); + } + + void Semaphore::signal() { + sem_post(&_semaphore); + } + + void Semaphore::wait() { + sem_wait(&_semaphore); + } + + bool Semaphore::trywait() { + return sem_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 = sem_timedwait(&_semaphore, &ts); + if (result == 0) { + return true; + } else if (errno == EINTR) { + continue; + } else if (errno == ETIMEDOUT) { + return false; + } else { + return false; + } + } + } + extern "C" { typedef void (*sa_handler_t)(int); typedef void (*sa_sigaction_t)(int, siginfo_t *, void *); }
*** 2434,2443 **** --- 2488,2498 ---- // a counter for each possible signal value static volatile jint pending_signals[NSIG+1] = { 0 }; // Linux(POSIX) specific hand shaking semaphore. static sem_t sig_sem; + static Semaphore sr_semaphore; void os::signal_init_pd() { // Initialize signal structures ::memset((void*)pending_signals, 0, sizeof(pending_signals));
*** 3547,3559 **** // static void resume_clear_context(OSThread *osthread) { osthread->set_ucontext(NULL); osthread->set_siginfo(NULL); - - // notify the suspend action is completed, we have now resumed - osthread->sr.clear_suspended(); } static void suspend_save_context(OSThread *osthread, siginfo_t* siginfo, ucontext_t* context) { osthread->set_ucontext(context); osthread->set_siginfo(siginfo); --- 3602,3611 ----
*** 3569,3619 **** // interface point of view, but sigwait() prevents the signal hander // from being run. libpthread would get very confused by not having // its signal handlers run and prevents sigwait()'s use with the // mutex granting granting signal. // ! // Currently only ever called on the VMThread // static void SR_handler(int sig, siginfo_t* siginfo, ucontext_t* context) { // Save and restore errno to avoid confusing native code with EINTR // after sigsuspend. int old_errno = errno; Thread* thread = Thread::current(); OSThread* osthread = thread->osthread(); ! assert(thread->is_VM_thread(), "Must be VMThread"); ! // read current suspend action ! int action = osthread->sr.suspend_action(); ! if (action == os::Linux::SuspendResume::SR_SUSPEND) { ! suspend_save_context(osthread, siginfo, context); ! ! // Notify the suspend action is about to be completed. do_suspend() ! // waits until SR_SUSPENDED is set and then returns. We will wait ! // here for a resume signal and that completes the suspend-other ! // action. do_suspend/do_resume is always called as a pair from ! // the same thread - so there are no races ! // notify the caller ! osthread->sr.set_suspended(); sigset_t suspend_set; // signals for sigsuspend() // get current set of blocked signals and unblock resume signal pthread_sigmask(SIG_BLOCK, NULL, &suspend_set); sigdelset(&suspend_set, SR_signum); // wait here until we are resumed ! do { sigsuspend(&suspend_set); - // ignore all returns until we get a resume signal - } while (osthread->sr.suspend_action() != os::Linux::SuspendResume::SR_CONTINUE); ! resume_clear_context(osthread); } else { ! assert(action == os::Linux::SuspendResume::SR_CONTINUE, "unexpected sr action"); ! // nothing special to do - just leave the handler } errno = old_errno; } --- 3621,3679 ---- // interface point of view, but sigwait() prevents the signal hander // from being run. libpthread would get very confused by not having // its signal handlers run and prevents sigwait()'s use with the // mutex granting granting signal. // ! // Currently only ever called on the VMThread and JavaThreads (PC sampling) // static void SR_handler(int sig, siginfo_t* siginfo, ucontext_t* context) { // Save and restore errno to avoid confusing native code with EINTR // after sigsuspend. int old_errno = errno; Thread* thread = Thread::current(); 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, siginfo, context); + // 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 pthread_sigmask(SIG_BLOCK, NULL, &suspend_set); sigdelset(&suspend_set, SR_signum); + 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; }
*** 3653,3698 **** // Save signal flag os::Linux::set_our_sigflags(SR_signum, act.sa_flags); return 0; } // returns true on success and false on error - really an error is fatal // but this seems the normal response to library errors static bool do_suspend(OSThread* osthread) { ! // mark as suspended and send signal ! osthread->sr.set_suspend_action(os::Linux::SuspendResume::SR_SUSPEND); ! int status = pthread_kill(osthread->pthread_id(), SR_signum); ! assert_status(status == 0, status, "pthread_kill"); ! // check status and wait until notified of suspension ! if (status == 0) { ! for (int i = 0; !osthread->sr.is_suspended(); i++) { ! os::yield_all(i); } ! osthread->sr.set_suspend_action(os::Linux::SuspendResume::SR_NONE); ! return true; } ! else { ! osthread->sr.set_suspend_action(os::Linux::SuspendResume::SR_NONE); return false; } } static void do_resume(OSThread* osthread) { assert(osthread->sr.is_suspended(), "thread should be suspended"); ! osthread->sr.set_suspend_action(os::Linux::SuspendResume::SR_CONTINUE); ! int status = pthread_kill(osthread->pthread_id(), SR_signum); ! assert_status(status == 0, status, "pthread_kill"); ! // check status and wait unit notified of resumption ! if (status == 0) { ! for (int i = 0; osthread->sr.is_suspended(); i++) { ! os::yield_all(i); } } ! osthread->sr.set_suspend_action(os::Linux::SuspendResume::SR_NONE); } //////////////////////////////////////////////////////////////////////////////// // interrupt support --- 3713,3798 ---- // Save signal flag os::Linux::set_our_sigflags(SR_signum, act.sa_flags); return 0; } + static int sr_notify(OSThread* osthread) { + int status = pthread_kill(osthread->pthread_id(), SR_signum); + assert_status(status == 0, status, "pthread_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; // returns true on success and false on error - really an error is fatal // but this seems the normal response to library errors 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, 2 * 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!"); } //////////////////////////////////////////////////////////////////////////////// // interrupt support
*** 4460,4492 **** return false; } /// ! // Suspends the target using the signal mechanism and then grabs the PC before ! // resuming the target. Used by the flat-profiler only ! ExtendedPC os::get_thread_pc(Thread* thread) { ! // Make sure that it is called by the watcher for the VMThread ! assert(Thread::current()->is_Watcher_thread(), "Must be watcher"); ! assert(thread->is_VM_thread(), "Can only be called for VMThread"); ! ExtendedPC epc; OSThread* osthread = thread->osthread(); - if (do_suspend(osthread)) { if (osthread->ucontext() != NULL) { ! epc = os::Linux::ucontext_get_pc(osthread->ucontext()); } else { // NULL context is unexpected, double-check this is the VMThread guarantee(thread->is_VM_thread(), "can only be called for VMThread"); } ! do_resume(osthread); ! } ! // failure means pthread_kill failed for some reason - arguably this is ! // a fatal problem, but such problems are ignored elsewhere ! return epc; } int os::Linux::safe_cond_timedwait(pthread_cond_t *_cond, pthread_mutex_t *_mutex, const struct timespec *_abstime) { if (is_NPTL()) { --- 4560,4613 ---- return false; } /// ! 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::Linux::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"); } ! } ! ! // Suspends the target using the signal mechanism and then grabs the PC before ! // resuming the target. Used by the flat-profiler only ! ExtendedPC os::get_thread_pc(Thread* thread) { ! // Make sure that it is called by the watcher for the VMThread ! assert(Thread::current()->is_Watcher_thread(), "Must be watcher"); ! assert(thread->is_VM_thread(), "Can only be called for VMThread"); ! PcFetcher fetcher(thread); ! fetcher.run(); ! return fetcher.result(); } int os::Linux::safe_cond_timedwait(pthread_cond_t *_cond, pthread_mutex_t *_mutex, const struct timespec *_abstime) { if (is_NPTL()) {
*** 5604,5609 **** --- 5725,5731 ---- if (memnotify_thread() == NULL) { new MemNotifyThread(fd); } } + #endif // JAVASE_EMBEDDED