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