Bug 1931425 - Limit how often moz-label's #setStyles runs r=reusable-components-revie...
[gecko.git] / nsprpub / pr / src / threads / prtpd.c
blob2b37142e1f7b592ee96ed3b1093ecbe838777d24
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 /*
7 ** Thread Private Data
8 **
9 ** There is an aribitrary limit on the number of keys that will be allocated
10 ** by the runtime. It's largish, so it is intended to be a sanity check, not
11 ** an impediment.
13 ** There is a counter, initialized to zero and incremented every time a
14 ** client asks for a new key, that holds the high water mark for keys. All
15 ** threads logically have the same high water mark and are permitted to
16 ** ask for TPD up to that key value.
18 ** The vector to hold the TPD are allocated when PR_SetThreadPrivate() is
19 ** called. The size of the vector will be some value greater than or equal
20 ** to the current high water mark. Each thread has its own TPD length and
21 ** vector.
23 ** Threads that get private data for keys they have not set (or perhaps
24 ** don't even exist for that thread) get a NULL return. If the key is
25 ** beyond the high water mark, an error will be returned.
29 ** As of this time, BeOS has its own TPD implementation. Integrating
30 ** this standard one is a TODO for anyone with a bit of spare time on
31 ** their hand. For now, we just #ifdef out this whole file and use
32 ** the routines in pr/src/btthreads/
35 #include "primpl.h"
37 #include <string.h>
39 #if defined(WIN95)
41 ** Some local variables report warnings on Win95 because the code paths
42 ** using them are conditioned on HAVE_CUSTOME_USER_THREADS.
43 ** The pragma suppresses the warning.
46 # pragma warning(disable : 4101)
47 #endif
49 #define _PR_TPD_LIMIT 128 /* arbitary limit on the TPD slots */
50 static PRInt32 _pr_tpd_length = 0; /* current length of destructor vector */
51 static PRInt32 _pr_tpd_highwater = 0; /* next TPD key to be assigned */
52 static PRThreadPrivateDTOR* _pr_tpd_destructors = NULL;
53 /* the destructors are associated with
54 the keys, therefore asserting that
55 the TPD key depicts the data's 'type' */
58 ** Initialize the thread private data manipulation
60 void _PR_InitTPD(void) {
61 _pr_tpd_destructors = (PRThreadPrivateDTOR*)PR_CALLOC(
62 _PR_TPD_LIMIT * sizeof(PRThreadPrivateDTOR*));
63 PR_ASSERT(NULL != _pr_tpd_destructors);
64 _pr_tpd_length = _PR_TPD_LIMIT;
68 ** Clean up the thread private data manipulation
70 void _PR_CleanupTPD(void) {} /* _PR_CleanupTPD */
73 ** This routine returns a new index for per-thread-private data table.
74 ** The index is visible to all threads within a process. This index can
75 ** be used with the PR_SetThreadPrivate() and PR_GetThreadPrivate() routines
76 ** to save and retrieve data associated with the index for a thread.
78 ** The index independently maintains specific values for each binding thread.
79 ** A thread can only get access to its own thread-specific-data.
81 ** Upon a new index return the value associated with the index for all threads
82 ** is NULL, and upon thread creation the value associated with all indices for
83 ** that thread is NULL.
85 ** "dtor" is the destructor function to invoke when the private
86 ** data is set or destroyed
88 ** Returns PR_FAILURE if the total number of indices will exceed the maximun
89 ** allowed.
92 PR_IMPLEMENT(PRStatus)
93 PR_NewThreadPrivateIndex(PRUintn* newIndex, PRThreadPrivateDTOR dtor) {
94 PRStatus rv;
95 PRInt32 index;
97 if (!_pr_initialized) {
98 _PR_ImplicitInitialization();
101 PR_ASSERT(NULL != newIndex);
102 PR_ASSERT(NULL != _pr_tpd_destructors);
104 index = PR_ATOMIC_INCREMENT(&_pr_tpd_highwater) - 1; /* allocate index */
105 if (_PR_TPD_LIMIT <= index) {
106 PR_SetError(PR_TPD_RANGE_ERROR, 0);
107 rv = PR_FAILURE; /* that's just wrong */
108 } else {
109 _pr_tpd_destructors[index] = dtor; /* record destructor @index */
110 *newIndex = (PRUintn)index; /* copy into client's location */
111 rv = PR_SUCCESS; /* that's okay */
114 return rv;
118 ** Define some per-thread-private data.
119 ** "index" is an index into the per-thread private data table
120 ** "priv" is the per-thread-private data
122 ** If the per-thread private data table has a previously registered
123 ** destructor function and a non-NULL per-thread-private data value,
124 ** the destructor function is invoked.
126 ** This can return PR_FAILURE if index is invalid (ie., beyond the limit
127 ** on the TPD slots) or memory is insufficient to allocate an expanded
128 ** vector.
131 PR_IMPLEMENT(PRStatus) PR_SetThreadPrivate(PRUintn index, void* priv) {
132 PRThread* self = PR_GetCurrentThread();
135 ** To improve performance, we don't check if the index has been
136 ** allocated.
138 if (index >= _PR_TPD_LIMIT) {
139 PR_SetError(PR_TPD_RANGE_ERROR, 0);
140 return PR_FAILURE;
143 PR_ASSERT(((NULL == self->privateData) && (0 == self->tpdLength)) ||
144 ((NULL != self->privateData) && (0 != self->tpdLength)));
147 ** If this thread does not have a sufficient vector for the index
148 ** being set, go ahead and extend this vector now.
150 if ((NULL == self->privateData) || (self->tpdLength <= index)) {
151 void* extension = PR_CALLOC(_pr_tpd_length * sizeof(void*));
152 if (NULL == extension) {
153 PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
154 return PR_FAILURE;
156 if (self->privateData) {
157 (void)memcpy(extension, self->privateData,
158 self->tpdLength * sizeof(void*));
159 PR_DELETE(self->privateData);
161 self->tpdLength = _pr_tpd_length;
162 self->privateData = (void**)extension;
165 ** There wasn't much chance of having to call the destructor
166 ** unless the slot already existed.
168 else if (self->privateData[index] && _pr_tpd_destructors[index]) {
169 void* data = self->privateData[index];
170 self->privateData[index] = NULL;
171 (*_pr_tpd_destructors[index])(data);
174 PR_ASSERT(index < self->tpdLength);
175 self->privateData[index] = priv;
177 return PR_SUCCESS;
181 ** Recover the per-thread-private data for the current thread. "index" is
182 ** the index into the per-thread private data table.
184 ** The returned value may be NULL which is indistinguishable from an error
185 ** condition.
189 PR_IMPLEMENT(void*) PR_GetThreadPrivate(PRUintn index) {
190 PRThread* self = PR_GetCurrentThread();
191 void* tpd = ((NULL == self->privateData) || (index >= self->tpdLength))
192 ? NULL
193 : self->privateData[index];
195 return tpd;
199 ** Destroy the thread's private data, if any exists. This is called at
200 ** thread termination time only. There should be no threading issues
201 ** since this is being called by the thread itself.
203 void _PR_DestroyThreadPrivate(PRThread* self) {
204 #define _PR_TPD_DESTRUCTOR_ITERATIONS 4
206 if (NULL != self->privateData) /* we have some */
208 PRBool clean;
209 PRUint32 index;
210 PRInt32 passes = _PR_TPD_DESTRUCTOR_ITERATIONS;
211 PR_ASSERT(0 != self->tpdLength);
212 do {
213 clean = PR_TRUE;
214 for (index = 0; index < self->tpdLength; ++index) {
215 void* priv = self->privateData[index]; /* extract */
216 if (NULL != priv) /* we have data at this index */
218 if (NULL != _pr_tpd_destructors[index]) {
219 self->privateData[index] = NULL; /* precondition */
220 (*_pr_tpd_destructors[index])(priv); /* destroy */
221 clean = PR_FALSE; /* unknown side effects */
225 } while ((--passes > 0) && !clean); /* limit # of passes */
227 ** We give up after a fixed number of passes. Any non-NULL
228 ** thread-private data value with a registered destructor
229 ** function is not destroyed.
231 memset(self->privateData, 0, self->tpdLength * sizeof(void*));
233 } /* _PR_DestroyThreadPrivate */