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