root/maint/gnulib/lib/windows-tls.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. glwthread_tls_get
  2. glwthread_tls_set
  3. dtor_table_initialize
  4. dtor_table_ensure_initialized
  5. dtor_table_shrink_used
  6. glwthread_tls_process_destructors
  7. glwthread_tls_key_create
  8. glwthread_tls_key_delete

   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 <bruno@clisp.org>, 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 
  30 void *
  31 glwthread_tls_get (glwthread_tls_key_t key)
     /* [previous][next][first][last][top][bottom][index][help] */
  32 {
  33   return TlsGetValue (key);
  34 }
  35 
  36 int
  37 glwthread_tls_set (glwthread_tls_key_t key, void *value)
     /* [previous][next][first][last][top][bottom][index][help] */
  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 
  46 static glwthread_once_t dtor_table_init_once = GLWTHREAD_ONCE_INIT;
  47 
  48 static CRITICAL_SECTION dtor_table_lock;
  49 
  50 struct dtor { glwthread_tls_key_t key; void (*destructor) (void *); };
  51 
  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.  */
  61 
  62 /* Number of threads that are currently processing destructors.  */
  63 static unsigned int dtor_processing_threads;
  64 
  65 static void
  66 dtor_table_initialize (void)
     /* [previous][next][first][last][top][bottom][index][help] */
  67 {
  68   InitializeCriticalSection (&dtor_table_lock);
  69   /* The other variables are already initialized to NULL or 0, respectively.  */
  70 }
  71 
  72 static void
  73 dtor_table_ensure_initialized (void)
     /* [previous][next][first][last][top][bottom][index][help] */
  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.  */
  80 static void
  81 dtor_table_shrink_used (void)
     /* [previous][next][first][last][top][bottom][index][help] */
  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 
 128 void
 129 glwthread_tls_process_destructors (void)
     /* [previous][next][first][last][top][bottom][index][help] */
 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 
 191 int
 192 glwthread_tls_key_create (glwthread_tls_key_t *keyp, void (*destructor) (void *))
     /* [previous][next][first][last][top][bottom][index][help] */
 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 
 279 int
 280 glwthread_tls_key_delete (glwthread_tls_key_t key)
     /* [previous][next][first][last][top][bottom][index][help] */
 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 }

/* [previous][next][first][last][top][bottom][index][help] */