1 /*
2 * Copyright (c) 2006-2024 RT-Thread Development Team
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 *
6 * Change Logs:
7 * Date Author Notes
8 * 2010-10-26 Bernard the first version
9 */
10
11 #include <pthread.h>
12 #include "pthread_internal.h"
13
14 _pthread_key_data_t _thread_keys[PTHREAD_KEY_MAX];
15
16 /* initialize key area */
pthread_key_system_init(void)17 static int pthread_key_system_init(void)
18 {
19 rt_memset(&_thread_keys[0], 0, sizeof(_thread_keys));
20 return 0;
21 }
22 INIT_COMPONENT_EXPORT(pthread_key_system_init);
23
24 /**
25 * @brief Retrieves the value associated with a thread-specific data key for the calling thread.
26 *
27 * This function returns the value that was previously set for the specified key
28 * in the calling thread using `pthread_setspecific`. Each thread has its own independent
29 * value for the same key.
30 *
31 * @param[in] key
32 * The thread-specific data key, created using `pthread_key_create`.
33 *
34 * @return
35 * - The value associated with the key for the calling thread, or `NULL` if no value
36 * has been set for the key in this thread.
37 *
38 * @note
39 * - If no value has been set for the key in the calling thread, `pthread_getspecific`
40 * returns `NULL`. This does not indicate an error unless `NULL` was explicitly set as the value.
41 * - The value retrieved is specific to the calling thread and may differ for other threads.
42 *
43 * @attention
44 * - If the key has been deleted using `pthread_key_delete`, the behavior of this
45 * function is undefined.
46 *
47 * @see pthread_key_create(), pthread_setspecific(), pthread_key_delete()
48 */
pthread_getspecific(pthread_key_t key)49 void *pthread_getspecific(pthread_key_t key)
50 {
51 struct _pthread_data* ptd;
52
53 if (rt_thread_self() == NULL) return NULL;
54
55 /* get pthread data from user data of thread */
56 ptd = (_pthread_data_t *)rt_thread_self()->pthread_data;
57 RT_ASSERT(ptd != NULL);
58
59 if (ptd->tls == NULL)
60 return NULL;
61
62 if ((key < PTHREAD_KEY_MAX) && (_thread_keys[key].is_used))
63 return ptd->tls[key];
64
65 return NULL;
66 }
67 RTM_EXPORT(pthread_getspecific);
68
69 /**
70 * @brief Associates a value with a thread-specific data key for the calling thread.
71 *
72 * This function sets a thread-specific value for the given key. Each thread has its
73 * own independent value associated with the same key, and this value is not shared with
74 * other threads.
75 *
76 * @param[in] key
77 * The thread-specific data key, created using `pthread_key_create`.
78 * @param[in] value
79 * The value to associate with the key for the calling thread. The value can be
80 * a pointer to any data or `NULL`.
81 *
82 * @return
83 * - `0`: The value was successfully set.
84 * - `EINVAL`: The specified key is invalid or not initialized.
85 *
86 * @note
87 * - Setting a new value for a key in a thread overwrites any previously set value
88 * for that key in the same thread.
89 * - The value set using `pthread_setspecific` is accessible via `pthread_getspecific`
90 * for the same thread.
91 * - The association between the key and value is valid until the thread terminates,
92 * the key is deleted using `pthread_key_delete`, or a new value is set with this function.
93 *
94 * @attention
95 * - The value is specific to the calling thread; other threads will not be affected
96 * and will continue to maintain their own independent values for the same key.
97 *
98 * @see pthread_key_create(), pthread_getspecific(), pthread_key_delete()
99 */
pthread_setspecific(pthread_key_t key,const void * value)100 int pthread_setspecific(pthread_key_t key, const void *value)
101 {
102 struct _pthread_data* ptd;
103
104 if (rt_thread_self() == NULL) return EINVAL;
105
106 /* get pthread data from user data of thread */
107 ptd = (_pthread_data_t *)rt_thread_self()->pthread_data;
108 RT_ASSERT(ptd != NULL);
109
110 /* check tls area */
111 if (ptd->tls == NULL)
112 {
113 ptd->tls = (void**)rt_calloc(PTHREAD_KEY_MAX, sizeof(void*));
114 }
115
116 if ((key < PTHREAD_KEY_MAX) && _thread_keys[key].is_used)
117 {
118 ptd->tls[key] = (void *)value;
119
120 return 0;
121 }
122
123 return EINVAL;
124 }
125 RTM_EXPORT(pthread_setspecific);
126
127 /**
128 * @brief Creates a thread-specific data key.
129 *
130 * This function allocates a unique key for thread-specific data (TSD).
131 * Each thread can use this key to access its own specific data associated with it.
132 *
133 * @param[out] key
134 * A pointer to a variable where the newly created key will be stored.
135 * On success, `*key` will hold the newly allocated key.
136 * @param[in] destructor
137 * An optional destructor function pointer. This function will be called to
138 * clean up thread-specific data associated with the key when a thread terminates.
139 * Pass `NULL` if no cleanup is required.
140 *
141 * @return
142 * - `0`: The key was successfully created.
143 * - `EAGAIN`: The system has reached the maximum number of available keys, and no new key can be allocated.
144 *
145 * @note
146 * - Each thread can use `pthread_setspecific` and `pthread_getspecific` to set and retrieve the thread-specific data associated with the key.
147 * - The destructor function will be invoked automatically by the system when the thread terminates, if not explicitly called.
148 *
149 * @attention
150 * - If the `destructor` function is invoked and it sets thread-specific data for the same key,
151 * the destructor may be called multiple times, up to a limit defined by the system
152 * (typically `PTHREAD_DESTRUCTOR_ITERATIONS`).
153 *
154 * @see pthread_setspecific(), pthread_getspecific(), pthread_key_delete()
155 */
pthread_key_create(pthread_key_t * key,void (* destructor)(void *))156 int pthread_key_create(pthread_key_t *key, void (*destructor)(void*))
157 {
158 rt_uint32_t index;
159
160 rt_enter_critical();
161 for (index = 0; index < PTHREAD_KEY_MAX; index ++)
162 {
163 if (_thread_keys[index].is_used == 0)
164 {
165 _thread_keys[index].is_used = 1;
166 _thread_keys[index].destructor = destructor;
167
168 *key = index;
169
170 rt_exit_critical();
171
172 return 0;
173 }
174 }
175
176 rt_exit_critical();
177
178 return EAGAIN;
179 }
180 RTM_EXPORT(pthread_key_create);
181
182 /**
183 * @brief Deletes a thread-specific data key.
184 *
185 * This function deletes a previously created thread-specific data key.
186 * The key will no longer be valid for use in `pthread_setspecific` or `pthread_getspecific`.
187 *
188 * @param[in] key
189 * The thread-specific data key to delete. The key must have been created with
190 * `pthread_key_create`.
191 *
192 * @return
193 * - `0`: The key was successfully deleted.
194 * - `EINVAL`: The specified key is invalid or has not been initialized.
195 *
196 * @note
197 * - Deleting a key does not automatically free or clean up the thread-specific data
198 * associated with the key. It is the programmer's responsibility to ensure that
199 * associated resources are properly released, if necessary.
200 * - After deletion, using the key in `pthread_setspecific` or `pthread_getspecific`
201 * will result in undefined behavior.
202 *
203 * @see pthread_key_create(), pthread_setspecific(), pthread_getspecific()
204 */
pthread_key_delete(pthread_key_t key)205 int pthread_key_delete(pthread_key_t key)
206 {
207 if (key >= PTHREAD_KEY_MAX)
208 return EINVAL;
209
210 rt_enter_critical();
211 _thread_keys[key].is_used = 0;
212 _thread_keys[key].destructor = 0;
213 rt_exit_critical();
214
215 return 0;
216 }
217 RTM_EXPORT(pthread_key_delete);
218
219