1 /* Thread-local storage (native Windows implementation).
2 Copyright (C) 2005-2020 Free Software Foundation, Inc.
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 3 of the License, or
7 (at your option) any later version.
9 This program 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 General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <https://www.gnu.org/licenses/>. */
17 /* Written by Bruno Haible <bruno@clisp.org>, 2005. */
22 #include "windows-tls.h"
28 #include "windows-once.h"
31 glwthread_tls_get (glwthread_tls_key_t key
)
33 return TlsGetValue (key
);
37 glwthread_tls_set (glwthread_tls_key_t key
, void *value
)
39 if (!TlsSetValue (key
, value
))
44 /* The following variables keep track of TLS keys with non-NULL destructor. */
46 static glwthread_once_t dtor_table_init_once
= GLWTHREAD_ONCE_INIT
;
48 static CRITICAL_SECTION dtor_table_lock
;
50 struct dtor
{ glwthread_tls_key_t key
; void (*destructor
) (void *); };
52 /* The table of dtors. */
53 static struct dtor
*dtor_table
;
54 /* Number of active entries in the dtor_table. */
55 static unsigned int dtors_count
;
56 /* Valid indices into dtor_table are 0..dtors_used-1. */
57 static unsigned int dtors_used
;
58 /* Allocation size of dtor_table. */
59 static unsigned int dtors_allocated
;
60 /* Invariant: 0 <= dtors_count <= dtors_used <= dtors_allocated. */
62 /* Number of threads that are currently processing destructors. */
63 static unsigned int dtor_processing_threads
;
66 dtor_table_initialize (void)
68 InitializeCriticalSection (&dtor_table_lock
);
69 /* The other variables are already initialized to NULL or 0, respectively. */
73 dtor_table_ensure_initialized (void)
75 glwthread_once (&dtor_table_init_once
, dtor_table_initialize
);
78 /* Shrinks dtors_used down to dtors_count, by replacing inactive entries
81 dtor_table_shrink_used (void)
84 unsigned int j
= dtors_used
;
90 /* Find the next inactive entry, from the left. */
91 for (; i
< dtors_count
;)
93 if (dtor_table
[i
].destructor
== NULL
)
101 /* Find the next active entry, from the right. */
102 for (; j
> dtors_count
;)
105 if (dtor_table
[j
].destructor
!= NULL
)
112 if (i_found
!= j_found
)
113 /* dtors_count was apparently wrong. */
119 /* i_found and j_found are TRUE. Swap the two entries. */
120 dtor_table
[i
] = dtor_table
[j
];
125 dtors_used
= dtors_count
;
129 glwthread_tls_process_destructors (void)
133 dtor_table_ensure_initialized ();
135 EnterCriticalSection (&dtor_table_lock
);
136 if (dtor_processing_threads
== 0)
138 /* Now it's the appropriate time for shrinking dtors_used. */
139 if (dtors_used
> dtors_count
)
140 dtor_table_shrink_used ();
142 dtor_processing_threads
++;
144 for (repeat
= GLWTHREAD_DESTRUCTOR_ITERATIONS
; repeat
> 0; repeat
--)
146 unsigned int destructors_run
= 0;
148 /* Iterate across dtor_table. We don't need to make a copy of dtor_table,
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
;
162 for (i
= 0; i
< i_limit
; i
++)
164 struct dtor current
= dtor_table
[i
];
165 if (current
.destructor
!= NULL
)
167 /* The current dtor has not been deleted yet. */
168 void *current_value
= glwthread_tls_get (current
.key
);
169 if (current_value
!= NULL
)
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
);
181 /* When all TLS values were already NULL, no further iterations are
183 if (destructors_run
== 0)
187 dtor_processing_threads
--;
188 LeaveCriticalSection (&dtor_table_lock
);
192 glwthread_tls_key_create (glwthread_tls_key_t
*keyp
, void (*destructor
) (void *))
194 if (destructor
!= NULL
)
196 dtor_table_ensure_initialized ();
198 EnterCriticalSection (&dtor_table_lock
);
199 if (dtor_processing_threads
== 0)
201 /* Now it's the appropriate time for shrinking dtors_used. */
202 if (dtors_used
> dtors_count
)
203 dtor_table_shrink_used ();
206 while (dtors_used
== dtors_allocated
)
208 /* Need to grow the dtor_table. */
209 unsigned int new_allocated
= 2 * dtors_allocated
+ 1;
210 if (new_allocated
< 7)
212 if (new_allocated
<= dtors_allocated
) /* overflow? */
213 new_allocated
= UINT_MAX
;
215 LeaveCriticalSection (&dtor_table_lock
);
217 struct dtor
*new_table
=
218 (struct dtor
*) malloc (new_allocated
* sizeof (struct dtor
));
219 if (new_table
== NULL
)
221 EnterCriticalSection (&dtor_table_lock
);
222 /* Attention! dtors_used, dtors_allocated may have changed! */
223 if (dtors_used
< new_allocated
)
225 if (dtors_allocated
< new_allocated
)
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
;
235 /* The new_table is not useful, since another thread
236 meanwhile allocated a drop_table that is at least
242 /* The new_table is not useful, since other threads increased
243 dtors_used. Free it any retry. */
247 /* Here dtors_used < dtors_allocated. */
249 /* Allocate a new key. */
250 glwthread_tls_key_t key
= TlsAlloc ();
251 if (key
== (DWORD
)-1)
253 LeaveCriticalSection (&dtor_table_lock
);
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
;
264 LeaveCriticalSection (&dtor_table_lock
);
270 /* Allocate a new key. */
271 glwthread_tls_key_t key
= TlsAlloc ();
272 if (key
== (DWORD
)-1)
280 glwthread_tls_key_delete (glwthread_tls_key_t key
)
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 ();
291 EnterCriticalSection (&dtor_table_lock
);
292 if (dtor_processing_threads
== 0)
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. */
299 /* Find the key in dtor_table. */
301 unsigned int i_limit
= dtors_used
;
304 for (i
= 0; i
< i_limit
; i
++)
305 if (dtor_table
[i
].key
== key
)
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;
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
;
322 for (i
= 0; i
< i_limit
; i
++)
323 if (dtor_table
[i
].destructor
!= NULL
/* skip inactive entries */
324 && dtor_table
[i
].key
== key
)
326 /* Mark this entry as inactive. */
327 dtor_table
[i
].destructor
= NULL
;
328 dtors_count
= dtors_count
- 1;
332 LeaveCriticalSection (&dtor_table_lock
);
333 /* Now we have ensured that glwthread_tls_process_destructors will no longer