(root)/
findutils-4.9.0/
gnulib-tests/
windows-tls.c
       1  /* Thread-local storage (native Windows implementation).
       2     Copyright (C) 2005-2022 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)
      32  {
      33    return TlsGetValue (key);
      34  }
      35  
      36  int
      37  glwthread_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  
      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)
      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)
      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)
      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)
     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 *))
     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)
     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  }