325 lines
		
	
	
		
			9.6 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			325 lines
		
	
	
		
			9.6 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /***********************************************************************
 | |
|  * Copyright (c) 2016 Kungliga Tekniska Högskolan
 | |
|  * (Royal Institute of Technology, Stockholm, Sweden).
 | |
|  * All rights reserved.
 | |
|  *
 | |
|  * Redistribution and use in source and binary forms, with or without
 | |
|  * modification, are permitted provided that the following conditions
 | |
|  * are met:
 | |
|  *
 | |
|  * 1. Redistributions of source code must retain the above copyright
 | |
|  *    notice, this list of conditions and the following disclaimer.
 | |
|  *
 | |
|  * 2. Redistributions in binary form must reproduce the above copyright
 | |
|  *    notice, this list of conditions and the following disclaimer in the
 | |
|  *    documentation and/or other materials provided with the distribution.
 | |
|  *
 | |
|  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | |
|  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | |
|  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 | |
|  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 | |
|  * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 | |
|  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 | |
|  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 | |
|  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 | |
|  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 | |
|  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 | |
|  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 | |
|  * OF THE POSSIBILITY OF SUCH DAMAGE.
 | |
|  *
 | |
|  **********************************************************************/
 | |
| 
 | |
| /*
 | |
|  * This is an implementation of thread-specific storage with
 | |
|  * destructors.  WIN32 doesn't quite have this.  Instead it has
 | |
|  * DllMain(), an entry point in every DLL that gets called to notify the
 | |
|  * DLL of thread/process "attach"/"detach" events.
 | |
|  *
 | |
|  * We use __thread (or __declspec(thread)) for the thread-local itself
 | |
|  * and DllMain() DLL_THREAD_DETACH events to drive destruction of
 | |
|  * thread-local values.
 | |
|  *
 | |
|  * When building in maintainer mode on non-Windows pthread systems this
 | |
|  * uses a single pthread key instead to implement multiple keys.  This
 | |
|  * keeps the code from rotting when modified by non-Windows developers.
 | |
|  */
 | |
| 
 | |
| #include "baselocl.h"
 | |
| 
 | |
| #ifdef WIN32
 | |
| #include <windows.h>
 | |
| #endif
 | |
| 
 | |
| #ifdef HEIM_WIN32_TLS
 | |
| #include <assert.h>
 | |
| #include <err.h>
 | |
| #include <heim_threads.h>
 | |
| 
 | |
| #ifndef WIN32
 | |
| #include <pthread.h>
 | |
| #endif
 | |
| 
 | |
| /* Logical array of keys that grows lock-lessly */
 | |
| typedef struct tls_keys tls_keys;
 | |
| struct tls_keys {
 | |
|     void (**keys_dtors)(void *);    /* array of destructors         */
 | |
|     size_t keys_start_idx;          /* index of first destructor    */
 | |
|     size_t keys_num;
 | |
|     tls_keys *keys_next;
 | |
| };
 | |
| 
 | |
| /*
 | |
|  * Well, not quite locklessly.  We need synchronization primitives to do
 | |
|  * this locklessly.  An atomic CAS will do.
 | |
|  */
 | |
| static HEIMDAL_MUTEX tls_key_defs_lock = HEIMDAL_MUTEX_INITIALIZER;
 | |
| static tls_keys *tls_key_defs;
 | |
| 
 | |
| /* Logical array of values (per-thread; no locking needed here) */
 | |
| struct tls_values {
 | |
|     void **values; /* realloc()ed */
 | |
|     size_t values_num;
 | |
| };
 | |
| 
 | |
| static HEIMDAL_THREAD_LOCAL struct tls_values values;
 | |
| 
 | |
| #define DEAD_KEY ((void *)8)
 | |
| 
 | |
| void
 | |
| heim_w32_service_thread_detach(void *unused)
 | |
| {
 | |
|     tls_keys *key_defs;
 | |
|     void (*dtor)(void*);
 | |
|     size_t i;
 | |
| 
 | |
|     HEIMDAL_MUTEX_lock(&tls_key_defs_lock);
 | |
|     key_defs = tls_key_defs;
 | |
|     HEIMDAL_MUTEX_unlock(&tls_key_defs_lock);
 | |
| 
 | |
|     if (key_defs == NULL)
 | |
|         return;
 | |
| 
 | |
|     for (i = 0; i < values.values_num; i++) {
 | |
|         assert(i >= key_defs->keys_start_idx);
 | |
|         if (i >= key_defs->keys_start_idx + key_defs->keys_num) {
 | |
|             HEIMDAL_MUTEX_lock(&tls_key_defs_lock);
 | |
|             key_defs = key_defs->keys_next;
 | |
|             HEIMDAL_MUTEX_unlock(&tls_key_defs_lock);
 | |
| 
 | |
|             assert(key_defs != NULL);
 | |
|             assert(i >= key_defs->keys_start_idx);
 | |
|             assert(i < key_defs->keys_start_idx + key_defs->keys_num);
 | |
|         }
 | |
|         dtor = key_defs->keys_dtors[i - key_defs->keys_start_idx];
 | |
|         if (values.values[i] != NULL && dtor != NULL && dtor != DEAD_KEY)
 | |
|             dtor(values.values[i]);
 | |
|         values.values[i] = NULL;
 | |
|     }
 | |
| }
 | |
| 
 | |
| #if !defined(WIN32)
 | |
| static pthread_key_t pt_key;
 | |
| pthread_once_t pt_once = PTHREAD_ONCE_INIT;
 | |
| 
 | |
| static void
 | |
| atexit_del_tls_for_thread(void)
 | |
| {
 | |
|     heim_w32_service_thread_detach(NULL);
 | |
| }
 | |
| 
 | |
| static void
 | |
| create_pt_key(void)
 | |
| {
 | |
|     int ret;
 | |
| 
 | |
|     /* The main thread may not execute TLS destructors */
 | |
|     atexit(atexit_del_tls_for_thread);
 | |
|     ret = pthread_key_create(&pt_key, heim_w32_service_thread_detach);
 | |
|     if (ret != 0)
 | |
|         err(1, "pthread_key_create() failed");
 | |
| }
 | |
| 
 | |
| #endif
 | |
| 
 | |
| int
 | |
| heim_w32_key_create(HEIM_PRIV_thread_key *key, void (*dtor)(void *))
 | |
| {
 | |
|     tls_keys *key_defs, *new_key_defs;
 | |
|     size_t i, k;
 | |
|     int ret = ENOMEM;
 | |
| 
 | |
| #if !defined(WIN32)
 | |
|     (void) pthread_once(&pt_once, create_pt_key);
 | |
|     (void) pthread_setspecific(pt_key, DEAD_KEY);
 | |
| #endif
 | |
| 
 | |
|     HEIMDAL_MUTEX_lock(&tls_key_defs_lock);
 | |
|     if (tls_key_defs == NULL) {
 | |
|         /* First key */
 | |
|         new_key_defs = calloc(1, sizeof(*new_key_defs));
 | |
|         if (new_key_defs == NULL) {
 | |
|             HEIMDAL_MUTEX_unlock(&tls_key_defs_lock);
 | |
|             return ENOMEM;
 | |
|         }
 | |
|         new_key_defs->keys_num = 8;
 | |
|         new_key_defs->keys_dtors = calloc(new_key_defs->keys_num,
 | |
|                                           sizeof(*new_key_defs->keys_dtors));
 | |
|         if (new_key_defs->keys_dtors == NULL) {
 | |
|             HEIMDAL_MUTEX_unlock(&tls_key_defs_lock);
 | |
|             free(new_key_defs);
 | |
|             return ENOMEM;
 | |
|         }
 | |
|         tls_key_defs = new_key_defs;
 | |
|         new_key_defs->keys_dtors[0] = dtor;
 | |
|         for (i = 1; i < new_key_defs->keys_num; i++)
 | |
|             new_key_defs->keys_dtors[i] = NULL;
 | |
|         HEIMDAL_MUTEX_unlock(&tls_key_defs_lock);
 | |
|         return 0;
 | |
|     }
 | |
| 
 | |
|     for (key_defs = tls_key_defs;
 | |
|          key_defs != NULL;
 | |
|          key_defs = key_defs->keys_next) {
 | |
|         k = key_defs->keys_start_idx;
 | |
|         for (i = 0; i < key_defs->keys_num; i++, k++) {
 | |
|             if (key_defs->keys_dtors[i] == NULL) {
 | |
|                 /* Found free slot; use it */
 | |
|                 key_defs->keys_dtors[i] = dtor;
 | |
|                 *key = k;
 | |
|                 HEIMDAL_MUTEX_unlock(&tls_key_defs_lock);
 | |
|                 return 0;
 | |
|             }
 | |
|         }
 | |
|         if (key_defs->keys_next != NULL)
 | |
|             continue;
 | |
| 
 | |
|         /* Grow the registration array */
 | |
|         /* XXX DRY */
 | |
|         new_key_defs = calloc(1, sizeof(*new_key_defs));
 | |
|         if (new_key_defs == NULL)
 | |
|             break;
 | |
| 
 | |
|         new_key_defs->keys_dtors =
 | |
|             calloc(key_defs->keys_num + key_defs->keys_num / 2,
 | |
|                    sizeof(*new_key_defs->keys_dtors));
 | |
|         if (new_key_defs->keys_dtors == NULL) {
 | |
|             free(new_key_defs);
 | |
|             break;
 | |
|         }
 | |
|         new_key_defs->keys_start_idx = key_defs->keys_start_idx +
 | |
|             key_defs->keys_num;
 | |
|         new_key_defs->keys_num = key_defs->keys_num + key_defs->keys_num / 2;
 | |
|         new_key_defs->keys_dtors[i] = dtor;
 | |
|         for (i = 1; i < new_key_defs->keys_num; i++)
 | |
|             new_key_defs->keys_dtors[i] = NULL;
 | |
|         key_defs->keys_next = new_key_defs;
 | |
|         ret = 0;
 | |
|         break;
 | |
|     }
 | |
|     HEIMDAL_MUTEX_unlock(&tls_key_defs_lock);
 | |
|     return ret;
 | |
| }
 | |
| 
 | |
| static void
 | |
| key_lookup(HEIM_PRIV_thread_key key, tls_keys **kd,
 | |
|            size_t *dtor_idx, void (**dtor)(void *))
 | |
| {
 | |
|     tls_keys *key_defs;
 | |
| 
 | |
|     if (kd != NULL)
 | |
|         *kd = NULL;
 | |
|     if (dtor_idx != NULL)
 | |
|         *dtor_idx = 0;
 | |
|     if (dtor != NULL)
 | |
|         *dtor = NULL;
 | |
| 
 | |
|     HEIMDAL_MUTEX_lock(&tls_key_defs_lock);
 | |
|     key_defs = tls_key_defs;
 | |
|     HEIMDAL_MUTEX_unlock(&tls_key_defs_lock);
 | |
| 
 | |
|     while (key_defs != NULL) {
 | |
|         if (key >= key_defs->keys_start_idx &&
 | |
|             key < key_defs->keys_start_idx + key_defs->keys_num) {
 | |
|             if (kd != NULL)
 | |
|                 *kd = key_defs;
 | |
|             if (dtor_idx != NULL)
 | |
|                 *dtor_idx = key - key_defs->keys_start_idx;
 | |
|             if (dtor != NULL)
 | |
|                 *dtor = key_defs->keys_dtors[key - key_defs->keys_start_idx];
 | |
|             return;
 | |
|         }
 | |
| 
 | |
|         HEIMDAL_MUTEX_lock(&tls_key_defs_lock);
 | |
|         key_defs = key_defs->keys_next;
 | |
|         HEIMDAL_MUTEX_unlock(&tls_key_defs_lock);
 | |
|         assert(key_defs != NULL);
 | |
|         assert(key >= key_defs->keys_start_idx);
 | |
|     }
 | |
| }
 | |
| 
 | |
| int
 | |
| heim_w32_delete_key(HEIM_PRIV_thread_key key)
 | |
| {
 | |
|     tls_keys *key_defs;
 | |
|     size_t dtor_idx;
 | |
| 
 | |
|     key_lookup(key, &key_defs, &dtor_idx, NULL);
 | |
|     if (key_defs == NULL)
 | |
|         return EINVAL;
 | |
|     key_defs->keys_dtors[dtor_idx] = DEAD_KEY;
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| int
 | |
| heim_w32_setspecific(HEIM_PRIV_thread_key key, void *value)
 | |
| {
 | |
|     void **new_values;
 | |
|     size_t new_num;
 | |
|     void (*dtor)(void *);
 | |
|     size_t i;
 | |
| 
 | |
| #if !defined(WIN32)
 | |
|     (void) pthread_setspecific(pt_key, DEAD_KEY);
 | |
| #endif
 | |
| 
 | |
|     key_lookup(key, NULL, NULL, &dtor);
 | |
|     if (dtor == NULL)
 | |
|         return EINVAL;
 | |
| 
 | |
|     if (key >= values.values_num) {
 | |
|         if (values.values_num == 0) {
 | |
|             values.values = NULL;
 | |
|             new_num = 8;
 | |
|         } else {
 | |
|             new_num = (values.values_num + values.values_num / 2);
 | |
|         }
 | |
|         new_values = realloc(values.values, sizeof(void *) * new_num);
 | |
|         if (new_values == NULL)
 | |
|             return ENOMEM;
 | |
|         for (i = values.values_num; i < new_num; i++)
 | |
|             new_values[i] = NULL;
 | |
|         values.values = new_values;
 | |
|         values.values_num = new_num;
 | |
|     }
 | |
| 
 | |
|     assert(key < values.values_num);
 | |
| 
 | |
|     if (values.values[key] != NULL && dtor != NULL && dtor != DEAD_KEY)
 | |
|         dtor(values.values[key]);
 | |
| 
 | |
|     values.values[key] = value;
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| void *
 | |
| heim_w32_getspecific(HEIM_PRIV_thread_key key)
 | |
| {
 | |
|     if (key >= values.values_num)
 | |
|         return NULL;
 | |
|     return values.values[key];
 | |
| }
 | |
| 
 | |
| #else
 | |
| static char dummy;
 | |
| #endif /* HEIM_WIN32_TLS */
 | 
