1 /* 2 * Copyright 2008, Axel Dörfler, axeld@pinc-software.de. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 7 #include "pthread_private.h" 8 9 #include <limits.h> 10 #include <stdlib.h> 11 12 13 static pthread_key sKeyTable[PTHREAD_KEYS_MAX]; 14 static int32 sNextSequence = 1; 15 16 17 /*! Retrieves the destructor of a key locklessly. 18 Returns the destructor's sequence in \a sequence. 19 */ 20 static pthread_key_destructor 21 get_key_destructor(uint32 key, int32& sequence) 22 { 23 pthread_key_destructor destructor = NULL; 24 25 do { 26 sequence = sKeyTable[key].sequence; 27 if (sequence == PTHREAD_UNUSED_SEQUENCE) 28 return NULL; 29 30 destructor = sKeyTable[key].destructor; 31 } while (sKeyTable[key].sequence != sequence); 32 33 return destructor; 34 } 35 36 37 /*! Function to get the thread specific value of a key in a lockless 38 way. The thread specific value is reset to NULL. 39 \a sequence must be the sequence of the key table that this value 40 has to fit to. 41 */ 42 static void* 43 get_key_value(pthread_thread* thread, uint32 key, int32 sequence) 44 { 45 pthread_key_data& keyData = thread->specific[key]; 46 int32 specificSequence; 47 void* value; 48 49 do { 50 specificSequence = keyData.sequence; 51 if (specificSequence != sequence) 52 return NULL; 53 54 value = keyData.value; 55 } while (specificSequence != sequence); 56 57 keyData.value = NULL; 58 59 return value; 60 } 61 62 63 void 64 __pthread_key_call_destructors(pthread_thread* thread) 65 { 66 for (uint32 key = 0; key < PTHREAD_KEYS_MAX; key++) { 67 int32 sequence; 68 pthread_key_destructor destructor = get_key_destructor(key, sequence); 69 void* value = get_key_value(thread, key, sequence); 70 71 if (value != NULL && destructor != NULL) 72 destructor(value); 73 } 74 } 75 76 77 // #pragma mark - public API 78 79 80 int 81 pthread_key_create(pthread_key_t* _key, void (*destructor)(void*)) 82 { 83 int32 nextSequence = atomic_add(&sNextSequence, 1); 84 85 for (uint32 key = 0; key < PTHREAD_KEYS_MAX; key++) { 86 int32 sequence = sKeyTable[key].sequence; 87 if (sequence != PTHREAD_UNUSED_SEQUENCE) 88 continue; 89 90 // try to acquire this slot 91 92 if (atomic_test_and_set(&sKeyTable[key].sequence, nextSequence, 93 sequence) != sequence) 94 continue; 95 96 sKeyTable[key].destructor = destructor; 97 *_key = key; 98 return 0; 99 } 100 101 return EAGAIN; 102 } 103 104 105 int 106 pthread_key_delete(pthread_key_t key) 107 { 108 if (key < 0 || key >= PTHREAD_KEYS_MAX) 109 return EINVAL; 110 111 int32 sequence = atomic_get_and_set(&sKeyTable[key].sequence, 112 PTHREAD_UNUSED_SEQUENCE); 113 if (sequence == PTHREAD_UNUSED_SEQUENCE) 114 return EINVAL; 115 116 return 0; 117 } 118 119 120 void* 121 pthread_getspecific(pthread_key_t key) 122 { 123 pthread_thread* thread = pthread_self(); 124 125 if (key < 0 || key >= PTHREAD_KEYS_MAX) 126 return NULL; 127 128 // check if this key is used, and our value belongs to its current meaning 129 int32 sequence = atomic_get(&sKeyTable[key].sequence); 130 if (sequence == PTHREAD_UNUSED_SEQUENCE 131 || thread->specific[key].sequence != sequence) 132 return NULL; 133 134 return thread->specific[key].value; 135 } 136 137 138 int 139 pthread_setspecific(pthread_key_t key, const void* value) 140 { 141 if (key < 0 || key >= PTHREAD_KEYS_MAX) 142 return EINVAL; 143 144 int32 sequence = atomic_get(&sKeyTable[key].sequence); 145 if (sequence == PTHREAD_UNUSED_SEQUENCE) 146 return EINVAL; 147 148 pthread_key_data& keyData = pthread_self()->specific[key]; 149 keyData.sequence = sequence; 150 keyData.value = const_cast<void*>(value); 151 return 0; 152 } 153 154