VirtualBox

source: kBuild/vendor/grep/3.7/gnulib-tests/windows-tls.c@ 3529

Last change on this file since 3529 was 3529, checked in by bird, 3 years ago

Imported grep 3.7 from grep-3.7.tar.gz (sha256: c22b0cf2d4f6bbe599c902387e8058990e1eee99aef333a203829e5fd3dbb342), applying minimal auto-props.

  • Property svn:eol-style set to native
File size: 10.4 KB
Line 
1/* Thread-local storage (native Windows implementation).
2 Copyright (C) 2005-2021 Free Software Foundation, Inc.
3
4 This file is free software: you can redistribute it and/or modify
5 it under the terms of the GNU Lesser General Public License as
6 published by the Free Software Foundation; either version 2.1 of the
7 License, or (at your option) any later version.
8
9 This file is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU Lesser General Public License for more details.
13
14 You should have received a copy of the GNU Lesser General Public License
15 along with this program. If not, see <https://www.gnu.org/licenses/>. */
16
17/* Written by Bruno Haible <[email protected]>, 2005. */
18
19#include <config.h>
20
21/* Specification. */
22#include "windows-tls.h"
23
24#include <errno.h>
25#include <limits.h>
26#include <stdlib.h>
27
28#include "windows-once.h"
29
30void *
31glwthread_tls_get (glwthread_tls_key_t key)
32{
33 return TlsGetValue (key);
34}
35
36int
37glwthread_tls_set (glwthread_tls_key_t key, void *value)
38{
39 if (!TlsSetValue (key, value))
40 return EINVAL;
41 return 0;
42}
43
44/* The following variables keep track of TLS keys with non-NULL destructor. */
45
46static glwthread_once_t dtor_table_init_once = GLWTHREAD_ONCE_INIT;
47
48static CRITICAL_SECTION dtor_table_lock;
49
50struct dtor { glwthread_tls_key_t key; void (*destructor) (void *); };
51
52/* The table of dtors. */
53static struct dtor *dtor_table;
54/* Number of active entries in the dtor_table. */
55static unsigned int dtors_count;
56/* Valid indices into dtor_table are 0..dtors_used-1. */
57static unsigned int dtors_used;
58/* Allocation size of dtor_table. */
59static unsigned int dtors_allocated;
60/* Invariant: 0 <= dtors_count <= dtors_used <= dtors_allocated. */
61
62/* Number of threads that are currently processing destructors. */
63static unsigned int dtor_processing_threads;
64
65static void
66dtor_table_initialize (void)
67{
68 InitializeCriticalSection (&dtor_table_lock);
69 /* The other variables are already initialized to NULL or 0, respectively. */
70}
71
72static void
73dtor_table_ensure_initialized (void)
74{
75 glwthread_once (&dtor_table_init_once, dtor_table_initialize);
76}
77
78/* Shrinks dtors_used down to dtors_count, by replacing inactive entries
79 with active ones. */
80static void
81dtor_table_shrink_used (void)
82{
83 unsigned int i = 0;
84 unsigned int j = dtors_used;
85
86 for (;;)
87 {
88 BOOL i_found = FALSE;
89 BOOL j_found = FALSE;
90 /* Find the next inactive entry, from the left. */
91 for (; i < dtors_count;)
92 {
93 if (dtor_table[i].destructor == NULL)
94 {
95 i_found = TRUE;
96 break;
97 }
98 i++;
99 }
100
101 /* Find the next active entry, from the right. */
102 for (; j > dtors_count;)
103 {
104 j--;
105 if (dtor_table[j].destructor != NULL)
106 {
107 j_found = TRUE;
108 break;
109 }
110 }
111
112 if (i_found != j_found)
113 /* dtors_count was apparently wrong. */
114 abort ();
115
116 if (!i_found)
117 break;
118
119 /* i_found and j_found are TRUE. Swap the two entries. */
120 dtor_table[i] = dtor_table[j];
121
122 i++;
123 }
124
125 dtors_used = dtors_count;
126}
127
128void
129glwthread_tls_process_destructors (void)
130{
131 unsigned int repeat;
132
133 dtor_table_ensure_initialized ();
134
135 EnterCriticalSection (&dtor_table_lock);
136 if (dtor_processing_threads == 0)
137 {
138 /* Now it's the appropriate time for shrinking dtors_used. */
139 if (dtors_used > dtors_count)
140 dtor_table_shrink_used ();
141 }
142 dtor_processing_threads++;
143
144 for (repeat = GLWTHREAD_DESTRUCTOR_ITERATIONS; repeat > 0; repeat--)
145 {
146 unsigned int destructors_run = 0;
147
148 /* Iterate across dtor_table. We don't need to make a copy of dtor_table,
149 because
150 * When another thread calls glwthread_tls_key_create with a non-NULL
151 destructor argument, this will possibly reallocate the dtor_table
152 array and increase dtors_allocated as well as dtors_used and
153 dtors_count, but it will not change dtors_used nor the contents of
154 the first dtors_used entries of dtor_table.
155 * When another thread calls glwthread_tls_key_delete, this will
156 possibly set some 'destructor' member to NULL, thus marking an
157 entry as inactive, but it will not otherwise change dtors_used nor
158 the contents of the first dtors_used entries of dtor_table. */
159 unsigned int i_limit = dtors_used;
160 unsigned int i;
161
162 for (i = 0; i < i_limit; i++)
163 {
164 struct dtor current = dtor_table[i];
165 if (current.destructor != NULL)
166 {
167 /* The current dtor has not been deleted yet. */
168 void *current_value = glwthread_tls_get (current.key);
169 if (current_value != NULL)
170 {
171 /* The current value is non-NULL. Run the destructor. */
172 glwthread_tls_set (current.key, NULL);
173 LeaveCriticalSection (&dtor_table_lock);
174 current.destructor (current_value);
175 EnterCriticalSection (&dtor_table_lock);
176 destructors_run++;
177 }
178 }
179 }
180
181 /* When all TLS values were already NULL, no further iterations are
182 needed. */
183 if (destructors_run == 0)
184 break;
185 }
186
187 dtor_processing_threads--;
188 LeaveCriticalSection (&dtor_table_lock);
189}
190
191int
192glwthread_tls_key_create (glwthread_tls_key_t *keyp, void (*destructor) (void *))
193{
194 if (destructor != NULL)
195 {
196 dtor_table_ensure_initialized ();
197
198 EnterCriticalSection (&dtor_table_lock);
199 if (dtor_processing_threads == 0)
200 {
201 /* Now it's the appropriate time for shrinking dtors_used. */
202 if (dtors_used > dtors_count)
203 dtor_table_shrink_used ();
204 }
205
206 while (dtors_used == dtors_allocated)
207 {
208 /* Need to grow the dtor_table. */
209 unsigned int new_allocated = 2 * dtors_allocated + 1;
210 if (new_allocated < 7)
211 new_allocated = 7;
212 if (new_allocated <= dtors_allocated) /* overflow? */
213 new_allocated = UINT_MAX;
214
215 LeaveCriticalSection (&dtor_table_lock);
216 {
217 struct dtor *new_table =
218 (struct dtor *) malloc (new_allocated * sizeof (struct dtor));
219 if (new_table == NULL)
220 return ENOMEM;
221 EnterCriticalSection (&dtor_table_lock);
222 /* Attention! dtors_used, dtors_allocated may have changed! */
223 if (dtors_used < new_allocated)
224 {
225 if (dtors_allocated < new_allocated)
226 {
227 /* The new_table is useful. */
228 memcpy (new_table, dtor_table,
229 dtors_used * sizeof (struct dtor));
230 dtor_table = new_table;
231 dtors_allocated = new_allocated;
232 }
233 else
234 {
235 /* The new_table is not useful, since another thread
236 meanwhile allocated a drop_table that is at least
237 as large. */
238 free (new_table);
239 }
240 break;
241 }
242 /* The new_table is not useful, since other threads increased
243 dtors_used. Free it any retry. */
244 free (new_table);
245 }
246 }
247 /* Here dtors_used < dtors_allocated. */
248 {
249 /* Allocate a new key. */
250 glwthread_tls_key_t key = TlsAlloc ();
251 if (key == (DWORD)-1)
252 {
253 LeaveCriticalSection (&dtor_table_lock);
254 return EAGAIN;
255 }
256 /* Store the new dtor in the dtor_table, after all used entries.
257 Do not overwrite inactive entries with indices < dtors_used, in order
258 not to disturb glwthread_tls_process_destructors invocations that may
259 be executing in other threads. */
260 dtor_table[dtors_used].key = key;
261 dtor_table[dtors_used].destructor = destructor;
262 dtors_used++;
263 dtors_count++;
264 LeaveCriticalSection (&dtor_table_lock);
265 *keyp = key;
266 }
267 }
268 else
269 {
270 /* Allocate a new key. */
271 glwthread_tls_key_t key = TlsAlloc ();
272 if (key == (DWORD)-1)
273 return EAGAIN;
274 *keyp = key;
275 }
276 return 0;
277}
278
279int
280glwthread_tls_key_delete (glwthread_tls_key_t key)
281{
282 /* Should the destructor be called for all threads that are currently running?
283 Probably not, because
284 - ISO C does not specify when the destructor is to be invoked at all.
285 - In POSIX, the destructor functions specified with pthread_key_create()
286 are invoked at thread exit.
287 - It would be hard to implement, because there are no primitives for
288 accessing thread-specific values from a different thread. */
289 dtor_table_ensure_initialized ();
290
291 EnterCriticalSection (&dtor_table_lock);
292 if (dtor_processing_threads == 0)
293 {
294 /* Now it's the appropriate time for shrinking dtors_used. */
295 if (dtors_used > dtors_count)
296 dtor_table_shrink_used ();
297 /* Here dtors_used == dtors_count. */
298
299 /* Find the key in dtor_table. */
300 {
301 unsigned int i_limit = dtors_used;
302 unsigned int i;
303
304 for (i = 0; i < i_limit; i++)
305 if (dtor_table[i].key == key)
306 {
307 if (i < dtors_used - 1)
308 /* Swap the entries i and dtors_used - 1. */
309 dtor_table[i] = dtor_table[dtors_used - 1];
310 dtors_count = dtors_used = dtors_used - 1;
311 break;
312 }
313 }
314 }
315 else
316 {
317 /* Be careful not to disturb the glwthread_tls_process_destructors
318 invocations that are executing in other threads. */
319 unsigned int i_limit = dtors_used;
320 unsigned int i;
321
322 for (i = 0; i < i_limit; i++)
323 if (dtor_table[i].destructor != NULL /* skip inactive entries */
324 && dtor_table[i].key == key)
325 {
326 /* Mark this entry as inactive. */
327 dtor_table[i].destructor = NULL;
328 dtors_count = dtors_count - 1;
329 break;
330 }
331 }
332 LeaveCriticalSection (&dtor_table_lock);
333 /* Now we have ensured that glwthread_tls_process_destructors will no longer
334 use this key. */
335
336 if (!TlsFree (key))
337 return EINVAL;
338 return 0;
339}
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette