/* Copyright (c) 2001, Stanford University
* All rights reserved.
*
* See the file LICENSE.txt for information on redistributing this software.
*/
#include <stdio.h>
#include "cr_threads.h"
#include "cr_error.h"
/* perror() messages */
#define INIT_TSD_ERROR "InitTSD: failed to allocate key"
#define FREE_TSD_ERROR "FreeTSD: failed to destroy key"
#define SET_TSD_ERROR "InitTSD: thread failed to set thread specific data"
#define GET_TSD_ERROR "InitTSD: failed to get thread specific data"
/* Magic number to determine if a CRtsd has been initialized */
#define INIT_MAGIC 0xff8adc98
/* Initialize a CRtsd */
void crInitTSDF(CRtsd *tsd, void (*destructor)(void *))
{
#ifdef WINDOWS
tsd->key = TlsAlloc();
if (tsd->key == 0xffffffff) {
crError("crInitTSD failed!");
}
(void) destructor;
#else
if (pthread_key_create(&tsd->key, destructor) != 0)
crDebug("crServer: failed to allocate TLS key.");
else
crDebug("crServer: TLS key %d allocated.", tsd->key);
#endif
tsd->initMagic = INIT_MAGIC;
}
void crInitTSD(CRtsd *tsd)
{
crInitTSDF(tsd, NULL);
}
void crFreeTSD(CRtsd *tsd)
{
#ifdef WINDOWS
/* Windows returns true on success, 0 on failure */
if (TlsFree(tsd->key) == 0) {
crError("crFreeTSD failed!");
}
#else
if (pthread_key_delete(tsd->key) != 0)
crDebug("crServer: failed to delete TLS key %d.", tsd->key);
else
crDebug("crServer: TLS key %d deleted.", tsd->key);
#endif
tsd->initMagic = 0x0;
}
/* Set thread-specific data */
void crSetTSD(CRtsd *tsd, void *ptr)
{
if (tsd->initMagic != (int) INIT_MAGIC) {
/* initialize this CRtsd */
crInitTSD(tsd);
}
#ifdef WINDOWS
if (TlsSetValue(tsd->key, ptr) == 0) {
crError("crSetTSD failed!");
}
#else
if (pthread_setspecific(tsd->key, ptr) != 0) {
crError("crSetTSD failed!");
}
#endif
}
/* Get thread-specific data */
void *crGetTSD(CRtsd *tsd)
{
#ifdef WINDOWS
void * value;
DWORD err;
LPVOID lpMsgBuf;
#endif
if (tsd->initMagic != (int) INIT_MAGIC) {
crInitTSD(tsd);
}
#ifdef WINDOWS
value = TlsGetValue(tsd->key);
if (!value) {
err = GetLastError();
if ( err != ERROR_SUCCESS ) {
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
err,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &lpMsgBuf,
0, NULL );
crError("crGetTSD failed with %d: %s", err, lpMsgBuf);
LocalFree(lpMsgBuf);
}
}
return value;
#else
return pthread_getspecific(tsd->key);
#endif
}
/* Return ID of calling thread */
unsigned long crThreadID(void)
{
#ifdef WINDOWS
return (unsigned long) GetCurrentThreadId();
#else
return (unsigned long) pthread_self();
#endif
}
void crInitMutex(CRmutex *mutex)
{
#ifdef WINDOWS
InitializeCriticalSection(mutex);
#else
pthread_mutexattr_t mta;
int rc;
rc = pthread_mutexattr_init(&mta);
CRASSERT(!rc);
rc = pthread_mutexattr_settype(&mta, PTHREAD_MUTEX_RECURSIVE);
CRASSERT(!rc);
rc = pthread_mutex_init(mutex, &mta);
CRASSERT(!rc);
pthread_mutexattr_destroy(&mta);
#endif
}
void crFreeMutex(CRmutex *mutex)
{
#ifdef WINDOWS
DeleteCriticalSection(mutex);
#else
pthread_mutex_destroy(mutex);
#endif
}
void crLockMutex(CRmutex *mutex)
{
#ifdef WINDOWS
EnterCriticalSection(mutex);
#else
pthread_mutex_lock(mutex);
#endif
}
void crUnlockMutex(CRmutex *mutex)
{
#ifdef WINDOWS
LeaveCriticalSection(mutex);
#else
pthread_mutex_unlock(mutex);
#endif
}
void crInitCondition(CRcondition *cond)
{
#ifdef WINDOWS
/* XXX fix me */
(void) cond;
#else
int err = pthread_cond_init(cond, NULL);
if (err) {
crError("crInitCondition failed");
}
#endif
}
void crFreeCondition(CRcondition *cond)
{
#ifdef WINDOWS
/* XXX fix me */
(void) cond;
#else
int err = pthread_cond_destroy(cond);
if (err) {
crError("crFreeCondition error (threads waiting on the condition?)");
}
#endif
}
/**
* We're basically just wrapping the pthread condition var interface.
* See the man page for pthread_cond_wait to learn about the mutex parameter.
*/
void crWaitCondition(CRcondition *cond, CRmutex *mutex)
{
#ifdef WINDOWS
/* XXX fix me */
(void) cond;
(void) mutex;
#else
pthread_cond_wait(cond, mutex);
#endif
}
void crSignalCondition(CRcondition *cond)
{
#ifdef WINDOWS
/* XXX fix me */
(void) cond;
#else
pthread_cond_signal(cond);
#endif
}
void crInitBarrier(CRbarrier *b, unsigned int count)
{
#ifdef WINDOWS
unsigned int i;
for (i = 0; i < count; i++)
b->hEvents[i] = CreateEvent(NULL, FALSE, FALSE, NULL);
#else
b->count = count;
b->waiting = 0;
pthread_cond_init( &(b->cond), NULL );
pthread_mutex_init( &(b->mutex), NULL );
#endif
}
void crFreeBarrier(CRbarrier *b)
{
/* XXX anything to do? */
}
void crWaitBarrier(CRbarrier *b)
{
#ifdef WINDOWS
DWORD dwEvent
= WaitForMultipleObjects( b->count, b->hEvents, FALSE, INFINITE );
#else
pthread_mutex_lock( &(b->mutex) );
b->waiting++;
if (b->waiting < b->count) {
pthread_cond_wait( &(b->cond), &(b->mutex) );
}
else {
pthread_cond_broadcast( &(b->cond) );
b->waiting = 0;
}
pthread_mutex_unlock( &(b->mutex) );
#endif
}
void crInitSemaphore(CRsemaphore *s, unsigned int count)
{
#ifdef WINDOWS
crWarning("CRsemaphore functions not implemented on Windows");
#else
sem_init(s, 0, count);
#endif
}
void crWaitSemaphore(CRsemaphore *s)
{
#ifdef WINDOWS
/* to do */
#else
sem_wait(s);
#endif
}
void crSignalSemaphore(CRsemaphore *s)
{
#ifdef WINDOWS
/* to do */
#else
sem_post(s);
#endif
}