1 /*
2 * Copyright (c) 2017 Intel Corporation
3 * Copyright (c) 2023 Meta
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 */
7
8 #include "posix_internal.h"
9
10 #include <zephyr/init.h>
11 #include <zephyr/kernel.h>
12 #include <zephyr/posix/pthread.h>
13 #include <zephyr/sys/bitarray.h>
14
15 struct posix_barrier {
16 struct k_mutex mutex;
17 struct k_condvar cond;
18 uint32_t max;
19 uint32_t count;
20 };
21
22 __pinned_bss
23 static struct posix_barrier posix_barrier_pool[CONFIG_MAX_PTHREAD_BARRIER_COUNT];
24
25 SYS_BITARRAY_DEFINE_STATIC(posix_barrier_bitarray, CONFIG_MAX_PTHREAD_BARRIER_COUNT);
26
27 /*
28 * We reserve the MSB to mark a pthread_barrier_t as initialized (from the
29 * perspective of the application). With a linear space, this means that
30 * the theoretical pthread_barrier_t range is [0,2147483647].
31 */
32 BUILD_ASSERT(CONFIG_MAX_PTHREAD_BARRIER_COUNT < PTHREAD_OBJ_MASK_INIT,
33 "CONFIG_MAX_PTHREAD_BARRIER_COUNT is too high");
34
posix_barrier_to_offset(struct posix_barrier * bar)35 static inline size_t posix_barrier_to_offset(struct posix_barrier *bar)
36 {
37 return bar - posix_barrier_pool;
38 }
39
to_posix_barrier_idx(pthread_barrier_t b)40 static inline size_t to_posix_barrier_idx(pthread_barrier_t b)
41 {
42 return mark_pthread_obj_uninitialized(b);
43 }
44
get_posix_barrier(pthread_barrier_t b)45 struct posix_barrier *get_posix_barrier(pthread_barrier_t b)
46 {
47 int actually_initialized;
48 size_t bit = to_posix_barrier_idx(b);
49
50 /* if the provided barrier does not claim to be initialized, its invalid */
51 if (!is_pthread_obj_initialized(b)) {
52 return NULL;
53 }
54
55 /* Mask off the MSB to get the actual bit index */
56 if (sys_bitarray_test_bit(&posix_barrier_bitarray, bit, &actually_initialized) < 0) {
57 return NULL;
58 }
59
60 if (actually_initialized == 0) {
61 /* The barrier claims to be initialized but is actually not */
62 return NULL;
63 }
64
65 return &posix_barrier_pool[bit];
66 }
67
pthread_barrier_wait(pthread_barrier_t * b)68 int pthread_barrier_wait(pthread_barrier_t *b)
69 {
70 int ret;
71 int err;
72 pthread_barrier_t bb = *b;
73 struct posix_barrier *bar;
74
75 bar = get_posix_barrier(bb);
76 if (bar == NULL) {
77 return EINVAL;
78 }
79
80 err = k_mutex_lock(&bar->mutex, K_FOREVER);
81 __ASSERT_NO_MSG(err == 0);
82
83 ++bar->count;
84
85 if (bar->count == bar->max) {
86 bar->count = 0;
87 ret = PTHREAD_BARRIER_SERIAL_THREAD;
88
89 goto unlock;
90 }
91
92 while (bar->count != 0) {
93 err = k_condvar_wait(&bar->cond, &bar->mutex, K_FOREVER);
94 __ASSERT_NO_MSG(err == 0);
95 /* Note: count is reset to zero by the serialized thread */
96 }
97
98 ret = 0;
99
100 unlock:
101 err = k_condvar_signal(&bar->cond);
102 __ASSERT_NO_MSG(err == 0);
103 err = k_mutex_unlock(&bar->mutex);
104 __ASSERT_NO_MSG(err == 0);
105
106 return ret;
107 }
108
pthread_barrier_init(pthread_barrier_t * b,const pthread_barrierattr_t * attr,unsigned int count)109 int pthread_barrier_init(pthread_barrier_t *b, const pthread_barrierattr_t *attr,
110 unsigned int count)
111 {
112 size_t bit;
113 struct posix_barrier *bar;
114
115 if (count == 0) {
116 return EINVAL;
117 }
118
119 if (sys_bitarray_alloc(&posix_barrier_bitarray, 1, &bit) < 0) {
120 return ENOMEM;
121 }
122
123 bar = &posix_barrier_pool[bit];
124 bar->max = count;
125 bar->count = 0;
126
127 *b = mark_pthread_obj_initialized(bit);
128
129 return 0;
130 }
131
pthread_barrier_destroy(pthread_barrier_t * b)132 int pthread_barrier_destroy(pthread_barrier_t *b)
133 {
134 int err;
135 size_t bit;
136 struct posix_barrier *bar;
137
138 bar = get_posix_barrier(*b);
139 if (bar == NULL) {
140 return EINVAL;
141 }
142
143 err = k_mutex_lock(&bar->mutex, K_FOREVER);
144 if (err < 0) {
145 return -err;
146 }
147 __ASSERT_NO_MSG(err == 0);
148
149 /* An implementation may use this function to set barrier to an invalid value */
150 bar->max = 0;
151 bar->count = 0;
152
153 bit = posix_barrier_to_offset(bar);
154 err = sys_bitarray_free(&posix_barrier_bitarray, 1, bit);
155 __ASSERT_NO_MSG(err == 0);
156
157 err = k_condvar_broadcast(&bar->cond);
158 __ASSERT_NO_MSG(err == 0);
159
160 err = k_mutex_unlock(&bar->mutex);
161 __ASSERT_NO_MSG(err == 0);
162
163 return 0;
164 }
165
pthread_barrierattr_init(pthread_barrierattr_t * attr)166 int pthread_barrierattr_init(pthread_barrierattr_t *attr)
167 {
168 __ASSERT_NO_MSG(attr != NULL);
169
170 attr->pshared = PTHREAD_PROCESS_PRIVATE;
171
172 return 0;
173 }
174
pthread_barrierattr_setpshared(pthread_barrierattr_t * attr,int pshared)175 int pthread_barrierattr_setpshared(pthread_barrierattr_t *attr, int pshared)
176 {
177 if (pshared != PTHREAD_PROCESS_PRIVATE && pshared != PTHREAD_PROCESS_PUBLIC) {
178 return -EINVAL;
179 }
180
181 attr->pshared = pshared;
182 return 0;
183 }
184
pthread_barrierattr_getpshared(const pthread_barrierattr_t * restrict attr,int * restrict pshared)185 int pthread_barrierattr_getpshared(const pthread_barrierattr_t *restrict attr,
186 int *restrict pshared)
187 {
188 *pshared = attr->pshared;
189
190 return 0;
191 }
192
pthread_barrierattr_destroy(pthread_barrierattr_t * attr)193 int pthread_barrierattr_destroy(pthread_barrierattr_t *attr)
194 {
195 ARG_UNUSED(attr);
196
197 return 0;
198 }
199
200 __boot_func
pthread_barrier_pool_init(void)201 static int pthread_barrier_pool_init(void)
202 {
203 int err;
204 size_t i;
205
206 for (i = 0; i < CONFIG_MAX_PTHREAD_BARRIER_COUNT; ++i) {
207 err = k_mutex_init(&posix_barrier_pool[i].mutex);
208 __ASSERT_NO_MSG(err == 0);
209 err = k_condvar_init(&posix_barrier_pool[i].cond);
210 __ASSERT_NO_MSG(err == 0);
211 }
212
213 return 0;
214 }
215 SYS_INIT(pthread_barrier_pool_init, PRE_KERNEL_1, 0);
216