1 /*
2 * Copyright (c) 2018 Intel Corporation
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6 #include "posix_internal.h"
7
8 #include <zephyr/kernel.h>
9 #include <zephyr/posix/pthread.h>
10 #include <zephyr/posix/pthread_key.h>
11 #include <zephyr/sys/bitarray.h>
12
13 struct pthread_key_data {
14 sys_snode_t node;
15 pthread_thread_data thread_data;
16 };
17
18 static struct k_spinlock pthread_key_lock;
19
20 /* This is non-standard (i.e. an implementation detail) */
21 #define PTHREAD_KEY_INITIALIZER (-1)
22
23 /*
24 * We reserve the MSB to mark a pthread_key_t as initialized (from the
25 * perspective of the application). With a linear space, this means that
26 * the theoretical pthread_key_t range is [0,2147483647].
27 */
28 BUILD_ASSERT(CONFIG_MAX_PTHREAD_KEY_COUNT < PTHREAD_OBJ_MASK_INIT,
29 "CONFIG_MAX_PTHREAD_KEY_COUNT is too high");
30
31 static pthread_key_obj posix_key_pool[CONFIG_MAX_PTHREAD_KEY_COUNT];
32 SYS_BITARRAY_DEFINE_STATIC(posix_key_bitarray, CONFIG_MAX_PTHREAD_KEY_COUNT);
33
posix_key_to_offset(pthread_key_obj * k)34 static inline size_t posix_key_to_offset(pthread_key_obj *k)
35 {
36 return k - posix_key_pool;
37 }
38
to_posix_key_idx(pthread_key_t key)39 static inline size_t to_posix_key_idx(pthread_key_t key)
40 {
41 return mark_pthread_obj_uninitialized(key);
42 }
43
get_posix_key(pthread_key_t key)44 static pthread_key_obj *get_posix_key(pthread_key_t key)
45 {
46 int actually_initialized;
47 size_t bit = to_posix_key_idx(key);
48
49 /* if the provided cond does not claim to be initialized, its invalid */
50 if (!is_pthread_obj_initialized(key)) {
51 return NULL;
52 }
53
54 /* Mask off the MSB to get the actual bit index */
55 if (sys_bitarray_test_bit(&posix_key_bitarray, bit, &actually_initialized) < 0) {
56 return NULL;
57 }
58
59 if (actually_initialized == 0) {
60 /* The cond claims to be initialized but is actually not */
61 return NULL;
62 }
63
64 return &posix_key_pool[bit];
65 }
66
to_posix_key(pthread_key_t * key)67 static pthread_key_obj *to_posix_key(pthread_key_t *key)
68 {
69 size_t bit;
70 pthread_key_obj *k;
71
72 if (*key != PTHREAD_KEY_INITIALIZER) {
73 return get_posix_key(*key);
74 }
75
76 /* Try and automatically associate a pthread_key_obj */
77 if (sys_bitarray_alloc(&posix_key_bitarray, 1, &bit) < 0) {
78 /* No keys left to allocate */
79 return NULL;
80 }
81
82 /* Record the associated posix_cond in mu and mark as initialized */
83 *key = mark_pthread_obj_initialized(bit);
84 k = &posix_key_pool[bit];
85
86 /* Initialize the condition variable here */
87 memset(k, 0, sizeof(*k));
88
89 return k;
90 }
91
92 /**
93 * @brief Create a key for thread-specific data
94 *
95 * See IEEE 1003.1
96 */
pthread_key_create(pthread_key_t * key,void (* destructor)(void *))97 int pthread_key_create(pthread_key_t *key,
98 void (*destructor)(void *))
99 {
100 pthread_key_obj *new_key;
101
102 *key = PTHREAD_KEY_INITIALIZER;
103 new_key = to_posix_key(key);
104 if (new_key == NULL) {
105 return ENOMEM;
106 }
107
108 sys_slist_init(&(new_key->key_data_l));
109
110 new_key->destructor = destructor;
111
112 return 0;
113 }
114
115 /**
116 * @brief Delete a key for thread-specific data
117 *
118 * See IEEE 1003.1
119 */
pthread_key_delete(pthread_key_t key)120 int pthread_key_delete(pthread_key_t key)
121 {
122 pthread_key_obj *key_obj;
123 struct pthread_key_data *key_data;
124 sys_snode_t *node_l, *next_node_l;
125 k_spinlock_key_t key_key;
126
127 key_key = k_spin_lock(&pthread_key_lock);
128
129 key_obj = get_posix_key(key);
130 if (key_obj == NULL) {
131 k_spin_unlock(&pthread_key_lock, key_key);
132 return EINVAL;
133 }
134
135 /* Delete thread-specific elements associated with the key */
136 SYS_SLIST_FOR_EACH_NODE_SAFE(&(key_obj->key_data_l),
137 node_l, next_node_l) {
138
139 /* Remove the object from the list key_data_l */
140 key_data = (struct pthread_key_data *)
141 sys_slist_get(&(key_obj->key_data_l));
142
143 /* Deallocate the object's memory */
144 k_free((void *)key_data);
145 }
146
147 (void)sys_bitarray_free(&posix_key_bitarray, 1, 0);
148
149 k_spin_unlock(&pthread_key_lock, key_key);
150
151 return 0;
152 }
153
154 /**
155 * @brief Associate a thread-specific value with a key
156 *
157 * See IEEE 1003.1
158 */
pthread_setspecific(pthread_key_t key,const void * value)159 int pthread_setspecific(pthread_key_t key, const void *value)
160 {
161 pthread_key_obj *key_obj;
162 struct posix_thread *thread = to_posix_thread(pthread_self());
163 struct pthread_key_data *key_data;
164 pthread_thread_data *thread_spec_data;
165 k_spinlock_key_t key_key;
166 sys_snode_t *node_l;
167 int retval = 0;
168
169 /* Traverse the list of keys set by the thread, looking for key.
170 * If the key is already in the list, re-assign its value.
171 * Else add the key to the thread's list.
172 */
173 key_key = k_spin_lock(&pthread_key_lock);
174
175 key_obj = get_posix_key(key);
176 if (key_obj == NULL) {
177 k_spin_unlock(&pthread_key_lock, key_key);
178 return EINVAL;
179 }
180
181 SYS_SLIST_FOR_EACH_NODE(&(thread->key_list), node_l) {
182
183 thread_spec_data = (pthread_thread_data *)node_l;
184
185 if (thread_spec_data->key == key_obj) {
186
187 /* Key is already present so
188 * associate thread specific data
189 */
190 thread_spec_data->spec_data = (void *)value;
191 goto out;
192 }
193 }
194
195 if (node_l == NULL) {
196 key_data = k_malloc(sizeof(struct pthread_key_data));
197
198 if (key_data == NULL) {
199 retval = ENOMEM;
200 goto out;
201
202 } else {
203 /* Associate thread specific data, initialize new key */
204 key_data->thread_data.key = key_obj;
205 key_data->thread_data.spec_data = (void *)value;
206
207 /* Append new thread key data to thread's key list */
208 sys_slist_append((&thread->key_list),
209 (sys_snode_t *)(&key_data->thread_data));
210
211 /* Append new key data to the key object's list */
212 sys_slist_append(&(key_obj->key_data_l),
213 (sys_snode_t *)key_data);
214 }
215 }
216
217 out:
218 k_spin_unlock(&pthread_key_lock, key_key);
219
220 return retval;
221 }
222
223 /**
224 * @brief Get the thread-specific value associated with the key
225 *
226 * See IEEE 1003.1
227 */
pthread_getspecific(pthread_key_t key)228 void *pthread_getspecific(pthread_key_t key)
229 {
230 pthread_key_obj *key_obj;
231 struct posix_thread *thread = to_posix_thread(pthread_self());
232 pthread_thread_data *thread_spec_data;
233 void *value = NULL;
234 sys_snode_t *node_l;
235 k_spinlock_key_t key_key;
236
237 key_key = k_spin_lock(&pthread_key_lock);
238
239 key_obj = get_posix_key(key);
240 if (key_obj == NULL) {
241 k_spin_unlock(&pthread_key_lock, key_key);
242 return NULL;
243 }
244
245 node_l = sys_slist_peek_head(&(thread->key_list));
246
247 /* Traverse the list of keys set by the thread, looking for key */
248
249 SYS_SLIST_FOR_EACH_NODE(&(thread->key_list), node_l) {
250 thread_spec_data = (pthread_thread_data *)node_l;
251 if (thread_spec_data->key == key_obj) {
252 /* Key is present, so get the set thread data */
253 value = thread_spec_data->spec_data;
254 break;
255 }
256 }
257
258 k_spin_unlock(&pthread_key_lock, key_key);
259
260 return value;
261 }
262