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