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