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.
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	return value;
58}
59
60
61void
62__pthread_key_call_destructors(pthread_thread* thread)
63{
64	for (uint32 key = 0; key < PTHREAD_KEYS_MAX; key++) {
65		int32 sequence;
66		pthread_key_destructor destructor = get_key_destructor(key, sequence);
67		void* value = get_key_value(thread, key, sequence);
68
69		if (value != NULL && destructor != NULL)
70			destructor(value);
71	}
72}
73
74
75//	#pragma mark - public API
76
77
78int
79pthread_key_create(pthread_key_t* _key, void (*destructor)(void*))
80{
81	int32 nextSequence = atomic_add(&sNextSequence, 1);
82
83	for (uint32 key = 0; key < PTHREAD_KEYS_MAX; key++) {
84		int32 sequence = sKeyTable[key].sequence;
85		if (sequence != PTHREAD_UNUSED_SEQUENCE)
86			continue;
87
88		// try to acquire this slot
89
90		if (atomic_test_and_set(&sKeyTable[key].sequence, nextSequence,
91				sequence) != sequence)
92			continue;
93
94		sKeyTable[key].destructor = destructor;
95		*_key = key;
96		return 0;
97	}
98
99	return EAGAIN;
100}
101
102
103int
104pthread_key_delete(pthread_key_t key)
105{
106	if (key < 0 || key >= PTHREAD_KEYS_MAX)
107		return EINVAL;
108
109	int32 sequence = atomic_set(&sKeyTable[key].sequence,
110		PTHREAD_UNUSED_SEQUENCE);
111	if (sequence == PTHREAD_UNUSED_SEQUENCE)
112		return EINVAL;
113
114	return 0;
115}
116
117
118void*
119pthread_getspecific(pthread_key_t key)
120{
121	pthread_thread* thread = pthread_self();
122
123	if (key < 0 || key >= PTHREAD_KEYS_MAX)
124		return NULL;
125
126	// check if this key is used, and our value belongs to its current meaning
127	int32 sequence = atomic_get(&sKeyTable[key].sequence);
128	if (sequence == PTHREAD_UNUSED_SEQUENCE
129		|| thread->specific[key].sequence != sequence)
130		return NULL;
131
132	return thread->specific[key].value;
133}
134
135
136int
137pthread_setspecific(pthread_key_t key, const void* value)
138{
139	if (key < 0 || key >= PTHREAD_KEYS_MAX)
140		return EINVAL;
141
142	int32 sequence = atomic_get(&sKeyTable[key].sequence);
143	if (sequence == PTHREAD_UNUSED_SEQUENCE)
144		return EINVAL;
145
146	pthread_key_data& keyData = pthread_self()->specific[key];
147	keyData.sequence = sequence;
148	keyData.value = const_cast<void*>(value);
149	return 0;
150}
151
152