Coverage Report

Created: 2025-07-01 06:25

/src/nspr/pr/src/threads/prtpd.c
Line
Count
Source (jump to first uncovered line)
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/. */
5
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.
12
**
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.
17
**
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.
22
**
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.
26
*/
27
28
/*
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/
33
*/
34
35
#include "primpl.h"
36
37
#include <string.h>
38
39
#if defined(WIN95)
40
/*
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.
44
**
45
*/
46
#  pragma warning(disable : 4101)
47
#endif
48
49
0
#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' */
56
57
/*
58
** Initialize the thread private data manipulation
59
*/
60
0
void _PR_InitTPD(void) {
61
0
  _pr_tpd_destructors = (PRThreadPrivateDTOR*)PR_CALLOC(
62
0
      _PR_TPD_LIMIT * sizeof(PRThreadPrivateDTOR*));
63
0
  PR_ASSERT(NULL != _pr_tpd_destructors);
64
0
  _pr_tpd_length = _PR_TPD_LIMIT;
65
0
}
66
67
/*
68
** Clean up the thread private data manipulation
69
*/
70
0
void _PR_CleanupTPD(void) {} /* _PR_CleanupTPD */
71
72
/*
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.
77
**
78
** The index independently maintains specific values for each binding thread.
79
** A thread can only get access to its own thread-specific-data.
80
**
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.
84
**
85
**     "dtor" is the destructor function to invoke when the private
86
**       data is set or destroyed
87
**
88
** Returns PR_FAILURE if the total number of indices will exceed the maximun
89
** allowed.
90
*/
91
92
PR_IMPLEMENT(PRStatus)
93
0
PR_NewThreadPrivateIndex(PRUintn* newIndex, PRThreadPrivateDTOR dtor) {
94
0
  PRStatus rv;
95
0
  PRInt32 index;
96
97
0
  if (!_pr_initialized) {
98
0
    _PR_ImplicitInitialization();
99
0
  }
100
101
0
  PR_ASSERT(NULL != newIndex);
102
0
  PR_ASSERT(NULL != _pr_tpd_destructors);
103
104
0
  index = PR_ATOMIC_INCREMENT(&_pr_tpd_highwater) - 1; /* allocate index */
105
0
  if (_PR_TPD_LIMIT <= index) {
106
0
    PR_SetError(PR_TPD_RANGE_ERROR, 0);
107
0
    rv = PR_FAILURE; /* that's just wrong */
108
0
  } else {
109
0
    _pr_tpd_destructors[index] = dtor; /* record destructor @index */
110
0
    *newIndex = (PRUintn)index;        /* copy into client's location */
111
0
    rv = PR_SUCCESS;                   /* that's okay */
112
0
  }
113
114
0
  return rv;
115
0
}
116
117
/*
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
121
**
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.
125
**
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.
129
*/
130
131
0
PR_IMPLEMENT(PRStatus) PR_SetThreadPrivate(PRUintn index, void* priv) {
132
0
  PRThread* self = PR_GetCurrentThread();
133
134
  /*
135
  ** To improve performance, we don't check if the index has been
136
  ** allocated.
137
  */
138
0
  if (index >= _PR_TPD_LIMIT) {
139
0
    PR_SetError(PR_TPD_RANGE_ERROR, 0);
140
0
    return PR_FAILURE;
141
0
  }
142
143
0
  PR_ASSERT(((NULL == self->privateData) && (0 == self->tpdLength)) ||
144
0
            ((NULL != self->privateData) && (0 != self->tpdLength)));
145
146
  /*
147
  ** If this thread does not have a sufficient vector for the index
148
  ** being set, go ahead and extend this vector now.
149
  */
150
0
  if ((NULL == self->privateData) || (self->tpdLength <= index)) {
151
0
    void* extension = PR_CALLOC(_pr_tpd_length * sizeof(void*));
152
0
    if (NULL == extension) {
153
0
      PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
154
0
      return PR_FAILURE;
155
0
    }
156
0
    if (self->privateData) {
157
0
      (void)memcpy(extension, self->privateData,
158
0
                   self->tpdLength * sizeof(void*));
159
0
      PR_DELETE(self->privateData);
160
0
    }
161
0
    self->tpdLength = _pr_tpd_length;
162
0
    self->privateData = (void**)extension;
163
0
  }
164
  /*
165
  ** There wasn't much chance of having to call the destructor
166
  ** unless the slot already existed.
167
  */
168
0
  else if (self->privateData[index] && _pr_tpd_destructors[index]) {
169
0
    void* data = self->privateData[index];
170
0
    self->privateData[index] = NULL;
171
0
    (*_pr_tpd_destructors[index])(data);
172
0
  }
173
174
0
  PR_ASSERT(index < self->tpdLength);
175
0
  self->privateData[index] = priv;
176
177
0
  return PR_SUCCESS;
178
0
}
179
180
/*
181
** Recover the per-thread-private data for the current thread. "index" is
182
** the index into the per-thread private data table.
183
**
184
** The returned value may be NULL which is indistinguishable from an error
185
** condition.
186
**
187
*/
188
189
0
PR_IMPLEMENT(void*) PR_GetThreadPrivate(PRUintn index) {
190
0
  PRThread* self = PR_GetCurrentThread();
191
0
  void* tpd = ((NULL == self->privateData) || (index >= self->tpdLength))
192
0
                  ? NULL
193
0
                  : self->privateData[index];
194
195
0
  return tpd;
196
0
}
197
198
/*
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.
202
*/
203
0
void _PR_DestroyThreadPrivate(PRThread* self) {
204
0
#define _PR_TPD_DESTRUCTOR_ITERATIONS 4
205
206
0
  if (NULL != self->privateData) /* we have some */
207
0
  {
208
0
    PRBool clean;
209
0
    PRUint32 index;
210
0
    PRInt32 passes = _PR_TPD_DESTRUCTOR_ITERATIONS;
211
0
    PR_ASSERT(0 != self->tpdLength);
212
0
    do {
213
0
      clean = PR_TRUE;
214
0
      for (index = 0; index < self->tpdLength; ++index) {
215
0
        void* priv = self->privateData[index]; /* extract */
216
0
        if (NULL != priv)                      /* we have data at this index */
217
0
        {
218
0
          if (NULL != _pr_tpd_destructors[index]) {
219
0
            self->privateData[index] = NULL;     /* precondition */
220
0
            (*_pr_tpd_destructors[index])(priv); /* destroy */
221
0
            clean = PR_FALSE;                    /* unknown side effects */
222
0
          }
223
0
        }
224
0
      }
225
0
    } while ((--passes > 0) && !clean); /* limit # of passes */
226
    /*
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.
230
    */
231
0
    memset(self->privateData, 0, self->tpdLength * sizeof(void*));
232
0
  }
233
0
} /* _PR_DestroyThreadPrivate */