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