Logo Search packages:      
Sourcecode: tcl8.5 version File versions  Download package

tclThread.c

/*
 * tclThread.c --
 *
 *    This file implements Platform independent thread operations. Most of
 *    the real work is done in the platform dependent files.
 *
 * Copyright (c) 1998 by Sun Microsystems, Inc.
 *
 * See the file "license.terms" for information on usage and redistribution of
 * this file, and for a DISCLAIMER OF ALL WARRANTIES.
 *
 * RCS: @(#) $Id: tclThread.c,v 1.19 2007/12/13 15:23:20 dgp Exp $
 */

#include "tclInt.h"

/*
 * There are three classes of synchronization objects: mutexes, thread data
 * keys, and condition variables. The following are used to record the memory
 * used for these objects so they can be finalized.
 *
 * These statics are guarded by the mutex in the caller of
 * TclRememberThreadData, e.g., TclpThreadDataKeyInit
 */

typedef struct {
    int num;            /* Number of objects remembered */
    int max;            /* Max size of the array */
    char **list;  /* List of pointers */
} SyncObjRecord;

static SyncObjRecord keyRecord = {0, 0, NULL};
static SyncObjRecord mutexRecord = {0, 0, NULL};
static SyncObjRecord condRecord = {0, 0, NULL};

/*
 * Prototypes of functions used only in this file.
 */

static void       ForgetSyncObject(char *objPtr, SyncObjRecord *recPtr);
static void       RememberSyncObject(char *objPtr,
                      SyncObjRecord *recPtr);

/*
 * Several functions are #defined to nothing in tcl.h if TCL_THREADS is not
 * specified. Here we undo that so the functions are defined in the stubs
 * table.
 */

#ifndef TCL_THREADS
#undef Tcl_MutexLock
#undef Tcl_MutexUnlock
#undef Tcl_MutexFinalize
#undef Tcl_ConditionNotify
#undef Tcl_ConditionWait
#undef Tcl_ConditionFinalize
#endif

/*
 *----------------------------------------------------------------------
 *
 * Tcl_GetThreadData --
 *
 *    This function allocates and initializes a chunk of thread local
 *    storage.
 *
 * Results:
 *    A thread-specific pointer to the data structure.
 *
 * Side effects:
 *    Will allocate memory the first time this thread calls for this chunk
 *    of storage.
 *
 *----------------------------------------------------------------------
 */

void *
Tcl_GetThreadData(
    Tcl_ThreadDataKey *keyPtr,      /* Identifier for the data chunk */
    int size)                 /* Size of storage block */
{
    void *result;
#ifdef TCL_THREADS
    /*
     * Initialize the key for this thread.
     */
    result = TclpThreadDataKeyGet(keyPtr);

    if (result == NULL) {
      result = ckalloc((size_t) size);
      memset(result, 0, (size_t) size);
      TclpThreadDataKeySet(keyPtr, result);
    }
#else /* TCL_THREADS */
    if (*keyPtr == NULL) {
      result = ckalloc((size_t) size);
      memset(result, 0, (size_t) size);
      *keyPtr = (Tcl_ThreadDataKey)result;
      RememberSyncObject((char *) keyPtr, &keyRecord);
    }
    result = * (void **) keyPtr;
#endif /* TCL_THREADS */
    return result;
}

/*
 *----------------------------------------------------------------------
 *
 * TclThreadDataKeyGet --
 *
 *    This function returns a pointer to a block of thread local storage.
 *
 * Results:
 *    A thread-specific pointer to the data structure, or NULL if the memory
 *    has not been assigned to this key for this thread.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------
 */

void *
TclThreadDataKeyGet(
    Tcl_ThreadDataKey *keyPtr)      /* Identifier for the data chunk, really
                         * (pthread_key_t **) */
{
#ifdef TCL_THREADS
    return TclpThreadDataKeyGet(keyPtr);
#else /* TCL_THREADS */
    char *result = *(char **) keyPtr;
    return result;
#endif /* TCL_THREADS */
}


/*
 *----------------------------------------------------------------------
 *
 * RememberSyncObject
 *
 *    Keep a list of (mutexes/condition variable/data key) used during
 *    finalization.
 *
 *    Assume master lock is held.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    Add to the appropriate list.
 *
 *----------------------------------------------------------------------
 */

static void
RememberSyncObject(
    char *objPtr,       /* Pointer to sync object */
    SyncObjRecord *recPtr)    /* Record of sync objects */
{
    char **newList;
    int i, j;


    /*
     * Reuse any free slot in the list.
     */

    for (i=0 ; i < recPtr->num ; ++i) {
      if (recPtr->list[i] == NULL) {
          recPtr->list[i] = objPtr;
          return;
      }
    }

    /*
     * Grow the list of pointers if necessary, copying only non-NULL
     * pointers to the new list.
     */

    if (recPtr->num >= recPtr->max) {
      recPtr->max += 8;
      newList = (char **) ckalloc(recPtr->max * sizeof(char *));
      for (i=0,j=0 ; i<recPtr->num ; i++) {
          if (recPtr->list[i] != NULL) {
            newList[j++] = recPtr->list[i];
          }
      }
      if (recPtr->list != NULL) {
          ckfree((char *) recPtr->list);
      }
      recPtr->list = newList;
      recPtr->num = j;
    }

    recPtr->list[recPtr->num] = objPtr;
    recPtr->num++;
}

/*
 *----------------------------------------------------------------------
 *
 * ForgetSyncObject
 *
 *    Remove a single object from the list.
 *    Assume master lock is held.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    Remove from the appropriate list.
 *
 *----------------------------------------------------------------------
 */

static void
ForgetSyncObject(
    char *objPtr,       /* Pointer to sync object */
    SyncObjRecord *recPtr)    /* Record of sync objects */
{
    int i;

    for (i=0 ; i<recPtr->num ; i++) {
      if (objPtr == recPtr->list[i]) {
          recPtr->list[i] = NULL;
          return;
      }
    }
}

/*
 *----------------------------------------------------------------------
 *
 * TclRememberMutex
 *
 *    Keep a list of mutexes used during finalization.
 *    Assume master lock is held.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    Add to the mutex list.
 *
 *----------------------------------------------------------------------
 */

void
TclRememberMutex(
    Tcl_Mutex *mutexPtr)
{
    RememberSyncObject((char *)mutexPtr, &mutexRecord);
}

/*
 *----------------------------------------------------------------------
 *
 * Tcl_MutexFinalize --
 *
 *    Finalize a single mutex and remove it from the list of remembered
 *    objects.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    Remove the mutex from the list.
 *
 *----------------------------------------------------------------------
 */

void
Tcl_MutexFinalize(
    Tcl_Mutex *mutexPtr)
{
#ifdef TCL_THREADS
    TclpFinalizeMutex(mutexPtr);
#endif
    TclpMasterLock();
    ForgetSyncObject((char *) mutexPtr, &mutexRecord);
    TclpMasterUnlock();
}

/*
 *----------------------------------------------------------------------
 *
 * TclRememberCondition
 *
 *    Keep a list of condition variables used during finalization.
 *    Assume master lock is held.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    Add to the condition variable list.
 *
 *----------------------------------------------------------------------
 */

void
TclRememberCondition(
    Tcl_Condition *condPtr)
{
    RememberSyncObject((char *) condPtr, &condRecord);
}

/*
 *----------------------------------------------------------------------
 *
 * Tcl_ConditionFinalize --
 *
 *    Finalize a single condition variable and remove it from the list of
 *    remembered objects.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    Remove the condition variable from the list.
 *
 *----------------------------------------------------------------------
 */

void
Tcl_ConditionFinalize(
    Tcl_Condition *condPtr)
{
#ifdef TCL_THREADS
    TclpFinalizeCondition(condPtr);
#endif
    TclpMasterLock();
    ForgetSyncObject((char *) condPtr, &condRecord);
    TclpMasterUnlock();
}

/*
 *----------------------------------------------------------------------
 *
 * TclFinalizeThreadData --
 *
 *    This function cleans up the thread-local storage. This is called once
 *    for each thread.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    Frees up all thread local storage.
 *
 *----------------------------------------------------------------------
 */

void
TclFinalizeThreadData(void)
{
    TclpFinalizeThreadDataThread();
}

/*
 *----------------------------------------------------------------------
 *
 * TclFinalizeSynchronization --
 *
 *    This function cleans up all synchronization objects: mutexes,
 *    condition variables, and thread-local storage.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    Frees up the memory.
 *
 *----------------------------------------------------------------------
 */

void
TclFinalizeSynchronization(void)
{
    int i;
    void *blockPtr;
    Tcl_ThreadDataKey *keyPtr;
#ifdef TCL_THREADS
    Tcl_Mutex *mutexPtr;
    Tcl_Condition *condPtr;

    TclpMasterLock();
#endif

    /*
     * If we're running unthreaded, the TSD blocks are simply stored inside
     * their thread data keys. Free them here.
     */

    if (keyRecord.list != NULL) {
      for (i=0 ; i<keyRecord.num ; i++) {
          keyPtr = (Tcl_ThreadDataKey *) keyRecord.list[i];
          blockPtr = (void *) *keyPtr;
          ckfree(blockPtr);
      }
      ckfree((char *) keyRecord.list);
      keyRecord.list = NULL;
    }
    keyRecord.max = 0;
    keyRecord.num = 0;
    
#ifdef TCL_THREADS
    /*
     * Call thread storage master cleanup.
     */

    TclFinalizeThreadStorage();

    for (i=0 ; i<mutexRecord.num ; i++) {
      mutexPtr = (Tcl_Mutex *)mutexRecord.list[i];
      if (mutexPtr != NULL) {
          TclpFinalizeMutex(mutexPtr);
      }
    }
    if (mutexRecord.list != NULL) {
      ckfree((char *) mutexRecord.list);
      mutexRecord.list = NULL;
    }
    mutexRecord.max = 0;
    mutexRecord.num = 0;

    for (i=0 ; i<condRecord.num ; i++) {
      condPtr = (Tcl_Condition *) condRecord.list[i];
      if (condPtr != NULL) {
          TclpFinalizeCondition(condPtr);
      }
    }
    if (condRecord.list != NULL) {
      ckfree((char *) condRecord.list);
      condRecord.list = NULL;
    }
    condRecord.max = 0;
    condRecord.num = 0;

    TclpMasterUnlock();
#endif /* TCL_THREADS */
}

/*
 *----------------------------------------------------------------------
 *
 * Tcl_ExitThread --
 *
 *    This function is called to terminate the current thread. This should
 *    be used by extensions that create threads with additional interpreters
 *    in them.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    All thread exit handlers are invoked, then the thread dies.
 *
 *----------------------------------------------------------------------
 */

void
Tcl_ExitThread(
    int status)
{
    Tcl_FinalizeThread();
#ifdef TCL_THREADS
    TclpThreadExit(status);
#endif
}

#ifndef TCL_THREADS

/*
 *----------------------------------------------------------------------
 *
 * Tcl_ConditionWait, et al. --
 *
 *    These noop functions are provided so the stub table does not have to
 *    be conditionalized for threads. The real implementations of these
 *    functions live in the platform specific files.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------
 */

#undef Tcl_ConditionWait
void
Tcl_ConditionWait(
    Tcl_Condition *condPtr,   /* Really (pthread_cond_t **) */
    Tcl_Mutex *mutexPtr,      /* Really (pthread_mutex_t **) */
    Tcl_Time *timePtr)        /* Timeout on waiting period */
{
}

#undef Tcl_ConditionNotify
void
Tcl_ConditionNotify(
    Tcl_Condition *condPtr)
{
}

#undef Tcl_MutexLock
void
Tcl_MutexLock(
    Tcl_Mutex *mutexPtr)
{
}

#undef Tcl_MutexUnlock
void
Tcl_MutexUnlock(
    Tcl_Mutex *mutexPtr)
{
}
#endif /* !TCL_THREADS */

/*
 * Local Variables:
 * mode: c
 * c-basic-offset: 4
 * fill-column: 78
 * End:
 */

Generated by  Doxygen 1.6.0   Back to index