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