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
get_key_destructor(uint32 key,int32 & sequence)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*
get_key_value(pthread_thread * thread,uint32 key,int32 sequence)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
__pthread_key_call_destructors(pthread_thread * thread)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
pthread_key_create(pthread_key_t * _key,void (* destructor)(void *))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
pthread_key_delete(pthread_key_t key)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*
pthread_getspecific(pthread_key_t key)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
pthread_setspecific(pthread_key_t key,const void * value)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