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