m3core/src/thread/PTHREAD/ThreadPThreadC.c
/* Copyright (C) 2005, Purdue Research Foundation */
/* All rights reserved. */
/* See the file COPYRIGHT-PURDUE for a full description. */
#include "m3unix.h"
#include <stdlib.h>
#include <pthread.h>
#include <setjmp.h>
#include <stdio.h>
#include <signal.h>
#include <sys/ucontext.h>
#ifdef __OpenBSD__
#error OpenBSD pthreads do not work.
#endif
#ifdef __APPLE__
#define M3_DIRECT_SUSPEND
#include <mach/mach.h>
#include <mach/thread_act.h>
#if defined(__ppc__) || defined(__ppc64__)
#include <architecture/ppc/cframe.h>
#endif
#endif
#ifdef __FreeBSD__
#define M3_DIRECT_SUSPEND
#endif
#ifndef M3_DIRECT_SUSPEND
#include <semaphore.h>
#endif
/* Sometimes setjmp saves signal mask, in which case _setjmp does not.
setjmp works, but _setjmp can be much faster. */
#ifndef __sun
#define M3_SETJMP _setjmp
#define M3_LONGJMP _longjmp
#else
#define M3_SETJMP setjmp
#define M3_LONGJMP longjmp
#endif
#if defined(__sparc) || defined(__ia64__)
#define M3_REGISTER_WINDOWS
#endif
#ifdef M3_DIRECT_SUSPEND
#define M3_DIRECT_SUSPEND_ASSERT_FALSE do { \
assert(0 && "MacOS X, FreeBSD should not get here."); \
fprintf(stderr, "MacOS X, FreeBSD should not get here.\n"); \
abort(); \
} while(0);
#endif
/* const is extern const in C, but static const in C++,
* but gcc gives a warning for the correct portable form "extern const" */
#if defined(__cplusplus) || !defined(__GNUC__)
#define EXTERN_CONST extern const
#else
#define EXTERN_CONST const
#endif
#if __GNUC__ || __SUNPRO_C >= 0x590
#define ATTRIBUTE_NOINLINE __attribute__((noinline))
#else
#define ATTRIBUTE_NOINLINE
#endif
#if _MSC_VER >= 1300
#define DECLSPEC_NOINLINE __declspec(noinline)
#else
#define DECLSPEC_NOINLINE
#endif
#ifdef __cplusplus
extern "C" {
#endif
#define InitC ThreadPThread__InitC
#define SignalHandler ThreadPThread__SignalHandler
#define sizeof_pthread_mutex_t ThreadPThread__sizeof_pthread_mutex_t
#define sizeof_pthread_cond_t ThreadPThread__sizeof_pthread_cond_t
#define SIG_SUSPEND ThreadPThread__SIG_SUSPEND
/* expected values for compat, if compat matters:
Solaris: 17 (at least 32bit SPARC?)
Cygwin: 19 -- er, but maybe that's wrong
Linux: 64
FreeBSD: 31 (not used)
OpenBSD: 31 (not used)
HPUX: 44
Look at the history of Usignal and RTMachine to find more values. There was
RTMachine.SIG_SUSPEND and SIG was aliased to it. Both SIG and SIG_SUSPEND
were only defined for systems using pthreads. SIG was shorthand. */
#ifdef M3_DIRECT_SUSPEND
EXTERN_CONST int SIG_SUSPEND = 0;
#elif defined(__sun) || defined(__CYGWIN__)
EXTERN_CONST int SIG_SUSPEND = SIGUSR2;
#elif defined(__linux)
EXTERN_CONST int SIG_SUSPEND = NSIG - 1;
#elif defined(__hpux)
EXTERN_CONST int SIG_SUSPEND = _SIGRTMAX;
#elif defined(SIGRTMAX)
/* This might be a function call, in which case try _SIGRTMAX or initializing
it somewhere. */
EXTERN_CONST int SIG_SUSPEND = SIGRTMAX;
#elif defined(SIGUSR2)
EXTERN_CONST int SIG_SUSPEND = SIGUSR2;
#else
#error Unable to determine SIG_SUSPEND.
#endif
static int stack_grows_down;
#ifndef M3_DIRECT_SUSPEND
typedef struct sigaction sigaction_t;
static sigset_t mask;
/* Signal based suspend/resume */
static sem_t ackSem;
void SignalHandler(int signo, siginfo_t *info, void *context);
int ThreadPThread__sem_wait(void) { return sem_wait(&ackSem); }
int ThreadPThread__sem_post(void) { return sem_post(&ackSem); }
int ThreadPThread__sem_getvalue(int *value) { return sem_getvalue(&ackSem, value); }
void
ThreadPThread__sigsuspend(void)
{
jmp_buf jb;
if (M3_SETJMP(jb) == 0) /* save registers to stack */
#ifdef M3_REGISTER_WINDOWS
M3_LONGJMP(jb, 1); /* flush register windows */
else
#endif
sigsuspend(&mask);
}
int
ThreadPThread__SuspendThread (m3_pthread_t mt)
{
abort();
}
int
ThreadPThread__RestartThread (m3_pthread_t mt)
{
abort();
}
void
ThreadPThread__ProcessStopped (m3_pthread_t mt, void *bottom, void *context,
void (*p)(void *start, void *limit))
{
/* process stack */
if (!bottom) return;
if (stack_grows_down) {
assert((char *)context < (char *)bottom);
p(context, bottom);
} else {
assert((char *)bottom < (char *)context);
p(bottom, context);
}
/* process register context */
p(context, ((char *)context) + sizeof(ucontext_t));
}
#else /* M3_DIRECT_SUSPEND */
void ThreadPThread__sem_wait(void) { M3_DIRECT_SUSPEND_ASSERT_FALSE }
void ThreadPThread__sem_post(void) { M3_DIRECT_SUSPEND_ASSERT_FALSE }
void ThreadPThread__sem_getvalue(void) { M3_DIRECT_SUSPEND_ASSERT_FALSE }
void ThreadPThread__sigsuspend(void) { M3_DIRECT_SUSPEND_ASSERT_FALSE }
#ifdef __FreeBSD__
int
ThreadPThread__SuspendThread (m3_pthread_t mt)
{
int a = pthread_suspend_np(PTHREAD_FROM_M3(mt));
int success = (a == 0);
assert(success);
return success;
}
int
ThreadPThread__RestartThread (m3_pthread_t mt)
{
int a = pthread_resume_np(PTHREAD_FROM_M3(mt));
int success = (a == 0);
assert(success);
return success;
}
void
ThreadPThread__ProcessStopped (m3_pthread_t mt, void *bottom, void *context,
void (*p)(void *start, void *limit))
{
pthread_attr_t attr;
char *stackaddr;
size_t stacksize;
/* process the stacks */
if (pthread_attr_init(&attr) != 0) abort();
if (pthread_attr_get_np(PTHREAD_FROM_M3(mt), &attr) != 0) abort();
if (pthread_attr_getstack(&attr, (void **)&stackaddr, &stacksize) != 0) abort();
if (pthread_attr_destroy(&attr) != 0) abort();
assert(stack_grows_down);
assert(context == 0);
assert((char *)bottom >= stackaddr);
assert((char *)bottom <= (stackaddr + stacksize));
p(stackaddr, bottom);
/* assume registers are stored in the stack */
/* but call p to simulate processing registers: see RTHeapStats.m3 */
p(0, 0);
}
#endif /* FreeBSD */
#ifdef __APPLE__
int
ThreadPThread__SuspendThread (m3_pthread_t mt)
{
pthread_t t = PTHREAD_FROM_M3(mt);
mach_port_t mach_thread = pthread_mach_thread_np(t);
if (thread_suspend(mach_thread) != KERN_SUCCESS) return 0;
if (thread_abort_safely(mach_thread) != KERN_SUCCESS) {
if (thread_resume(mach_thread != KERN_SUCCESS)) abort();
return 0;
}
return 1;
}
int
ThreadPThread__RestartThread (m3_pthread_t mt)
{
pthread_t t = PTHREAD_FROM_M3(mt);
mach_port_t mach_thread = pthread_mach_thread_np(t);
return thread_resume(mach_thread) == KERN_SUCCESS;
}
void
ThreadPThread__ProcessStopped (m3_pthread_t mt, void *bottom, void *context,
void (*p)(void *start, void *limit))
{
void *sp;
pthread_t t = PTHREAD_FROM_M3(mt);
mach_port_t mach_thread = pthread_mach_thread_np(t);
#if defined(__ppc__)
ppc_thread_state_t state;
mach_msg_type_number_t thread_state_count = PPC_THREAD_STATE_COUNT;
if (thread_get_state(mach_thread, PPC_THREAD_STATE,
(thread_state_t)&state, &thread_state_count)
!= KERN_SUCCESS) abort();
if (thread_state_count != PPC_THREAD_STATE_COUNT) abort();
#if __DARWIN_UNIX03
sp = (void *)(state.__r1 - C_RED_ZONE);
#else
sp = (void *)(state.r1 - C_RED_ZONE);
#endif
#elif defined(__ppc64__)
ppc_thread_state64_t state;
mach_msg_type_number_t thread_state_count = PPC_THREAD_STATE64_COUNT;
if (thread_get_state(mach_thread, PPC_THREAD_STATE64,
(thread_state_t)&state, &thread_state_count)
!= KERN_SUCCESS) abort();
if (thread_state_count != PPC_THREAD_STATE64_COUNT) abort();
#if __DARWIN_UNIX03
sp = (void *)(state.__r1 - C_RED_ZONE);
#else
sp = (void *)(state.r1 - C_RED_ZONE);
#endif
#elif defined(__i386__)
i386_thread_state_t state;
mach_msg_type_number_t thread_state_count = i386_THREAD_STATE_COUNT;
if (thread_get_state(mach_thread, i386_THREAD_STATE,
(thread_state_t)&state, &thread_state_count)
!= KERN_SUCCESS) abort();
if (thread_state_count != i386_THREAD_STATE_COUNT) abort();
#if __DARWIN_UNIX03
sp = (void *)(state.__esp);
#else
sp = (void *)(state.esp);
#endif
#elif defined(__x86_64__)
x86_thread_state64_t state;
mach_msg_type_number_t thread_state_count = x86_THREAD_STATE64_COUNT;
if (thread_get_state(mach_thread, x86_THREAD_STATE64,
(thread_state_t)&state, &thread_state_count)
!= KERN_SUCCESS) abort();
if (thread_state_count != x86_THREAD_STATE64_COUNT) abort();
#if __DARWIN_UNIX03
sp = (void *)(state.__rsp - 128);
#else
sp = (void *)(state.rsp - 128);
#endif
#elif defined(__arm__)
mach_msg_type_number_t thread_state_count = ARM_THREAD_STATE_COUNT;
if (thread_get_state(mach_thread, ARM_THREAD_STATE,
state, &thread_state_count)
!= KERN_SUCCESS) abort();
if (thread_state_count != ARM_THREAD_STATE_COUNT) abort();
sp = (void *)(state.r13);
#endif
/* process the stack */
assert(stack_grows_down);
assert(context == 0);
p(sp, bottom);
/* process the registers */
p(&state, (char *)&state + sizeof(state));
}
#endif /* Apple */
#endif /* M3_DIRECT_SUSPEND */
void
ThreadPThread__ProcessLive(void *bottom, void (*p)(void *start, void *limit))
{
jmp_buf jb;
if (M3_SETJMP(jb) == 0) /* save registers to stack */
#ifdef M3_REGISTER_WINDOWS
M3_LONGJMP(jb, 1); /* flush register windows */
else
#endif
{
void *top = ⊤
assert(bottom);
if (stack_grows_down) {
assert((char *)top < (char *)bottom);
p(top, bottom);
} else {
assert((char *)bottom < (char *)top);
p(bottom, top);
}
p(&jb, ((char *)&jb) + sizeof(jb));
}
}
#define M3_MAX(x, y) (((x) > (y)) ? (x) : (y))
typedef void *(*start_routine_t)(void *);
int
ThreadPThread__thread_create(size_t stackSize,
start_routine_t start_routine,
void *arg)
{
int r;
size_t bytes;
pthread_attr_t attr;
pthread_t pthread;
r = pthread_attr_init(&attr);
#ifdef __hpux
if (r == ENOSYS)
{
fprintf(stderr,
"You got the nonfunctional pthread stubs on HP-UX. You need to"
" adjust your build commands, such as to link to -lpthread or"
" use -pthread, and not link explicitly to -lc.\n");
}
#endif
assert(r == 0);
r = pthread_attr_getstacksize(&attr, &bytes); assert(r == 0);
bytes = M3_MAX(bytes, stackSize);
pthread_attr_setstacksize(&attr, bytes);
r = pthread_create(&pthread, &attr, start_routine, arg);
pthread_attr_destroy(&attr);
return r;
}
#define MUTEX(name) \
static pthread_mutex_t name##Mu = PTHREAD_MUTEX_INITIALIZER; \
pthread_mutex_t * const ThreadPThread__##name##Mu = &name##Mu; \
#define CONDITION_VARIABLE(name) \
static pthread_cond_t name##Cond = PTHREAD_COND_INITIALIZER; \
pthread_cond_t * const ThreadPThread__##name##Cond = &name##Cond; \
/* activeMu slotMu initMu perfMu heapMu heapCond */
MUTEX(active) /* global lock for list of active threads */
MUTEX(slots) /* global lock for thread slots table */
MUTEX(init) /* global lock for initializers */
MUTEX(perf) /* global lock for thread state tracing */
MUTEX(heap) /* global lock for heap atomicity */
CONDITION_VARIABLE(heap) /* CV for heap state changes */
static pthread_key_t activations;
void
ThreadPThread__SetActivation(void *value)
{
int r = pthread_setspecific(activations, value);
assert(r == 0);
}
void *
ThreadPThread__GetActivation(void)
{
return pthread_getspecific(activations);
}
typedef int (*generic_init_t)(void *, const void *);
void *
ThreadPThread_pthread_generic_new(size_t size, generic_init_t init)
{
int r;
void *p = calloc(1, size);
if (p == NULL)
goto Error;
r = init(p, NULL);
if (r == EAGAIN)
r = init(p, NULL);
if (r == ENOMEM)
goto Error;
assert(r == 0);
if (r != 0)
goto Error;
return p;
Error:
if (p) free(p);
return NULL;
}
#define THREADPTHREAD__PTHREAD_GENERIC_NEW(type) { \
typedef pthread_##type##_t T; \
typedef pthread_##type##attr_t attr_t; \
typedef int (*init_t)(T *, const attr_t *); \
/* make sure the type matches */ \
init_t init = pthread_##type##_init; \
return ThreadPThread_pthread_generic_new(sizeof(T), \
(generic_init_t)init); \
}
void *
ThreadPThread__pthread_mutex_new(void)
{
THREADPTHREAD__PTHREAD_GENERIC_NEW(mutex);
}
void *
ThreadPThread__pthread_cond_new(void)
{
THREADPTHREAD__PTHREAD_GENERIC_NEW(cond);
}
void
ThreadPThread__pthread_mutex_delete(pthread_mutex_t* p)
{
int e;
if (p == NULL) return;
#if defined(__hpux) || defined(__osf)
/* workaround Tru64 5.1 and HP-UX bug: pthread_mutex_destroy()
intermittently returns EBUSY even when there are no threads accessing the
mutex. */
while ((e = pthread_mutex_destroy(p)) == EBUSY) { }
#else
e = pthread_mutex_destroy(p);
#endif
assert(e == 0);
free(p);
}
void
ThreadPThread__pthread_cond_delete(pthread_cond_t *p)
{
int r;
if (p == NULL) return;
r = pthread_cond_destroy(p);
assert(r == 0);
free(p);
}
int
ThreadPThread__Nanosleep(timespec_T *req, timespec_T *rem)
{
#ifdef __INTERIX
/* This is only an approximation. */
if (rem != NULL)
memset(rem, 0, sizeof(*rem));
if (req->tv_sec > 0)
sleep(req->tv_sec);
else
usleep(req->tv_nsec / 1000);
return 0;
#else
return nanosleep(req, rem);
#endif
}
int
ThreadPThread__pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
{
return pthread_cond_wait(cond, mutex);
}
int
ThreadPThread__pthread_cond_timedwait(pthread_cond_t *cond,
pthread_mutex_t *mutex,
const timespec_T *abs)
{
return pthread_cond_timedwait(cond, mutex, abs);
}
int
ThreadPThread__pthread_cond_signal(pthread_cond_t *cond)
{
return pthread_cond_signal(cond);
}
int
ThreadPThread__pthread_cond_broadcast(pthread_cond_t *cond)
{
return pthread_cond_broadcast(cond);
}
int
ThreadPThread__pthread_detach_self(void)
{
return pthread_detach(pthread_self());
}
m3_pthread_t ThreadPThread__pthread_self(void)
{
pthread_t a = pthread_self();
return PTHREAD_TO_M3(a);
}
int
ThreadPThread__pthread_equal(m3_pthread_t t1, m3_pthread_t t2)
{
return pthread_equal(PTHREAD_FROM_M3(t1), PTHREAD_FROM_M3(t2));
}
int
ThreadPThread__pthread_kill(m3_pthread_t thread, int sig)
{
return pthread_kill(PTHREAD_FROM_M3(thread), sig);
}
int
ThreadPThread__pthread_mutex_lock(pthread_mutex_t *m)
{
return pthread_mutex_lock(m);
}
int
ThreadPThread__pthread_mutex_unlock(pthread_mutex_t *m)
{
return pthread_mutex_unlock(m);
}
void
InitC(int *bottom)
{
#ifndef M3_DIRECT_SUSPEND
sigaction_t act;
sigaction_t oact;
#endif
int r;
stack_grows_down = (bottom > &r);
r = pthread_key_create(&activations, NULL); assert(r == 0);
#ifndef M3_DIRECT_SUSPEND
ZeroMemory(&act, sizeof(act));
ZeroMemory(&oact, sizeof(oact));
r = sem_init(&ackSem, 0, 0); assert(r == 0);
r = sigfillset(&mask); assert(r == 0);
r = sigdelset(&mask, SIG_SUSPEND); assert(r == 0);
r = sigdelset(&mask, SIGINT); assert(r == 0);
r = sigdelset(&mask, SIGQUIT); assert(r == 0);
r = sigdelset(&mask, SIGABRT); assert(r == 0);
r = sigdelset(&mask, SIGTERM); assert(r == 0);
act.sa_flags = SA_RESTART | SA_SIGINFO;
act.sa_sigaction = SignalHandler;
r = sigfillset(&act.sa_mask); assert(r == 0);
r = sigaction(SIG_SUSPEND, &act, &oact); assert(r == 0);
#endif
}
#ifdef __cplusplus
} /* extern "C" */
#endif