1 /*
2 * Copyright (c) 2018 Intel Corporation
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <pthread.h>
8
9 #include <zephyr/sys/util.h>
10 #include <zephyr/ztest.h>
11
12 #define N_THR 2
13 #define N_KEY 2
14 #define BUFFSZ 48
15
16 static pthread_key_t key;
17 static pthread_key_t keys[N_KEY];
18 static pthread_once_t key_once = PTHREAD_ONCE_INIT;
19 static pthread_once_t keys_once = PTHREAD_ONCE_INIT;
20
thread_top(void * p1)21 static void *thread_top(void *p1)
22 {
23 void *value;
24 char *buffer[BUFFSZ];
25
26 value = k_malloc(sizeof(buffer));
27 zassert_not_null(value, "thread could not allocate storage");
28 zassert_ok(pthread_setspecific(key, value), "pthread_setspecific failed");
29 zassert_equal(pthread_getspecific(key), value, "set and retrieved values are different");
30 k_free(value);
31
32 return NULL;
33 }
34
thread_func(void * p1)35 static void *thread_func(void *p1)
36 {
37 void *value;
38 char *buffer[BUFFSZ];
39
40 value = k_malloc(sizeof(buffer));
41 zassert_not_null(value, "thread could not allocate storage");
42 for (int i = 0; i < N_KEY; i++) {
43 zassert_ok(pthread_setspecific(keys[i], value), "pthread_setspecific failed");
44 zassert_equal(pthread_getspecific(keys[i]), value,
45 "set and retrieved values are different");
46 }
47 k_free(value);
48 return NULL;
49 }
50
make_key(void)51 static void make_key(void)
52 {
53 zassert_ok(pthread_key_create(&key, NULL), "insufficient memory to create key");
54 }
55
make_keys(void)56 static void make_keys(void)
57 {
58 for (int i = 0; i < N_KEY; i++) {
59 zassert_ok(pthread_key_create(&keys[i], NULL),
60 "insufficient memory to create keys");
61 }
62 }
63
64 /**
65 * @brief Test to demonstrate pthread_key APIs usage
66 *
67 * @details The tests spawn a thread which uses pthread_once() to
68 * create a key via pthread_key_create() API. It then sets the
69 * thread-specific value to the key using pthread_setspecific() and
70 * gets it back using pthread_getspecific and asserts that they
71 * are equal. It then deletes the key using pthread_key_delete().
72 * Both the sub-tests do the above, but one with multiple threads
73 * using the same key and the other with a single thread using
74 * multiple keys.
75 */
76
ZTEST(key,test_key_1toN_thread)77 ZTEST(key, test_key_1toN_thread)
78 {
79 void *retval;
80 pthread_t newthread[N_THR];
81
82 zassert_ok(pthread_once(&key_once, make_key), "attempt to create key failed");
83
84 /* Different threads set different values to same key */
85
86 for (int i = 0; i < N_THR; i++) {
87 zassert_ok(pthread_create(&newthread[i], NULL, thread_top, NULL),
88 "attempt to create thread %d failed", i);
89 }
90
91 for (int i = 0; i < N_THR; i++) {
92 zassert_ok(pthread_join(newthread[i], &retval), "failed to join thread %d", i);
93 }
94
95 zassert_ok(pthread_key_delete(key), "attempt to delete key failed");
96 }
97
ZTEST(key,test_key_Nto1_thread)98 ZTEST(key, test_key_Nto1_thread)
99 {
100 pthread_t newthread;
101
102 zassert_ok(pthread_once(&keys_once, make_keys), "attempt to create keys failed");
103
104 /* Single thread associates its value with different keys */
105
106 zassert_ok(pthread_create(&newthread, NULL, thread_func, NULL),
107 "attempt to create thread failed");
108
109 zassert_ok(pthread_join(newthread, NULL), "failed to join thread");
110
111 for (int i = 0; i < N_KEY; i++) {
112 zassert_ok(pthread_key_delete(keys[i]), "attempt to delete keys[%d] failed", i);
113 }
114 }
115
ZTEST(key,test_key_resource_leak)116 ZTEST(key, test_key_resource_leak)
117 {
118 pthread_key_t key;
119
120 for (size_t i = 0; i < CONFIG_POSIX_THREAD_KEYS_MAX; ++i) {
121 zassert_ok(pthread_key_create(&key, NULL), "failed to create key %zu", i);
122 zassert_ok(pthread_key_delete(key), "failed to delete key %zu", i);
123 }
124 }
125
ZTEST(key,test_correct_key_is_deleted)126 ZTEST(key, test_correct_key_is_deleted)
127 {
128 pthread_key_t key;
129 size_t j = CONFIG_POSIX_THREAD_KEYS_MAX - 1;
130 pthread_key_t keys[CONFIG_POSIX_THREAD_KEYS_MAX];
131
132 for (size_t i = 0; i < ARRAY_SIZE(keys); ++i) {
133 zassert_ok(pthread_key_create(&keys[i], NULL), "failed to create key %zu", i);
134 }
135
136 key = keys[j];
137 zassert_ok(pthread_key_delete(keys[j]));
138 zassert_ok(pthread_key_create(&keys[j], NULL), "failed to create key %zu", j);
139
140 zassert_equal(key, keys[j], "deleted key %x instead of key %x", keys[j], key);
141
142 for (size_t i = 0; i < ARRAY_SIZE(keys); ++i) {
143 zassert_ok(pthread_key_delete(keys[i]), "failed to delete key %zu", i);
144 }
145 }
146
setspecific_thread(void * count)147 static void *setspecific_thread(void *count)
148 {
149 int value = 42;
150 int *alloc_count = count;
151
152 while (1) {
153 pthread_key_t key;
154
155 zassert_ok(pthread_key_create(&key, NULL), "failed to create key");
156 if (pthread_setspecific(key, &value) == ENOMEM) {
157 break;
158 };
159 *alloc_count += 1;
160 }
161
162 return NULL;
163 }
164
ZTEST(key,test_thread_specific_data_deallocation)165 ZTEST(key, test_thread_specific_data_deallocation)
166 {
167 pthread_t thread;
168 static int alloc_count_t0;
169 static int alloc_count_t1;
170
171 zassert_ok(pthread_create(&thread, NULL, setspecific_thread, &alloc_count_t0),
172 "attempt to create thread failed");
173 zassert_ok(pthread_join(thread, NULL), "failed to join thread");
174 printk("first thread allocated %d keys", alloc_count_t0);
175
176 zassert_ok(pthread_create(&thread, NULL, setspecific_thread, &alloc_count_t1),
177 "attempt to create thread failed");
178 zassert_ok(pthread_join(thread, NULL), "failed to join thread");
179 printk("second thread allocated %d keys", alloc_count_t1);
180
181 zassert_equal(alloc_count_t0, alloc_count_t1,
182 "failed to deallocate thread specific data");
183 }
184
before(void * arg)185 static void before(void *arg)
186 {
187 ARG_UNUSED(arg);
188
189 if (!IS_ENABLED(CONFIG_DYNAMIC_THREAD)) {
190 /* skip redundant testing if there is no thread pool / heap allocation */
191 ztest_test_skip();
192 }
193 }
194
195 ZTEST_SUITE(key, NULL, NULL, before, NULL, NULL);
196