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