|
Packit Service |
4684c1 |
/* Thread-local storage (native Windows implementation).
|
|
Packit Service |
4684c1 |
Copyright (C) 2005-2020 Free Software Foundation, Inc.
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
This program is free software: you can redistribute it and/or modify
|
|
Packit Service |
4684c1 |
it under the terms of the GNU General Public License as published by
|
|
Packit Service |
4684c1 |
the Free Software Foundation; either version 3 of the License, or
|
|
Packit Service |
4684c1 |
(at your option) any later version.
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
This program is distributed in the hope that it will be useful,
|
|
Packit Service |
4684c1 |
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit Service |
4684c1 |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
Packit Service |
4684c1 |
GNU General Public License for more details.
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
You should have received a copy of the GNU General Public License
|
|
Packit Service |
4684c1 |
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
/* Written by Bruno Haible <bruno@clisp.org>, 2005. */
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
#include <config.h>
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
/* Specification. */
|
|
Packit Service |
4684c1 |
#include "windows-tls.h"
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
#include <errno.h>
|
|
Packit Service |
4684c1 |
#include <limits.h>
|
|
Packit Service |
4684c1 |
#include <stdlib.h>
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
#include "windows-once.h"
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
void *
|
|
Packit Service |
4684c1 |
glwthread_tls_get (glwthread_tls_key_t key)
|
|
Packit Service |
4684c1 |
{
|
|
Packit Service |
4684c1 |
return TlsGetValue (key);
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
int
|
|
Packit Service |
4684c1 |
glwthread_tls_set (glwthread_tls_key_t key, void *value)
|
|
Packit Service |
4684c1 |
{
|
|
Packit Service |
4684c1 |
if (!TlsSetValue (key, value))
|
|
Packit Service |
4684c1 |
return EINVAL;
|
|
Packit Service |
4684c1 |
return 0;
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
/* The following variables keep track of TLS keys with non-NULL destructor. */
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
static glwthread_once_t dtor_table_init_once = GLWTHREAD_ONCE_INIT;
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
static CRITICAL_SECTION dtor_table_lock;
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
struct dtor { glwthread_tls_key_t key; void (*destructor) (void *); };
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
/* The table of dtors. */
|
|
Packit Service |
4684c1 |
static struct dtor *dtor_table;
|
|
Packit Service |
4684c1 |
/* Number of active entries in the dtor_table. */
|
|
Packit Service |
4684c1 |
static unsigned int dtors_count;
|
|
Packit Service |
4684c1 |
/* Valid indices into dtor_table are 0..dtors_used-1. */
|
|
Packit Service |
4684c1 |
static unsigned int dtors_used;
|
|
Packit Service |
4684c1 |
/* Allocation size of dtor_table. */
|
|
Packit Service |
4684c1 |
static unsigned int dtors_allocated;
|
|
Packit Service |
4684c1 |
/* Invariant: 0 <= dtors_count <= dtors_used <= dtors_allocated. */
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
/* Number of threads that are currently processing destructors. */
|
|
Packit Service |
4684c1 |
static unsigned int dtor_processing_threads;
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
static void
|
|
Packit Service |
4684c1 |
dtor_table_initialize (void)
|
|
Packit Service |
4684c1 |
{
|
|
Packit Service |
4684c1 |
InitializeCriticalSection (&dtor_table_lock);
|
|
Packit Service |
4684c1 |
/* The other variables are already initialized to NULL or 0, respectively. */
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
static void
|
|
Packit Service |
4684c1 |
dtor_table_ensure_initialized (void)
|
|
Packit Service |
4684c1 |
{
|
|
Packit Service |
4684c1 |
glwthread_once (&dtor_table_init_once, dtor_table_initialize);
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
/* Shrinks dtors_used down to dtors_count, by replacing inactive entries
|
|
Packit Service |
4684c1 |
with active ones. */
|
|
Packit Service |
4684c1 |
static void
|
|
Packit Service |
4684c1 |
dtor_table_shrink_used (void)
|
|
Packit Service |
4684c1 |
{
|
|
Packit Service |
4684c1 |
unsigned int i = 0;
|
|
Packit Service |
4684c1 |
unsigned int j = dtors_used;
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
for (;;)
|
|
Packit Service |
4684c1 |
{
|
|
Packit Service |
4684c1 |
BOOL i_found = FALSE;
|
|
Packit Service |
4684c1 |
BOOL j_found = FALSE;
|
|
Packit Service |
4684c1 |
/* Find the next inactive entry, from the left. */
|
|
Packit Service |
4684c1 |
for (; i < dtors_count;)
|
|
Packit Service |
4684c1 |
{
|
|
Packit Service |
4684c1 |
if (dtor_table[i].destructor == NULL)
|
|
Packit Service |
4684c1 |
{
|
|
Packit Service |
4684c1 |
i_found = TRUE;
|
|
Packit Service |
4684c1 |
break;
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
i++;
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
/* Find the next active entry, from the right. */
|
|
Packit Service |
4684c1 |
for (; j > dtors_count;)
|
|
Packit Service |
4684c1 |
{
|
|
Packit Service |
4684c1 |
j--;
|
|
Packit Service |
4684c1 |
if (dtor_table[j].destructor != NULL)
|
|
Packit Service |
4684c1 |
{
|
|
Packit Service |
4684c1 |
j_found = TRUE;
|
|
Packit Service |
4684c1 |
break;
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
if (i_found != j_found)
|
|
Packit Service |
4684c1 |
/* dtors_count was apparently wrong. */
|
|
Packit Service |
4684c1 |
abort ();
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
if (!i_found)
|
|
Packit Service |
4684c1 |
break;
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
/* i_found and j_found are TRUE. Swap the two entries. */
|
|
Packit Service |
4684c1 |
dtor_table[i] = dtor_table[j];
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
i++;
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
dtors_used = dtors_count;
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
void
|
|
Packit Service |
4684c1 |
glwthread_tls_process_destructors (void)
|
|
Packit Service |
4684c1 |
{
|
|
Packit Service |
4684c1 |
unsigned int repeat;
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
dtor_table_ensure_initialized ();
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
EnterCriticalSection (&dtor_table_lock);
|
|
Packit Service |
4684c1 |
if (dtor_processing_threads == 0)
|
|
Packit Service |
4684c1 |
{
|
|
Packit Service |
4684c1 |
/* Now it's the appropriate time for shrinking dtors_used. */
|
|
Packit Service |
4684c1 |
if (dtors_used > dtors_count)
|
|
Packit Service |
4684c1 |
dtor_table_shrink_used ();
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
dtor_processing_threads++;
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
for (repeat = GLWTHREAD_DESTRUCTOR_ITERATIONS; repeat > 0; repeat--)
|
|
Packit Service |
4684c1 |
{
|
|
Packit Service |
4684c1 |
unsigned int destructors_run = 0;
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
/* Iterate across dtor_table. We don't need to make a copy of dtor_table,
|
|
Packit Service |
4684c1 |
because
|
|
Packit Service |
4684c1 |
* When another thread calls glwthread_tls_key_create with a non-NULL
|
|
Packit Service |
4684c1 |
destructor argument, this will possibly reallocate the dtor_table
|
|
Packit Service |
4684c1 |
array and increase dtors_allocated as well as dtors_used and
|
|
Packit Service |
4684c1 |
dtors_count, but it will not change dtors_used nor the contents of
|
|
Packit Service |
4684c1 |
the first dtors_used entries of dtor_table.
|
|
Packit Service |
4684c1 |
* When another thread calls glwthread_tls_key_delete, this will
|
|
Packit Service |
4684c1 |
possibly set some 'destructor' member to NULL, thus marking an
|
|
Packit Service |
4684c1 |
entry as inactive, but it will not otherwise change dtors_used nor
|
|
Packit Service |
4684c1 |
the contents of the first dtors_used entries of dtor_table. */
|
|
Packit Service |
4684c1 |
unsigned int i_limit = dtors_used;
|
|
Packit Service |
4684c1 |
unsigned int i;
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
for (i = 0; i < i_limit; i++)
|
|
Packit Service |
4684c1 |
{
|
|
Packit Service |
4684c1 |
struct dtor current = dtor_table[i];
|
|
Packit Service |
4684c1 |
if (current.destructor != NULL)
|
|
Packit Service |
4684c1 |
{
|
|
Packit Service |
4684c1 |
/* The current dtor has not been deleted yet. */
|
|
Packit Service |
4684c1 |
void *current_value = glwthread_tls_get (current.key);
|
|
Packit Service |
4684c1 |
if (current_value != NULL)
|
|
Packit Service |
4684c1 |
{
|
|
Packit Service |
4684c1 |
/* The current value is non-NULL. Run the destructor. */
|
|
Packit Service |
4684c1 |
glwthread_tls_set (current.key, NULL);
|
|
Packit Service |
4684c1 |
LeaveCriticalSection (&dtor_table_lock);
|
|
Packit Service |
4684c1 |
current.destructor (current_value);
|
|
Packit Service |
4684c1 |
EnterCriticalSection (&dtor_table_lock);
|
|
Packit Service |
4684c1 |
destructors_run++;
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
/* When all TLS values were already NULL, no further iterations are
|
|
Packit Service |
4684c1 |
needed. */
|
|
Packit Service |
4684c1 |
if (destructors_run == 0)
|
|
Packit Service |
4684c1 |
break;
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
dtor_processing_threads--;
|
|
Packit Service |
4684c1 |
LeaveCriticalSection (&dtor_table_lock);
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
int
|
|
Packit Service |
4684c1 |
glwthread_tls_key_create (glwthread_tls_key_t *keyp, void (*destructor) (void *))
|
|
Packit Service |
4684c1 |
{
|
|
Packit Service |
4684c1 |
if (destructor != NULL)
|
|
Packit Service |
4684c1 |
{
|
|
Packit Service |
4684c1 |
dtor_table_ensure_initialized ();
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
EnterCriticalSection (&dtor_table_lock);
|
|
Packit Service |
4684c1 |
if (dtor_processing_threads == 0)
|
|
Packit Service |
4684c1 |
{
|
|
Packit Service |
4684c1 |
/* Now it's the appropriate time for shrinking dtors_used. */
|
|
Packit Service |
4684c1 |
if (dtors_used > dtors_count)
|
|
Packit Service |
4684c1 |
dtor_table_shrink_used ();
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
while (dtors_used == dtors_allocated)
|
|
Packit Service |
4684c1 |
{
|
|
Packit Service |
4684c1 |
/* Need to grow the dtor_table. */
|
|
Packit Service |
4684c1 |
unsigned int new_allocated = 2 * dtors_allocated + 1;
|
|
Packit Service |
4684c1 |
if (new_allocated < 7)
|
|
Packit Service |
4684c1 |
new_allocated = 7;
|
|
Packit Service |
4684c1 |
if (new_allocated <= dtors_allocated) /* overflow? */
|
|
Packit Service |
4684c1 |
new_allocated = UINT_MAX;
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
LeaveCriticalSection (&dtor_table_lock);
|
|
Packit Service |
4684c1 |
{
|
|
Packit Service |
4684c1 |
struct dtor *new_table =
|
|
Packit Service |
4684c1 |
(struct dtor *) malloc (new_allocated * sizeof (struct dtor));
|
|
Packit Service |
4684c1 |
if (new_table == NULL)
|
|
Packit Service |
4684c1 |
return ENOMEM;
|
|
Packit Service |
4684c1 |
EnterCriticalSection (&dtor_table_lock);
|
|
Packit Service |
4684c1 |
/* Attention! dtors_used, dtors_allocated may have changed! */
|
|
Packit Service |
4684c1 |
if (dtors_used < new_allocated)
|
|
Packit Service |
4684c1 |
{
|
|
Packit Service |
4684c1 |
if (dtors_allocated < new_allocated)
|
|
Packit Service |
4684c1 |
{
|
|
Packit Service |
4684c1 |
/* The new_table is useful. */
|
|
Packit Service |
4684c1 |
memcpy (new_table, dtor_table,
|
|
Packit Service |
4684c1 |
dtors_used * sizeof (struct dtor));
|
|
Packit Service |
4684c1 |
dtor_table = new_table;
|
|
Packit Service |
4684c1 |
dtors_allocated = new_allocated;
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
else
|
|
Packit Service |
4684c1 |
{
|
|
Packit Service |
4684c1 |
/* The new_table is not useful, since another thread
|
|
Packit Service |
4684c1 |
meanwhile allocated a drop_table that is at least
|
|
Packit Service |
4684c1 |
as large. */
|
|
Packit Service |
4684c1 |
free (new_table);
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
break;
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
/* The new_table is not useful, since other threads increased
|
|
Packit Service |
4684c1 |
dtors_used. Free it any retry. */
|
|
Packit Service |
4684c1 |
free (new_table);
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
/* Here dtors_used < dtors_allocated. */
|
|
Packit Service |
4684c1 |
{
|
|
Packit Service |
4684c1 |
/* Allocate a new key. */
|
|
Packit Service |
4684c1 |
glwthread_tls_key_t key = TlsAlloc ();
|
|
Packit Service |
4684c1 |
if (key == (DWORD)-1)
|
|
Packit Service |
4684c1 |
{
|
|
Packit Service |
4684c1 |
LeaveCriticalSection (&dtor_table_lock);
|
|
Packit Service |
4684c1 |
return EAGAIN;
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
/* Store the new dtor in the dtor_table, after all used entries.
|
|
Packit Service |
4684c1 |
Do not overwrite inactive entries with indices < dtors_used, in order
|
|
Packit Service |
4684c1 |
not to disturb glwthread_tls_process_destructors invocations that may
|
|
Packit Service |
4684c1 |
be executing in other threads. */
|
|
Packit Service |
4684c1 |
dtor_table[dtors_used].key = key;
|
|
Packit Service |
4684c1 |
dtor_table[dtors_used].destructor = destructor;
|
|
Packit Service |
4684c1 |
dtors_used++;
|
|
Packit Service |
4684c1 |
dtors_count++;
|
|
Packit Service |
4684c1 |
LeaveCriticalSection (&dtor_table_lock);
|
|
Packit Service |
4684c1 |
*keyp = key;
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
else
|
|
Packit Service |
4684c1 |
{
|
|
Packit Service |
4684c1 |
/* Allocate a new key. */
|
|
Packit Service |
4684c1 |
glwthread_tls_key_t key = TlsAlloc ();
|
|
Packit Service |
4684c1 |
if (key == (DWORD)-1)
|
|
Packit Service |
4684c1 |
return EAGAIN;
|
|
Packit Service |
4684c1 |
*keyp = key;
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
return 0;
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
int
|
|
Packit Service |
4684c1 |
glwthread_tls_key_delete (glwthread_tls_key_t key)
|
|
Packit Service |
4684c1 |
{
|
|
Packit Service |
4684c1 |
/* Should the destructor be called for all threads that are currently running?
|
|
Packit Service |
4684c1 |
Probably not, because
|
|
Packit Service |
4684c1 |
- ISO C does not specify when the destructor is to be invoked at all.
|
|
Packit Service |
4684c1 |
- In POSIX, the destructor functions specified with pthread_key_create()
|
|
Packit Service |
4684c1 |
are invoked at thread exit.
|
|
Packit Service |
4684c1 |
- It would be hard to implement, because there are no primitives for
|
|
Packit Service |
4684c1 |
accessing thread-specific values from a different thread. */
|
|
Packit Service |
4684c1 |
dtor_table_ensure_initialized ();
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
EnterCriticalSection (&dtor_table_lock);
|
|
Packit Service |
4684c1 |
if (dtor_processing_threads == 0)
|
|
Packit Service |
4684c1 |
{
|
|
Packit Service |
4684c1 |
/* Now it's the appropriate time for shrinking dtors_used. */
|
|
Packit Service |
4684c1 |
if (dtors_used > dtors_count)
|
|
Packit Service |
4684c1 |
dtor_table_shrink_used ();
|
|
Packit Service |
4684c1 |
/* Here dtors_used == dtors_count. */
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
/* Find the key in dtor_table. */
|
|
Packit Service |
4684c1 |
{
|
|
Packit Service |
4684c1 |
unsigned int i_limit = dtors_used;
|
|
Packit Service |
4684c1 |
unsigned int i;
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
for (i = 0; i < i_limit; i++)
|
|
Packit Service |
4684c1 |
if (dtor_table[i].key == key)
|
|
Packit Service |
4684c1 |
{
|
|
Packit Service |
4684c1 |
if (i < dtors_used - 1)
|
|
Packit Service |
4684c1 |
/* Swap the entries i and dtors_used - 1. */
|
|
Packit Service |
4684c1 |
dtor_table[i] = dtor_table[dtors_used - 1];
|
|
Packit Service |
4684c1 |
dtors_count = dtors_used = dtors_used - 1;
|
|
Packit Service |
4684c1 |
break;
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
else
|
|
Packit Service |
4684c1 |
{
|
|
Packit Service |
4684c1 |
/* Be careful not to disturb the glwthread_tls_process_destructors
|
|
Packit Service |
4684c1 |
invocations that are executing in other threads. */
|
|
Packit Service |
4684c1 |
unsigned int i_limit = dtors_used;
|
|
Packit Service |
4684c1 |
unsigned int i;
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
for (i = 0; i < i_limit; i++)
|
|
Packit Service |
4684c1 |
if (dtor_table[i].destructor != NULL /* skip inactive entries */
|
|
Packit Service |
4684c1 |
&& dtor_table[i].key == key)
|
|
Packit Service |
4684c1 |
{
|
|
Packit Service |
4684c1 |
/* Mark this entry as inactive. */
|
|
Packit Service |
4684c1 |
dtor_table[i].destructor = NULL;
|
|
Packit Service |
4684c1 |
dtors_count = dtors_count - 1;
|
|
Packit Service |
4684c1 |
break;
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
LeaveCriticalSection (&dtor_table_lock);
|
|
Packit Service |
4684c1 |
/* Now we have ensured that glwthread_tls_process_destructors will no longer
|
|
Packit Service |
4684c1 |
use this key. */
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
if (!TlsFree (key))
|
|
Packit Service |
4684c1 |
return EINVAL;
|
|
Packit Service |
4684c1 |
return 0;
|
|
Packit Service |
4684c1 |
}
|