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