sl@0: /* sl@0: * tclUnixThrd.c -- sl@0: * sl@0: * This file implements the UNIX-specific thread support. sl@0: * sl@0: * Copyright (c) 1991-1994 The Regents of the University of California. sl@0: * Copyright (c) 1994-1997 Sun Microsystems, Inc. sl@0: * Portions Copyright (c) 2007-2008 Nokia Corporation and/or its subsidiaries. All rights reserved. sl@0: * sl@0: * See the file "license.terms" for information on usage and redistribution sl@0: * of this file, and for a DISCLAIMER OF ALL WARRANTIES. sl@0: * sl@0: * SCCS: @(#) tclUnixThrd.c 1.18 98/02/19 14:24:12 sl@0: */ sl@0: sl@0: #include "tclInt.h" sl@0: #include "tclPort.h" sl@0: sl@0: #ifdef TCL_THREADS sl@0: sl@0: #include "pthread.h" sl@0: sl@0: typedef struct ThreadSpecificData { sl@0: char nabuf[16]; sl@0: } ThreadSpecificData; sl@0: sl@0: static Tcl_ThreadDataKey dataKey; sl@0: sl@0: /* sl@0: * masterLock is used to serialize creation of mutexes, condition sl@0: * variables, and thread local storage. sl@0: * This is the only place that can count on the ability to statically sl@0: * initialize the mutex. sl@0: */ sl@0: sl@0: static pthread_mutex_t masterLock = PTHREAD_MUTEX_INITIALIZER; sl@0: sl@0: /* sl@0: * initLock is used to serialize initialization and finalization sl@0: * of Tcl. It cannot use any dyamically allocated storage. sl@0: */ sl@0: sl@0: static pthread_mutex_t initLock = PTHREAD_MUTEX_INITIALIZER; sl@0: sl@0: /* sl@0: * allocLock is used by Tcl's version of malloc for synchronization. sl@0: * For obvious reasons, cannot use any dyamically allocated storage. sl@0: */ sl@0: sl@0: static pthread_mutex_t allocLock = PTHREAD_MUTEX_INITIALIZER; sl@0: static pthread_mutex_t *allocLockPtr = &allocLock; sl@0: sl@0: /* sl@0: * These are for the critical sections inside this file. sl@0: */ sl@0: sl@0: #define MASTER_LOCK pthread_mutex_lock(&masterLock) sl@0: #define MASTER_UNLOCK pthread_mutex_unlock(&masterLock) sl@0: sl@0: #endif /* TCL_THREADS */ sl@0: sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * TclpThreadCreate -- sl@0: * sl@0: * This procedure creates a new thread. sl@0: * sl@0: * Results: sl@0: * TCL_OK if the thread could be created. The thread ID is sl@0: * returned in a parameter. sl@0: * sl@0: * Side effects: sl@0: * A new thread is created. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: int sl@0: TclpThreadCreate(idPtr, proc, clientData, stackSize, flags) sl@0: Tcl_ThreadId *idPtr; /* Return, the ID of the thread */ sl@0: Tcl_ThreadCreateProc proc; /* Main() function of the thread */ sl@0: ClientData clientData; /* The one argument to Main() */ sl@0: int stackSize; /* Size of stack for the new thread */ sl@0: int flags; /* Flags controlling behaviour of sl@0: * the new thread */ sl@0: { sl@0: #ifdef TCL_THREADS sl@0: pthread_attr_t attr; sl@0: pthread_t theThread; sl@0: int result; sl@0: sl@0: pthread_attr_init(&attr); sl@0: pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM); sl@0: sl@0: #ifdef HAVE_PTHREAD_ATTR_SETSTACKSIZE sl@0: if (stackSize != TCL_THREAD_STACK_DEFAULT) { sl@0: pthread_attr_setstacksize(&attr, (size_t) stackSize); sl@0: #ifdef TCL_THREAD_STACK_MIN sl@0: } else { sl@0: /* sl@0: * Certain systems define a thread stack size that by default is sl@0: * too small for many operations. The user has the option of sl@0: * defining TCL_THREAD_STACK_MIN to a value large enough to work sl@0: * for their needs. This would look like (for 128K min stack): sl@0: * make MEM_DEBUG_FLAGS=-DTCL_THREAD_STACK_MIN=131072L sl@0: * sl@0: * This solution is not optimal, as we should allow the user to sl@0: * specify a size at runtime, but we don't want to slow this function sl@0: * down, and that would still leave the main thread at the default. sl@0: */ sl@0: sl@0: size_t size; sl@0: result = pthread_attr_getstacksize(&attr, &size); sl@0: if (!result && (size < TCL_THREAD_STACK_MIN)) { sl@0: pthread_attr_setstacksize(&attr, (size_t) TCL_THREAD_STACK_MIN); sl@0: } sl@0: #endif sl@0: } sl@0: #endif sl@0: if (! (flags & TCL_THREAD_JOINABLE)) { sl@0: pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED); sl@0: } sl@0: sl@0: sl@0: if (pthread_create(&theThread, &attr, sl@0: (void * (*)())proc, (void *)clientData) && sl@0: pthread_create(&theThread, NULL, sl@0: (void * (*)())proc, (void *)clientData)) { sl@0: result = TCL_ERROR; sl@0: } else { sl@0: *idPtr = (Tcl_ThreadId)theThread; sl@0: result = TCL_OK; sl@0: } sl@0: pthread_attr_destroy(&attr); sl@0: return result; sl@0: #else sl@0: return TCL_ERROR; sl@0: #endif /* TCL_THREADS */ sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * Tcl_JoinThread -- sl@0: * sl@0: * This procedure waits upon the exit of the specified thread. sl@0: * sl@0: * Results: sl@0: * TCL_OK if the wait was successful, TCL_ERROR else. sl@0: * sl@0: * Side effects: sl@0: * The result area is set to the exit code of the thread we sl@0: * waited upon. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: EXPORT_C int sl@0: Tcl_JoinThread(threadId, state) sl@0: Tcl_ThreadId threadId; /* Id of the thread to wait upon */ sl@0: int* state; /* Reference to the storage the result sl@0: * of the thread we wait upon will be sl@0: * written into. */ sl@0: { sl@0: #ifdef TCL_THREADS sl@0: int result; sl@0: sl@0: result = pthread_join ((pthread_t) threadId, (VOID**) state); sl@0: return (result == 0) ? TCL_OK : TCL_ERROR; sl@0: #else sl@0: return TCL_ERROR; sl@0: #endif sl@0: } sl@0: sl@0: #ifdef TCL_THREADS sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * TclpThreadExit -- sl@0: * sl@0: * This procedure terminates the current thread. sl@0: * sl@0: * Results: sl@0: * None. sl@0: * sl@0: * Side effects: sl@0: * This procedure terminates the current thread. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: void sl@0: TclpThreadExit(status) sl@0: int status; sl@0: { sl@0: pthread_exit((VOID *)status); sl@0: } sl@0: #endif /* TCL_THREADS */ sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * Tcl_GetCurrentThread -- sl@0: * sl@0: * This procedure returns the ID of the currently running thread. sl@0: * sl@0: * Results: sl@0: * A thread ID. sl@0: * sl@0: * Side effects: sl@0: * None. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: EXPORT_C Tcl_ThreadId sl@0: Tcl_GetCurrentThread() sl@0: { sl@0: #ifdef TCL_THREADS sl@0: return (Tcl_ThreadId) pthread_self(); sl@0: #else sl@0: return (Tcl_ThreadId) 0; sl@0: #endif sl@0: } sl@0: sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * TclpInitLock sl@0: * sl@0: * This procedure is used to grab a lock that serializes initialization sl@0: * and finalization of Tcl. On some platforms this may also initialize sl@0: * the mutex used to serialize creation of more mutexes and thread sl@0: * local storage keys. sl@0: * sl@0: * Results: sl@0: * None. sl@0: * sl@0: * Side effects: sl@0: * Acquire the initialization mutex. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: void sl@0: TclpInitLock() sl@0: { sl@0: #ifdef TCL_THREADS sl@0: pthread_mutex_lock(&initLock); sl@0: #endif sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * TclpFinalizeLock sl@0: * sl@0: * This procedure is used to destroy all private resources used in sl@0: * this file. sl@0: * sl@0: * Results: sl@0: * None. sl@0: * sl@0: * Side effects: sl@0: * Destroys everything private. TclpInitLock must be held sl@0: * entering this function. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: void sl@0: TclFinalizeLock () sl@0: { sl@0: #ifdef TCL_THREADS sl@0: /* sl@0: * You do not need to destroy mutexes that were created with the sl@0: * PTHREAD_MUTEX_INITIALIZER macro. These mutexes do not need sl@0: * any destruction: masterLock, allocLock, and initLock. sl@0: */ sl@0: pthread_mutex_unlock(&initLock); sl@0: #endif sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * TclpInitUnlock sl@0: * sl@0: * This procedure is used to release a lock that serializes initialization sl@0: * and finalization of Tcl. sl@0: * sl@0: * Results: sl@0: * None. sl@0: * sl@0: * Side effects: sl@0: * Release the initialization mutex. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: void sl@0: TclpInitUnlock() sl@0: { sl@0: #ifdef TCL_THREADS sl@0: pthread_mutex_unlock(&initLock); sl@0: #endif sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * TclpMasterLock sl@0: * sl@0: * This procedure is used to grab a lock that serializes creation sl@0: * and finalization of serialization objects. This interface is sl@0: * only needed in finalization; it is hidden during sl@0: * creation of the objects. sl@0: * sl@0: * This lock must be different than the initLock because the sl@0: * initLock is held during creation of syncronization objects. sl@0: * sl@0: * Results: sl@0: * None. sl@0: * sl@0: * Side effects: sl@0: * Acquire the master mutex. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: void sl@0: TclpMasterLock() sl@0: { sl@0: #ifdef TCL_THREADS sl@0: pthread_mutex_lock(&masterLock); sl@0: #endif sl@0: } sl@0: sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * TclpMasterUnlock sl@0: * sl@0: * This procedure is used to release a lock that serializes creation sl@0: * and finalization of synchronization objects. sl@0: * sl@0: * Results: sl@0: * None. sl@0: * sl@0: * Side effects: sl@0: * Release the master mutex. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: void sl@0: TclpMasterUnlock() sl@0: { sl@0: #ifdef TCL_THREADS sl@0: pthread_mutex_unlock(&masterLock); sl@0: #endif sl@0: } sl@0: sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * Tcl_GetAllocMutex sl@0: * sl@0: * This procedure returns a pointer to a statically initialized sl@0: * mutex for use by the memory allocator. The alloctor must sl@0: * use this lock, because all other locks are allocated... sl@0: * sl@0: * Results: sl@0: * A pointer to a mutex that is suitable for passing to sl@0: * Tcl_MutexLock and Tcl_MutexUnlock. sl@0: * sl@0: * Side effects: sl@0: * None. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: EXPORT_C Tcl_Mutex * sl@0: Tcl_GetAllocMutex() sl@0: { sl@0: #ifdef TCL_THREADS sl@0: return (Tcl_Mutex *)&allocLockPtr; sl@0: #else sl@0: return NULL; sl@0: #endif sl@0: } sl@0: sl@0: #ifdef TCL_THREADS sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * Tcl_MutexLock -- sl@0: * sl@0: * This procedure is invoked to lock a mutex. This procedure sl@0: * handles initializing the mutex, if necessary. The caller sl@0: * can rely on the fact that Tcl_Mutex is an opaque pointer. sl@0: * This routine will change that pointer from NULL after first use. sl@0: * sl@0: * Results: sl@0: * None. sl@0: * sl@0: * Side effects: sl@0: * May block the current thread. The mutex is aquired when sl@0: * this returns. Will allocate memory for a pthread_mutex_t sl@0: * and initialize this the first time this Tcl_Mutex is used. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: EXPORT_C void sl@0: Tcl_MutexLock(mutexPtr) sl@0: Tcl_Mutex *mutexPtr; /* Really (pthread_mutex_t **) */ sl@0: { sl@0: pthread_mutex_t *pmutexPtr; sl@0: if (*mutexPtr == NULL) { sl@0: MASTER_LOCK; sl@0: if (*mutexPtr == NULL) { sl@0: /* sl@0: * Double inside master lock check to avoid a race condition. sl@0: */ sl@0: sl@0: pmutexPtr = (pthread_mutex_t *)ckalloc(sizeof(pthread_mutex_t)); sl@0: pthread_mutex_init(pmutexPtr, NULL); sl@0: *mutexPtr = (Tcl_Mutex)pmutexPtr; sl@0: TclRememberMutex(mutexPtr); sl@0: } sl@0: MASTER_UNLOCK; sl@0: } sl@0: pmutexPtr = *((pthread_mutex_t **)mutexPtr); sl@0: pthread_mutex_lock(pmutexPtr); sl@0: } sl@0: sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * Tcl_MutexUnlock -- sl@0: * sl@0: * This procedure is invoked to unlock a mutex. The mutex must sl@0: * have been locked by Tcl_MutexLock. sl@0: * sl@0: * Results: sl@0: * None. sl@0: * sl@0: * Side effects: sl@0: * The mutex is released when this returns. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: EXPORT_C void sl@0: Tcl_MutexUnlock(mutexPtr) sl@0: Tcl_Mutex *mutexPtr; /* Really (pthread_mutex_t **) */ sl@0: { sl@0: pthread_mutex_t *pmutexPtr = *(pthread_mutex_t **)mutexPtr; sl@0: pthread_mutex_unlock(pmutexPtr); sl@0: } sl@0: sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * TclpFinalizeMutex -- sl@0: * sl@0: * This procedure is invoked to clean up one mutex. This is only sl@0: * safe to call at the end of time. sl@0: * sl@0: * This assumes the Master Lock is held. sl@0: * sl@0: * Results: sl@0: * None. sl@0: * sl@0: * Side effects: sl@0: * The mutex list is deallocated. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: void sl@0: TclpFinalizeMutex(mutexPtr) sl@0: Tcl_Mutex *mutexPtr; sl@0: { sl@0: pthread_mutex_t *pmutexPtr = *(pthread_mutex_t **)mutexPtr; sl@0: if (pmutexPtr != NULL) { sl@0: pthread_mutex_destroy(pmutexPtr); sl@0: ckfree((char *)pmutexPtr); sl@0: *mutexPtr = NULL; sl@0: } sl@0: } sl@0: sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * TclpThreadDataKeyInit -- sl@0: * sl@0: * This procedure initializes a thread specific data block key. sl@0: * Each thread has table of pointers to thread specific data. sl@0: * all threads agree on which table entry is used by each module. sl@0: * this is remembered in a "data key", that is just an index into sl@0: * this table. To allow self initialization, the interface sl@0: * passes a pointer to this key and the first thread to use sl@0: * the key fills in the pointer to the key. The key should be sl@0: * a process-wide static. sl@0: * sl@0: * Results: sl@0: * None. sl@0: * sl@0: * Side effects: sl@0: * Will allocate memory the first time this process calls for sl@0: * this key. In this case it modifies its argument sl@0: * to hold the pointer to information about the key. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: void sl@0: TclpThreadDataKeyInit(keyPtr) sl@0: Tcl_ThreadDataKey *keyPtr; /* Identifier for the data chunk, sl@0: * really (pthread_key_t **) */ sl@0: { sl@0: pthread_key_t *pkeyPtr; sl@0: sl@0: MASTER_LOCK; sl@0: if (*keyPtr == NULL) { sl@0: pkeyPtr = (pthread_key_t *)ckalloc(sizeof(pthread_key_t)); sl@0: pthread_key_create(pkeyPtr, NULL); sl@0: *keyPtr = (Tcl_ThreadDataKey)pkeyPtr; sl@0: TclRememberDataKey(keyPtr); sl@0: } sl@0: MASTER_UNLOCK; sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * TclpThreadDataKeyGet -- sl@0: * sl@0: * This procedure returns a pointer to a block of thread local storage. sl@0: * sl@0: * Results: sl@0: * A thread-specific pointer to the data structure, or NULL sl@0: * if the memory has not been assigned to this key for this thread. sl@0: * sl@0: * Side effects: sl@0: * None. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: VOID * sl@0: TclpThreadDataKeyGet(keyPtr) sl@0: Tcl_ThreadDataKey *keyPtr; /* Identifier for the data chunk, sl@0: * really (pthread_key_t **) */ sl@0: { sl@0: pthread_key_t *pkeyPtr = *(pthread_key_t **)keyPtr; sl@0: if (pkeyPtr == NULL) { sl@0: return NULL; sl@0: } else { sl@0: return (VOID *)pthread_getspecific(*pkeyPtr); sl@0: } sl@0: } sl@0: sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * TclpThreadDataKeySet -- sl@0: * sl@0: * This procedure sets the pointer to a block of thread local storage. sl@0: * sl@0: * Results: sl@0: * None. sl@0: * sl@0: * Side effects: sl@0: * Sets up the thread so future calls to TclpThreadDataKeyGet with sl@0: * this key will return the data pointer. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: void sl@0: TclpThreadDataKeySet(keyPtr, data) sl@0: Tcl_ThreadDataKey *keyPtr; /* Identifier for the data chunk, sl@0: * really (pthread_key_t **) */ sl@0: VOID *data; /* Thread local storage */ sl@0: { sl@0: pthread_key_t *pkeyPtr = *(pthread_key_t **)keyPtr; sl@0: pthread_setspecific(*pkeyPtr, data); sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * TclpFinalizeThreadData -- sl@0: * sl@0: * This procedure cleans up the thread-local storage. This is sl@0: * called once for each thread. sl@0: * sl@0: * Results: sl@0: * None. sl@0: * sl@0: * Side effects: sl@0: * Frees up all thread local storage. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: void sl@0: TclpFinalizeThreadData(keyPtr) sl@0: Tcl_ThreadDataKey *keyPtr; sl@0: { sl@0: VOID *result; sl@0: pthread_key_t *pkeyPtr; sl@0: sl@0: if (*keyPtr != NULL) { sl@0: pkeyPtr = *(pthread_key_t **)keyPtr; sl@0: result = (VOID *)pthread_getspecific(*pkeyPtr); sl@0: if (result != NULL) { sl@0: ckfree((char *)result); sl@0: pthread_setspecific(*pkeyPtr, (void *)NULL); sl@0: } sl@0: } sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * TclpFinalizeThreadDataKey -- sl@0: * sl@0: * This procedure is invoked to clean up one key. This is a sl@0: * process-wide storage identifier. The thread finalization code sl@0: * cleans up the thread local storage itself. sl@0: * sl@0: * This assumes the master lock is held. sl@0: * sl@0: * Results: sl@0: * None. sl@0: * sl@0: * Side effects: sl@0: * The key is deallocated. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: void sl@0: TclpFinalizeThreadDataKey(keyPtr) sl@0: Tcl_ThreadDataKey *keyPtr; sl@0: { sl@0: pthread_key_t *pkeyPtr; sl@0: if (*keyPtr != NULL) { sl@0: pkeyPtr = *(pthread_key_t **)keyPtr; sl@0: pthread_key_delete(*pkeyPtr); sl@0: ckfree((char *)pkeyPtr); sl@0: *keyPtr = NULL; sl@0: } sl@0: } sl@0: sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * Tcl_ConditionWait -- sl@0: * sl@0: * This procedure is invoked to wait on a condition variable. sl@0: * The mutex is automically released as part of the wait, and sl@0: * automatically grabbed when the condition is signaled. sl@0: * sl@0: * The mutex must be held when this procedure is called. sl@0: * sl@0: * Results: sl@0: * None. sl@0: * sl@0: * Side effects: sl@0: * May block the current thread. The mutex is aquired when sl@0: * this returns. Will allocate memory for a pthread_mutex_t sl@0: * and initialize this the first time this Tcl_Mutex is used. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: EXPORT_C void sl@0: Tcl_ConditionWait(condPtr, mutexPtr, timePtr) sl@0: Tcl_Condition *condPtr; /* Really (pthread_cond_t **) */ sl@0: Tcl_Mutex *mutexPtr; /* Really (pthread_mutex_t **) */ sl@0: Tcl_Time *timePtr; /* Timeout on waiting period */ sl@0: { sl@0: pthread_cond_t *pcondPtr; sl@0: pthread_mutex_t *pmutexPtr; sl@0: struct timespec ptime; sl@0: sl@0: if (*condPtr == NULL) { sl@0: MASTER_LOCK; sl@0: sl@0: /* sl@0: * Double check inside mutex to avoid race, sl@0: * then initialize condition variable if necessary. sl@0: */ sl@0: sl@0: if (*condPtr == NULL) { sl@0: pcondPtr = (pthread_cond_t *)ckalloc(sizeof(pthread_cond_t)); sl@0: pthread_cond_init(pcondPtr, NULL); sl@0: *condPtr = (Tcl_Condition)pcondPtr; sl@0: TclRememberCondition(condPtr); sl@0: } sl@0: MASTER_UNLOCK; sl@0: } sl@0: pmutexPtr = *((pthread_mutex_t **)mutexPtr); sl@0: pcondPtr = *((pthread_cond_t **)condPtr); sl@0: if (timePtr == NULL) { sl@0: pthread_cond_wait(pcondPtr, pmutexPtr); sl@0: } else { sl@0: Tcl_Time now; sl@0: sl@0: /* sl@0: * Make sure to take into account the microsecond component of the sl@0: * current time, including possible overflow situations. [Bug #411603] sl@0: */ sl@0: sl@0: Tcl_GetTime(&now); sl@0: ptime.tv_sec = timePtr->sec + now.sec + sl@0: (timePtr->usec + now.usec) / 1000000; sl@0: ptime.tv_nsec = 1000 * ((timePtr->usec + now.usec) % 1000000); sl@0: pthread_cond_timedwait(pcondPtr, pmutexPtr, &ptime); sl@0: } sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * Tcl_ConditionNotify -- sl@0: * sl@0: * This procedure is invoked to signal a condition variable. sl@0: * sl@0: * The mutex must be held during this call to avoid races, sl@0: * but this interface does not enforce that. sl@0: * sl@0: * Results: sl@0: * None. sl@0: * sl@0: * Side effects: sl@0: * May unblock another thread. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: EXPORT_C void sl@0: Tcl_ConditionNotify(condPtr) sl@0: Tcl_Condition *condPtr; sl@0: { sl@0: pthread_cond_t *pcondPtr = *((pthread_cond_t **)condPtr); sl@0: if (pcondPtr != NULL) { sl@0: pthread_cond_broadcast(pcondPtr); sl@0: } else { sl@0: /* sl@0: * Noone has used the condition variable, so there are no waiters. sl@0: */ sl@0: } sl@0: } sl@0: sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * TclpFinalizeCondition -- sl@0: * sl@0: * This procedure is invoked to clean up a condition variable. sl@0: * This is only safe to call at the end of time. sl@0: * sl@0: * This assumes the Master Lock is held. sl@0: * sl@0: * Results: sl@0: * None. sl@0: * sl@0: * Side effects: sl@0: * The condition variable is deallocated. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: void sl@0: TclpFinalizeCondition(condPtr) sl@0: Tcl_Condition *condPtr; sl@0: { sl@0: pthread_cond_t *pcondPtr = *(pthread_cond_t **)condPtr; sl@0: if (pcondPtr != NULL) { sl@0: pthread_cond_destroy(pcondPtr); sl@0: ckfree((char *)pcondPtr); sl@0: *condPtr = NULL; sl@0: } sl@0: } sl@0: #endif /* TCL_THREADS */ sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * TclpReaddir, TclpLocaltime, TclpGmtime, TclpInetNtoa -- sl@0: * sl@0: * These procedures replace core C versions to be used in a sl@0: * threaded environment. sl@0: * sl@0: * Results: sl@0: * See documentation of C functions. sl@0: * sl@0: * Side effects: sl@0: * See documentation of C functions. sl@0: * sl@0: * Notes: sl@0: * TclpReaddir is no longer used by the core (see 1095909), sl@0: * but it appears in the internal stubs table (see #589526). sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: Tcl_DirEntry * sl@0: TclpReaddir(DIR * dir) sl@0: { sl@0: return TclOSreaddir(dir); sl@0: } sl@0: sl@0: char * sl@0: TclpInetNtoa(struct in_addr addr) sl@0: { sl@0: #ifdef TCL_THREADS sl@0: ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); sl@0: unsigned char *b = (unsigned char*) &addr.s_addr; sl@0: sl@0: sprintf(tsdPtr->nabuf, "%u.%u.%u.%u", b[0], b[1], b[2], b[3]); sl@0: return tsdPtr->nabuf; sl@0: #else sl@0: return inet_ntoa(addr); sl@0: #endif sl@0: } sl@0: sl@0: #if defined(TCL_THREADS) && defined(USE_THREAD_ALLOC) && !defined(TCL_MEM_DEBUG) sl@0: /* sl@0: * Additions by AOL for specialized thread memory allocator. sl@0: */ sl@0: #ifdef USE_THREAD_ALLOC sl@0: static volatile int initialized = 0; sl@0: static pthread_key_t key; sl@0: sl@0: typedef struct allocMutex { sl@0: Tcl_Mutex tlock; sl@0: pthread_mutex_t plock; sl@0: } allocMutex; sl@0: sl@0: Tcl_Mutex * sl@0: TclpNewAllocMutex(void) sl@0: { sl@0: struct allocMutex *lockPtr; sl@0: sl@0: lockPtr = malloc(sizeof(struct allocMutex)); sl@0: if (lockPtr == NULL) { sl@0: panic("could not allocate lock"); sl@0: } sl@0: lockPtr->tlock = (Tcl_Mutex) &lockPtr->plock; sl@0: pthread_mutex_init(&lockPtr->plock, NULL); sl@0: return &lockPtr->tlock; sl@0: } sl@0: sl@0: void sl@0: TclpFreeAllocMutex(mutex) sl@0: Tcl_Mutex *mutex; /* The alloc mutex to free. */ sl@0: { sl@0: allocMutex* lockPtr = (allocMutex*) mutex; sl@0: if (!lockPtr) return; sl@0: pthread_mutex_destroy(&lockPtr->plock); sl@0: free(lockPtr); sl@0: } sl@0: sl@0: void TclpFreeAllocCache(ptr) sl@0: void *ptr; sl@0: { sl@0: if (ptr != NULL) { sl@0: /* sl@0: * Called by the pthread lib when a thread exits sl@0: */ sl@0: TclFreeAllocCache(ptr); sl@0: } else if (initialized) { sl@0: /* sl@0: * Called by us in TclFinalizeThreadAlloc() during sl@0: * the library finalization initiated from Tcl_Finalize() sl@0: */ sl@0: pthread_key_delete(key); sl@0: initialized = 0; sl@0: } sl@0: } sl@0: sl@0: void * sl@0: TclpGetAllocCache(void) sl@0: { sl@0: if (!initialized) { sl@0: pthread_mutex_lock(allocLockPtr); sl@0: if (!initialized) { sl@0: pthread_key_create(&key, TclpFreeAllocCache); sl@0: initialized = 1; sl@0: } sl@0: pthread_mutex_unlock(allocLockPtr); sl@0: } sl@0: return pthread_getspecific(key); sl@0: } sl@0: sl@0: void sl@0: TclpSetAllocCache(void *arg) sl@0: { sl@0: pthread_setspecific(key, arg); sl@0: } sl@0: sl@0: #endif /* USE_THREAD_ALLOC */ sl@0: #endif /* TCL_THREADS */