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 13static pthread_key sKeyTable[PTHREAD_KEYS_MAX]; 14static int32 sNextSequence = 1; 15 16 17/*! Retrieves the destructor of a key locklessly. 18 Returns the destructor's sequence in \a sequence. 19*/ 20static pthread_key_destructor 21get_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*/ 42static void* 43get_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 63void 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 80int 81pthread_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 105int 106pthread_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 120void* 121pthread_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 138int 139pthread_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