xref: /haiku/src/system/libroot/posix/pthread/pthread_key.cpp (revision 3802df804f6dfc8ec4fd94b0619e6b0afb96ebf2)
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