windows-tls.c 10 KB

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