blob: dddc2cc8b52116fbefe5b6a67990ca7f8ceb4b2a [file] [log] [blame]
/*
* Copyright (c) 1999, 2000, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* Implementation of notposix.h on Linux.
*/
#include <pthread.h>
#include <signal.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <time.h>
#include "hpi_impl.h"
#include "monitor_md.h"
#include "threads_md.h"
#include "np.h"
#undef LOG_THREADS
/* Global lock used when calling np_suspend and np_resume */
static pthread_mutex_t sr_lock;
/* Semaphore used to acknowledge when the handler has received HANDLER_SIG */
static sem_t sr_sem;
/* The tid of the thread being suspended/resumed */
static sys_thread_t *sr_tid;
int sr_sigsusp;
int sr_sigresu;
static void prtsigset(char *s, sigset_t *set)
{
int sig;
dprintf(2, "%s:", s);
for (sig = 1; sig < _NSIG; sig++) {
if (sigismember(set, sig)) {
dprintf(2, " %d", sig);
}
}
dprintf(2, "\n");
}
/*
* Handler function invoked when a thread's execution is suspended
* We have to be careful that only async-safe functions are
* called here. I'm not even sure if calling sysThreadSelf is safe so
* we temporarily stash SP in a global variable instead.
*/
static void
#ifdef SA_SIGINFO
susp_handler(int sig, siginfo_t* info, void* arg)
#else
susp_handler(int sig)
#endif
{
sys_thread_t *tid = sr_tid;
sigset_t set;
/* Save the current SP */
tid->sp = &tid;
sem_post(&sr_sem);
sigfillset(&set);
sigdelset(&set,(sr_sigresu));
/* block until we receive resume signal. */
sigsuspend(&set);
}
static void
#ifdef SA_SIGINFO
resu_handler(int sig, siginfo_t* info, void* arg)
#else
resu_handler(int sig)
#endif
{
return;
}
/*
* Initialize signal handlers for suspend and resume}.
*/
int
np_initialize()
{
struct sigaction act;
char *s;
int err;
/* Signal numbers used to suspend and resume */
#if __GLIBC__ == 2 && __GLIBC_MINOR__ == 0
#ifdef SIGUNUSED
sr_sigsusp = SIGUNUSED;
#else
sr_sigsusp = SIGLOST;
#endif
#ifdef SIGPWR
sr_sigresu = SIGPWR;
#else
sr_sigresu = SIGXFSZ;
#endif
#else
/* use real time signals */
/* currently __SIGRTMIN, +1, +2 are all used by LinuxThreads */
sr_sigsusp = SIGRTMIN + 3;
sr_sigresu = SIGRTMIN + 4;
#endif
/* Set up signal handler for suspend and resume */
#if defined(SA_SIGINFO) && !defined(__sparc__)
act.sa_handler = 0;
act.sa_sigaction = susp_handler;
#else
act.sa_handler = (__sighandler_t) susp_handler;
#endif
#ifdef SA_SIGINFO
act.sa_flags = SA_RESTART | SA_SIGINFO;
#else
act.sa_flags = SA_RESTART;
#endif
sigfillset(&act.sa_mask);
if (sigaction(sr_sigsusp, &act, 0) == -1) {
return -1;
}
#if defined(SA_SIGINFO) && !defined(__sparc__)
act.sa_handler = 0;
act.sa_sigaction = resu_handler;
#else
act.sa_handler = (__sighandler_t) resu_handler;
#endif
#ifdef SA_SIGINFO
act.sa_flags = SA_SIGINFO;
#else
act.sa_flags = 0;
#endif
sigfillset(&act.sa_mask);
if (sigaction(sr_sigresu, &act, 0) == -1) {
return -1;
}
/* Initialize semaphore used by np_{suspend/resume} */
if (sem_init(&sr_sem, 0, 0) == -1) {
return SYS_ERR;
}
/* Initialize mutex used by np_{suspend/resume} */
err = mutexInit(&sr_lock);
sysAssert(err == 0);
return SYS_OK;
}
int
np_initial_suspend(sys_thread_t* tid)
{
int count;
tid->selfsuspended = (tid == sysThreadSelf());
sysAssert(tid->selfsuspended);
count = tid->suspend_count++;
sysAssert(count == 0);
#ifdef LOG_THREADS
dprintf(2,
"[Initial self-suspend [tid = %ld, sys_thread = %ld]\n",
pthread_self(), tid->sys_thread);
#endif
/* Order should not matter but doing the post first should be faster */
sem_post(&tid->sem_suspended);
do {
sem_wait(&tid->sem_selfsuspend);
} while (tid->selfsuspended); /* paranoid */
return 0;
}
int
np_suspend(sys_thread_t *tid)
{
int count, ret = 0;
int err = mutexLock(&sr_lock);
sysAssert(err == 0);
tid->selfsuspended = (tid == sysThreadSelf());
count = tid->suspend_count++;
#ifdef LOG_THREADS
dprintf(2, "[Suspending fromtid = %ld, tid = %ld, pid = %d, count = %d]\n",
pthread_self(), tid->sys_thread, tid->lwp_id, count);
#endif
if (count == 0) {
if (tid->selfsuspended) {
#ifdef LOG_THREADS
dprintf(2,
"[Self-suspending [tid = %ld, sys_thread = %ld]\n",
pthread_self(), tid->sys_thread);
#endif
mutexUnlock(&sr_lock);
do {
sem_wait(&tid->sem_selfsuspend);
} while (tid->selfsuspended);
/* [jk] What is the correct return value here?
There was no error, but when we return the thread
has already been resumed. */
return SYS_OK;
} else {
sr_tid = tid;
ret = pthread_kill(tid->sys_thread, sr_sigsusp);
if (ret == 0) {
sem_wait(&sr_sem);
}
#ifdef LOG_THREADS
dprintf(2,
"[Suspended fromtid = %ld, pthread_kill(%ld, %d) = %d]\n",
pthread_self(), tid->sys_thread, sr_sigsusp, ret);
#endif
}
}
err = mutexUnlock(&sr_lock);
sysAssert(err == 0);
return ret == 0 ? SYS_OK : SYS_ERR;
}
int
np_continue(sys_thread_t *tid)
{
int count, ret = 0;
int err = mutexLock(&sr_lock);
sysAssert(err == 0);
count = --tid->suspend_count;
#ifdef LOG_THREADS
dprintf(2, "[Resuming fromtid = %ld, tid = %ld, pid = %d, count = %d]\n",
pthread_self(), tid->sys_thread, tid->lwp_id, count);
#endif
if (count == 0) {
if (tid->selfsuspended) {
tid->selfsuspended = 0;
sem_post(&tid->sem_selfsuspend);
} else {
sr_tid = tid;
ret = pthread_kill(tid->sys_thread, sr_sigresu);
}
#ifdef LOG_THREADS
dprintf(2, "[Resumed fromtid = %ld, pthread_kill(%ld, %d) = %d]\n",
pthread_self(), tid->sys_thread, sr_sigresu, ret);
#endif
} else if (count < 0) {
/* Ignore attempts to resume a thread that has not been suspended */
tid->suspend_count = 0;
}
err = mutexUnlock(&sr_lock);
sysAssert(err == 0);
return ret == 0 ? SYS_OK : SYS_ERR;
}
/*
* Get the stack base and size.
*/
int
np_stackinfo(void **addr, long *size)
{
/* For now assume stack is 2 meg, from internals.h. */
#define STACK_SIZE (2 * 1024 * 1024)
void *p;
char *sp = (char *)&p; /* rougly %esp */
*addr = (void *)(((unsigned long)sp | (STACK_SIZE-1))+1) - 1;
*size = STACK_SIZE;
return SYS_OK;
}
typedef unsigned long ulong_t;
#define VALID_SP(sp, bottom, top) \
(((ulong_t)(sp)) < ((ulong_t)(bottom)) && ((ulong_t)(sp)) > ((ulong_t)(top)))
/*
* Go into single threaded mode for GC.
*/
int
np_single()
{
sys_thread_t *tid;
pthread_t me = pthread_self();
int i;
#ifdef LOG_THREADS
dprintf(2, "[Entering np_single: thread count = %d]\n", ActiveThreadCount);
#endif
/* Stop all other threads. */
tid = ThreadQueue;
for (i = 0; i < ActiveThreadCount && tid != 0; i++) {
if ((tid->sys_thread != me) && (tid->state != SUSPENDED)) {
np_suspend(tid);
sysAssert(VALID_SP(tid->sp, tid->stack_bottom, tid->stack_top));
tid->onproc = FALSE; /* REMIND: Might not need this */
}
tid = tid->next;
}
#ifdef LOG_THREADS
dprintf(2, "[Leaving np_single]\n");
#endif
return SYS_OK;
}
/*
* Per thread initialization.
*/
void
np_initialize_thread(sys_thread_t *tid)
{
sigset_t set;
/* Block SIGQUIT so that it can be handled by the SIGQUIT handler thread */
sigemptyset(&set);
sigaddset(&set, SIGQUIT);
pthread_sigmask(SIG_BLOCK, &set, 0);
/* Set process id */
tid->lwp_id = getpid();
tid->suspend_count = 0;
/* Semaphore used for self-suspension */
sem_init(&tid->sem_selfsuspend, 0, 0);
tid->selfsuspended = 0;
#ifdef LOG_THREADS
dprintf(2, "[Init thread, tid = %ld, pid = %d, base = %p, size = %lu]\n",
pthread_self(), tid->lwp_id, tid->stack_bottom, tid->stack_size);
#endif
}
void
np_free_thread(sys_thread_t *tid)
{
sem_destroy(&tid->sem_selfsuspend);
}
/*
* Recover from single threaded mode after GC.
*/
void
np_multi()
{
int i;
sys_thread_t *tid;
pthread_t me = pthread_self();
tid = ThreadQueue;
for (i = 0; i < ActiveThreadCount && tid != 0; i++) {
if ((tid->sys_thread != me) && (tid->state != SUSPENDED)) {
np_continue(tid);
}
tid = tid->next;
}
}
void
np_profiler_init(sys_thread_t *tid)
{
}
int
np_profiler_suspend(sys_thread_t *tid)
{
return np_suspend(tid);
}
int
np_profiler_continue(sys_thread_t *tid)
{
return np_continue(tid);
}
bool_t
np_profiler_thread_is_running(sys_thread_t *tid)
{
return TRUE;
}