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