michael@0: /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /* michael@0: ** Thread Private Data michael@0: ** michael@0: ** There is an aribitrary limit on the number of keys that will be allocated michael@0: ** by the runtime. It's largish, so it is intended to be a sanity check, not michael@0: ** an impediment. michael@0: ** michael@0: ** There is a counter, initialized to zero and incremented every time a michael@0: ** client asks for a new key, that holds the high water mark for keys. All michael@0: ** threads logically have the same high water mark and are permitted to michael@0: ** ask for TPD up to that key value. michael@0: ** michael@0: ** The vector to hold the TPD are allocated when PR_SetThreadPrivate() is michael@0: ** called. The size of the vector will be some value greater than or equal michael@0: ** to the current high water mark. Each thread has its own TPD length and michael@0: ** vector. michael@0: ** michael@0: ** Threads that get private data for keys they have not set (or perhaps michael@0: ** don't even exist for that thread) get a NULL return. If the key is michael@0: ** beyond the high water mark, an error will be returned. michael@0: */ michael@0: michael@0: /* michael@0: ** As of this time, BeOS has its own TPD implementation. Integrating michael@0: ** this standard one is a TODO for anyone with a bit of spare time on michael@0: ** their hand. For now, we just #ifdef out this whole file and use michael@0: ** the routines in pr/src/btthreads/ michael@0: */ michael@0: michael@0: #ifndef XP_BEOS michael@0: michael@0: #include "primpl.h" michael@0: michael@0: #include michael@0: michael@0: #if defined(WIN95) michael@0: /* michael@0: ** Some local variables report warnings on Win95 because the code paths michael@0: ** using them are conditioned on HAVE_CUSTOME_USER_THREADS. michael@0: ** The pragma suppresses the warning. michael@0: ** michael@0: */ michael@0: #pragma warning(disable : 4101) michael@0: #endif michael@0: michael@0: #define _PR_TPD_LIMIT 128 /* arbitary limit on the TPD slots */ michael@0: static PRInt32 _pr_tpd_length = 0; /* current length of destructor vector */ michael@0: static PRInt32 _pr_tpd_highwater = 0; /* next TPD key to be assigned */ michael@0: static PRThreadPrivateDTOR *_pr_tpd_destructors = NULL; michael@0: /* the destructors are associated with michael@0: the keys, therefore asserting that michael@0: the TPD key depicts the data's 'type' */ michael@0: michael@0: /* michael@0: ** Initialize the thread private data manipulation michael@0: */ michael@0: void _PR_InitTPD(void) michael@0: { michael@0: _pr_tpd_destructors = (PRThreadPrivateDTOR*) michael@0: PR_CALLOC(_PR_TPD_LIMIT * sizeof(PRThreadPrivateDTOR*)); michael@0: PR_ASSERT(NULL != _pr_tpd_destructors); michael@0: _pr_tpd_length = _PR_TPD_LIMIT; michael@0: } michael@0: michael@0: /* michael@0: ** Clean up the thread private data manipulation michael@0: */ michael@0: void _PR_CleanupTPD(void) michael@0: { michael@0: } /* _PR_CleanupTPD */ michael@0: michael@0: /* michael@0: ** This routine returns a new index for per-thread-private data table. michael@0: ** The index is visible to all threads within a process. This index can michael@0: ** be used with the PR_SetThreadPrivate() and PR_GetThreadPrivate() routines michael@0: ** to save and retrieve data associated with the index for a thread. michael@0: ** michael@0: ** The index independently maintains specific values for each binding thread. michael@0: ** A thread can only get access to its own thread-specific-data. michael@0: ** michael@0: ** Upon a new index return the value associated with the index for all threads michael@0: ** is NULL, and upon thread creation the value associated with all indices for michael@0: ** that thread is NULL. michael@0: ** michael@0: ** "dtor" is the destructor function to invoke when the private michael@0: ** data is set or destroyed michael@0: ** michael@0: ** Returns PR_FAILURE if the total number of indices will exceed the maximun michael@0: ** allowed. michael@0: */ michael@0: michael@0: PR_IMPLEMENT(PRStatus) PR_NewThreadPrivateIndex( michael@0: PRUintn *newIndex, PRThreadPrivateDTOR dtor) michael@0: { michael@0: PRStatus rv; michael@0: PRInt32 index; michael@0: michael@0: if (!_pr_initialized) _PR_ImplicitInitialization(); michael@0: michael@0: PR_ASSERT(NULL != newIndex); michael@0: PR_ASSERT(NULL != _pr_tpd_destructors); michael@0: michael@0: index = PR_ATOMIC_INCREMENT(&_pr_tpd_highwater) - 1; /* allocate index */ michael@0: if (_PR_TPD_LIMIT <= index) michael@0: { michael@0: PR_SetError(PR_TPD_RANGE_ERROR, 0); michael@0: rv = PR_FAILURE; /* that's just wrong */ michael@0: } michael@0: else michael@0: { michael@0: _pr_tpd_destructors[index] = dtor; /* record destructor @index */ michael@0: *newIndex = (PRUintn)index; /* copy into client's location */ michael@0: rv = PR_SUCCESS; /* that's okay */ michael@0: } michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: /* michael@0: ** Define some per-thread-private data. michael@0: ** "index" is an index into the per-thread private data table michael@0: ** "priv" is the per-thread-private data michael@0: ** michael@0: ** If the per-thread private data table has a previously registered michael@0: ** destructor function and a non-NULL per-thread-private data value, michael@0: ** the destructor function is invoked. michael@0: ** michael@0: ** This can return PR_FAILURE if index is invalid (ie., beyond the current michael@0: ** high water mark) or memory is insufficient to allocate an exanded vector. michael@0: */ michael@0: michael@0: PR_IMPLEMENT(PRStatus) PR_SetThreadPrivate(PRUintn index, void *priv) michael@0: { michael@0: PRThread *self = PR_GetCurrentThread(); michael@0: michael@0: /* michael@0: ** The index being set might not have a sufficient vector in this michael@0: ** thread. But if the index has been allocated, it's okay to go michael@0: ** ahead and extend this one now. michael@0: */ michael@0: if ((index >= _PR_TPD_LIMIT) || (index >= _pr_tpd_highwater)) michael@0: { michael@0: PR_SetError(PR_TPD_RANGE_ERROR, 0); michael@0: return PR_FAILURE; michael@0: } michael@0: michael@0: PR_ASSERT(((NULL == self->privateData) && (0 == self->tpdLength)) michael@0: || ((NULL != self->privateData) && (0 != self->tpdLength))); michael@0: michael@0: if ((NULL == self->privateData) || (self->tpdLength <= index)) michael@0: { michael@0: void *extension = PR_CALLOC(_pr_tpd_length * sizeof(void*)); michael@0: if (NULL == extension) michael@0: { michael@0: PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); michael@0: return PR_FAILURE; michael@0: } michael@0: if (self->privateData) { michael@0: (void)memcpy( michael@0: extension, self->privateData, michael@0: self->tpdLength * sizeof(void*)); michael@0: PR_DELETE(self->privateData); michael@0: } michael@0: self->tpdLength = _pr_tpd_length; michael@0: self->privateData = (void**)extension; michael@0: } michael@0: /* michael@0: ** There wasn't much chance of having to call the destructor michael@0: ** unless the slot already existed. michael@0: */ michael@0: else if (self->privateData[index] && _pr_tpd_destructors[index]) michael@0: { michael@0: void *data = self->privateData[index]; michael@0: self->privateData[index] = NULL; michael@0: (*_pr_tpd_destructors[index])(data); michael@0: } michael@0: michael@0: PR_ASSERT(index < self->tpdLength); michael@0: self->privateData[index] = priv; michael@0: michael@0: return PR_SUCCESS; michael@0: } michael@0: michael@0: /* michael@0: ** Recover the per-thread-private data for the current thread. "index" is michael@0: ** the index into the per-thread private data table. michael@0: ** michael@0: ** The returned value may be NULL which is indistinguishable from an error michael@0: ** condition. michael@0: ** michael@0: */ michael@0: michael@0: PR_IMPLEMENT(void*) PR_GetThreadPrivate(PRUintn index) michael@0: { michael@0: PRThread *self = PR_GetCurrentThread(); michael@0: void *tpd = ((NULL == self->privateData) || (index >= self->tpdLength)) ? michael@0: NULL : self->privateData[index]; michael@0: michael@0: return tpd; michael@0: } michael@0: michael@0: /* michael@0: ** Destroy the thread's private data, if any exists. This is called at michael@0: ** thread termination time only. There should be no threading issues michael@0: ** since this is being called by the thread itself. michael@0: */ michael@0: void _PR_DestroyThreadPrivate(PRThread* self) michael@0: { michael@0: #define _PR_TPD_DESTRUCTOR_ITERATIONS 4 michael@0: michael@0: if (NULL != self->privateData) /* we have some */ michael@0: { michael@0: PRBool clean; michael@0: PRUint32 index; michael@0: PRInt32 passes = _PR_TPD_DESTRUCTOR_ITERATIONS; michael@0: PR_ASSERT(0 != self->tpdLength); michael@0: do michael@0: { michael@0: clean = PR_TRUE; michael@0: for (index = 0; index < self->tpdLength; ++index) michael@0: { michael@0: void *priv = self->privateData[index]; /* extract */ michael@0: if (NULL != priv) /* we have data at this index */ michael@0: { michael@0: if (NULL != _pr_tpd_destructors[index]) michael@0: { michael@0: self->privateData[index] = NULL; /* precondition */ michael@0: (*_pr_tpd_destructors[index])(priv); /* destroy */ michael@0: clean = PR_FALSE; /* unknown side effects */ michael@0: } michael@0: } michael@0: } michael@0: } while ((--passes > 0) && !clean); /* limit # of passes */ michael@0: /* michael@0: ** We give up after a fixed number of passes. Any non-NULL michael@0: ** thread-private data value with a registered destructor michael@0: ** function is not destroyed. michael@0: */ michael@0: memset(self->privateData, 0, self->tpdLength * sizeof(void*)); michael@0: } michael@0: } /* _PR_DestroyThreadPrivate */ michael@0: michael@0: #endif /* !XP_BEOS */