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 <assert.h>
#include <stdlib.h>
#include <errno.h>
#include <pthread.h>
#ifdef __APPLE__
/* MacOSX diverges in a good way and therefore many functions
in this file are just stubs for it, that other code dynamically choses
not to call (statically, but the compiler can't or won't tell). */
#define APPLE_ASSERT_FALSE assert(0 && "MacOS X should not get here.");
#else
#include <signal.h>
#include <semaphore.h>
#include <string.h>
#ifdef __hpux
#include <stdio.h>
#endif /* hpux */
#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
#ifdef __cplusplus
extern "C" {
#endif
#define SignalHandler ThreadPThread__SignalHandler
#define SetupHandlers ThreadPThread__SetupHandlers
#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
OpenBSD: 31
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.
*/
#if defined(__APPLE__)
EXTERN_CONST int SIG_SUSPEND = 0;
#elif defined(__sun) || defined(__CYGWIN__) || defined(__FreeBSD__)
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
#ifndef __APPLE__
#define ZeroMemory(a, b) (memset((a), 0, (b)))
typedef struct sigaction sigaction_t;
static sigset_t mask;
/* Signal based suspend/resume */
static sem_t ackSem;
void SignalHandler(int, siginfo_t*, void* /* ucontext_t* */);
void SetupHandlers(void)
{
sigaction_t act;
sigaction_t oact;
int r;
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);
}
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); }
int ThreadPThread__sigsuspend(void) { return sigsuspend(&mask); }
#else /* Apple */
void SetupHandlers(void) { APPLE_ASSERT_FALSE }
void ThreadPThread__sem_wait(void) { APPLE_ASSERT_FALSE }
void ThreadPThread__sem_post(void) { APPLE_ASSERT_FALSE }
void ThreadPThread__sem_getvalue(void) { APPLE_ASSERT_FALSE }
void ThreadPThread__sigsuspend(void) { APPLE_ASSERT_FALSE }
#endif /* Apple */
#define M3_MAX(x, y) (((x) > (y)) ? (x) : (y))
typedef void* (*start_routine_t)(void*);
int
ThreadPThread__thread_create(
pthread_t* pthread,
size_t stackSize,
start_routine_t start_routine,
void* arg)
{
int r;
size_t bytes;
pthread_attr_t attr;
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; \
int ThreadPThread__pthread_mutex_lock_##name(void) \
{ \
return pthread_mutex_lock(&name##Mu); \
} \
\
int ThreadPThread__pthread_mutex_unlock_##name(void) \
{ \
return pthread_mutex_unlock(&name##Mu); \
} \
#define CONDITION_VARIABLE(name) \
static pthread_cond_t name##Cond = PTHREAD_COND_INITIALIZER; \
int ThreadPThread__pthread_cond_broadcast_##name(void) \
{ \
return pthread_cond_broadcast(&name##Cond); \
} \
\
int ThreadPThread__pthread_cond_wait_##name(void) \
{ \
return pthread_cond_wait(&name##Cond, &name##Mu); \
} \
#define THREAD_LOCAL_FAST(name) \
static __thread void* name; \
int ThreadPThread__pthread_key_create_##name(void) \
{ \
/* nothing */ \
} \
int ThreadPThread__pthread_setspecific_##name(void* value) \
{ \
name = value; \
} \
void* ThreadPThread__pthread_getspecific_##name(void) \
{ \
return name; \
} \
#define THREAD_LOCAL_SLOW(name) \
static pthread_key_t name; \
int ThreadPThread__pthread_key_create_##name(void) \
{ \
return pthread_key_create(&name, NULL); \
} \
int ThreadPThread__pthread_setspecific_##name(void* value) \
{ \
return pthread_setspecific(name, value); \
} \
void* ThreadPThread__pthread_getspecific_##name(void) \
{ \
return pthread_getspecific(name); \
} \
#if 0 /* M3CONFIG_THREAD_LOCAL_STORAGE */
#define THREAD_LOCAL(name) THREAD_LOCAL_FAST(name)
#else
#define THREAD_LOCAL(name) THREAD_LOCAL_SLOW(name)
#endif
/* activeMu slotMu initMu perfMu heapMu heapCond */
MUTEX(active) /* global lock for list of active threads */
MUTEX(slot) /* global lock for thread slot table */
MUTEX(init) /* global lock for initializers */
MUTEX(perf)
MUTEX(heap)
CONDITION_VARIABLE(heap)
THREAD_LOCAL(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);
}
#ifdef __cplusplus
} /* extern "C" */
#endif