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/logging/log.h>
11 #include <zephyr/posix/pthread.h>
12 #include <zephyr/sys/bitarray.h>
13 #include <zephyr/sys/__assert.h>
14 #include <zephyr/sys/sem.h>
15
16 LOG_MODULE_REGISTER(pthread_key, CONFIG_PTHREAD_KEY_LOG_LEVEL);
17
18 SYS_SEM_DEFINE(pthread_key_lock, 1, 1);
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_POSIX_THREAD_KEYS_MAX < PTHREAD_OBJ_MASK_INIT,
29 "CONFIG_MAX_PTHREAD_KEY_COUNT is too high");
30
31 static pthread_key_obj posix_key_pool[CONFIG_POSIX_THREAD_KEYS_MAX];
32 SYS_BITARRAY_DEFINE_STATIC(posix_key_bitarray, CONFIG_POSIX_THREAD_KEYS_MAX);
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 LOG_DBG("Key is uninitialized (%x)", 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 LOG_DBG("Key is invalid (%x)", key);
58 return NULL;
59 }
60
61 if (actually_initialized == 0) {
62 /* The cond claims to be initialized but is actually not */
63 LOG_DBG("Key claims to be initialized (%x)", key);
64 return NULL;
65 }
66
67 return &posix_key_pool[bit];
68 }
69
to_posix_key(pthread_key_t * key)70 static pthread_key_obj *to_posix_key(pthread_key_t *key)
71 {
72 size_t bit;
73 pthread_key_obj *k;
74
75 if (*key != PTHREAD_KEY_INITIALIZER) {
76 return get_posix_key(*key);
77 }
78
79 /* Try and automatically associate a pthread_key_obj */
80 if (sys_bitarray_alloc(&posix_key_bitarray, 1, &bit) < 0) {
81 /* No keys left to allocate */
82 return NULL;
83 }
84
85 /* Record the associated posix_cond in mu and mark as initialized */
86 *key = mark_pthread_obj_initialized(bit);
87 k = &posix_key_pool[bit];
88
89 /* Initialize the condition variable here */
90 memset(k, 0, sizeof(*k));
91
92 return k;
93 }
94
95 /**
96 * @brief Create a key for thread-specific data
97 *
98 * See IEEE 1003.1
99 */
pthread_key_create(pthread_key_t * key,void (* destructor)(void *))100 int pthread_key_create(pthread_key_t *key,
101 void (*destructor)(void *))
102 {
103 pthread_key_obj *new_key;
104
105 *key = PTHREAD_KEY_INITIALIZER;
106 new_key = to_posix_key(key);
107 if (new_key == NULL) {
108 return ENOMEM;
109 }
110
111 sys_slist_init(&(new_key->key_data_l));
112
113 new_key->destructor = destructor;
114 LOG_DBG("Initialized key %p (%x)", new_key, *key);
115
116 return 0;
117 }
118
119 /**
120 * @brief Delete a key for thread-specific data
121 *
122 * See IEEE 1003.1
123 */
pthread_key_delete(pthread_key_t key)124 int pthread_key_delete(pthread_key_t key)
125 {
126 size_t bit;
127 int ret = EINVAL;
128 pthread_key_obj *key_obj = NULL;
129 struct pthread_key_data *key_data;
130 sys_snode_t *node_l, *next_node_l;
131
132 SYS_SEM_LOCK(&pthread_key_lock) {
133 key_obj = get_posix_key(key);
134 if (key_obj == NULL) {
135 ret = EINVAL;
136 SYS_SEM_LOCK_BREAK;
137 }
138
139 /* Delete thread-specific elements associated with the key */
140 SYS_SLIST_FOR_EACH_NODE_SAFE(&(key_obj->key_data_l), node_l, next_node_l) {
141
142 /* Remove the object from the list key_data_l */
143 key_data = (struct pthread_key_data *)sys_slist_get(&(key_obj->key_data_l));
144
145 /* Deallocate the object's memory */
146 k_free((void *)key_data);
147 LOG_DBG("Freed key data %p for key %x in thread %x", key_data, key,
148 pthread_self());
149 }
150
151 bit = posix_key_to_offset(key_obj);
152 ret = sys_bitarray_free(&posix_key_bitarray, 1, bit);
153 __ASSERT_NO_MSG(ret == 0);
154 }
155
156 if (ret == 0) {
157 LOG_DBG("Deleted key %p (%x)", key_obj, key);
158 }
159
160 return ret;
161 }
162
163 /**
164 * @brief Associate a thread-specific value with a key
165 *
166 * See IEEE 1003.1
167 */
pthread_setspecific(pthread_key_t key,const void * value)168 int pthread_setspecific(pthread_key_t key, const void *value)
169 {
170 pthread_key_obj *key_obj = NULL;
171 struct posix_thread *thread;
172 struct pthread_key_data *key_data;
173 sys_snode_t *node_l = NULL;
174 int retval = 0;
175
176 thread = to_posix_thread(pthread_self());
177 if (thread == NULL) {
178 return EINVAL;
179 }
180
181 /* Traverse the list of keys set by the thread, looking for key.
182 * If the key is already in the list, re-assign its value.
183 * Else add the key to the thread's list.
184 */
185 SYS_SEM_LOCK(&pthread_key_lock) {
186 key_obj = get_posix_key(key);
187 if (key_obj == NULL) {
188 retval = EINVAL;
189 SYS_SEM_LOCK_BREAK;
190 }
191
192 SYS_SLIST_FOR_EACH_NODE(&(thread->key_list), node_l) {
193 pthread_thread_data *thread_spec_data = (pthread_thread_data *)node_l;
194
195 if (thread_spec_data->key == key_obj) {
196 /* Key is already present so associate thread specific data */
197 thread_spec_data->spec_data = (void *)value;
198 LOG_DBG("Paired key %x to value %p for thread %x", key, value,
199 pthread_self());
200 break;
201 }
202 }
203
204 retval = 0;
205 if (node_l != NULL) {
206 /* Key is already present, so we are done */
207 SYS_SEM_LOCK_BREAK;
208 }
209
210 /* Key and data need to be added */
211 key_data = k_malloc(sizeof(struct pthread_key_data));
212
213 if (key_data == NULL) {
214 LOG_DBG("Failed to allocate key data for key %x", key);
215 retval = ENOMEM;
216 SYS_SEM_LOCK_BREAK;
217 }
218
219 LOG_DBG("Allocated key data %p for key %x in thread %x", key_data, key,
220 pthread_self());
221
222 /* Associate thread specific data, initialize new key */
223 key_data->thread_data.key = key_obj;
224 key_data->thread_data.spec_data = (void *)value;
225
226 /* Append new thread key data to thread's key list */
227 sys_slist_append((&thread->key_list), (sys_snode_t *)(&key_data->thread_data));
228
229 /* Append new key data to the key object's list */
230 sys_slist_append(&(key_obj->key_data_l), (sys_snode_t *)key_data);
231
232 LOG_DBG("Paired key %x to value %p for thread %x", key, value, pthread_self());
233 }
234
235 return retval;
236 }
237
238 /**
239 * @brief Get the thread-specific value associated with the key
240 *
241 * See IEEE 1003.1
242 */
pthread_getspecific(pthread_key_t key)243 void *pthread_getspecific(pthread_key_t key)
244 {
245 pthread_key_obj *key_obj;
246 struct posix_thread *thread;
247 pthread_thread_data *thread_spec_data;
248 void *value = NULL;
249 sys_snode_t *node_l;
250
251 thread = to_posix_thread(pthread_self());
252 if (thread == NULL) {
253 return NULL;
254 }
255
256 SYS_SEM_LOCK(&pthread_key_lock) {
257 key_obj = get_posix_key(key);
258 if (key_obj == NULL) {
259 value = NULL;
260 SYS_SEM_LOCK_BREAK;
261 }
262
263 /* Traverse the list of keys set by the thread, looking for key */
264
265 SYS_SLIST_FOR_EACH_NODE(&(thread->key_list), node_l) {
266 thread_spec_data = (pthread_thread_data *)node_l;
267 if (thread_spec_data->key == key_obj) {
268 /* Key is present, so get the set thread data */
269 value = thread_spec_data->spec_data;
270 break;
271 }
272 }
273 }
274
275 return value;
276 }
277