1*7adb4107Sriastradh /* $NetBSD: pthread_barrier.c,v 1.23 2022/02/12 14:59:32 riastradh Exp $ */
2c62a74e6Sthorpej
3c62a74e6Sthorpej /*-
4bc77394cSad * Copyright (c) 2001, 2003, 2006, 2007, 2009, 2020 The NetBSD Foundation, Inc.
5c62a74e6Sthorpej * All rights reserved.
6c62a74e6Sthorpej *
7c62a74e6Sthorpej * This code is derived from software contributed to The NetBSD Foundation
8c79299e2Sad * by Nathan J. Williams, by Jason R. Thorpe, and by Andrew Doran.
9c62a74e6Sthorpej *
10c62a74e6Sthorpej * Redistribution and use in source and binary forms, with or without
11c62a74e6Sthorpej * modification, are permitted provided that the following conditions
12c62a74e6Sthorpej * are met:
13c62a74e6Sthorpej * 1. Redistributions of source code must retain the above copyright
14c62a74e6Sthorpej * notice, this list of conditions and the following disclaimer.
15c62a74e6Sthorpej * 2. Redistributions in binary form must reproduce the above copyright
16c62a74e6Sthorpej * notice, this list of conditions and the following disclaimer in the
17c62a74e6Sthorpej * documentation and/or other materials provided with the distribution.
18c62a74e6Sthorpej *
19c62a74e6Sthorpej * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20c62a74e6Sthorpej * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21c62a74e6Sthorpej * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22c62a74e6Sthorpej * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23c62a74e6Sthorpej * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24c62a74e6Sthorpej * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25c62a74e6Sthorpej * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26c62a74e6Sthorpej * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27c62a74e6Sthorpej * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28c62a74e6Sthorpej * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29c62a74e6Sthorpej * POSSIBILITY OF SUCH DAMAGE.
30c62a74e6Sthorpej */
31c62a74e6Sthorpej
32f043c0fbSlukem #include <sys/cdefs.h>
33*7adb4107Sriastradh __RCSID("$NetBSD: pthread_barrier.c,v 1.23 2022/02/12 14:59:32 riastradh Exp $");
34*7adb4107Sriastradh
35*7adb4107Sriastradh /* Need to use libc-private names for atomic operations. */
36*7adb4107Sriastradh #include "../../common/lib/libc/atomic/atomic_op_namespace.h"
37f043c0fbSlukem
38c62a74e6Sthorpej #include <errno.h>
39c62a74e6Sthorpej
40c62a74e6Sthorpej #include "pthread.h"
41c62a74e6Sthorpej #include "pthread_int.h"
42c62a74e6Sthorpej
43c62a74e6Sthorpej int
pthread_barrier_init(pthread_barrier_t * barrier,const pthread_barrierattr_t * attr,unsigned int count)44c62a74e6Sthorpej pthread_barrier_init(pthread_barrier_t *barrier,
45c62a74e6Sthorpej const pthread_barrierattr_t *attr, unsigned int count)
46c62a74e6Sthorpej {
47c62a74e6Sthorpej
487f6f4173Skamil pthread__error(EINVAL, "Invalid barrier attribute",
497f6f4173Skamil attr == NULL || attr->ptba_magic == _PT_BARRIERATTR_MAGIC);
50c62a74e6Sthorpej if (count == 0)
51c62a74e6Sthorpej return EINVAL;
52c62a74e6Sthorpej
53c62a74e6Sthorpej barrier->ptb_magic = _PT_BARRIER_MAGIC;
54c62a74e6Sthorpej PTQ_INIT(&barrier->ptb_waiters);
55c62a74e6Sthorpej barrier->ptb_initcount = count;
56c62a74e6Sthorpej barrier->ptb_curcount = 0;
579ff1e2d3Snathanw barrier->ptb_generation = 0;
58c62a74e6Sthorpej return 0;
59c62a74e6Sthorpej }
60c62a74e6Sthorpej
61c62a74e6Sthorpej int
pthread_barrier_destroy(pthread_barrier_t * barrier)62c62a74e6Sthorpej pthread_barrier_destroy(pthread_barrier_t *barrier)
63c62a74e6Sthorpej {
64c62a74e6Sthorpej
657f6f4173Skamil pthread__error(EINVAL, "Invalid barrier",
667f6f4173Skamil barrier->ptb_magic == _PT_BARRIER_MAGIC);
67a46df1f1Sad if (barrier->ptb_curcount != 0)
68c62a74e6Sthorpej return EBUSY;
697f6f4173Skamil
707f6f4173Skamil barrier->ptb_magic = _PT_BARRIER_DEAD;
717f6f4173Skamil
72c62a74e6Sthorpej return 0;
73c62a74e6Sthorpej }
74c62a74e6Sthorpej
75c62a74e6Sthorpej int
pthread_barrier_wait(pthread_barrier_t * barrier)76c62a74e6Sthorpej pthread_barrier_wait(pthread_barrier_t *barrier)
77c62a74e6Sthorpej {
782bcb8bf1Sad pthread_mutex_t *interlock;
79c62a74e6Sthorpej pthread_t self;
809ff1e2d3Snathanw unsigned int gen;
81c62a74e6Sthorpej
827f6f4173Skamil pthread__error(EINVAL, "Invalid barrier",
837f6f4173Skamil barrier->ptb_magic == _PT_BARRIER_MAGIC);
84c62a74e6Sthorpej
85c62a74e6Sthorpej /*
86c62a74e6Sthorpej * A single arbitrary thread is supposed to return
87c62a74e6Sthorpej * PTHREAD_BARRIER_SERIAL_THREAD, and everone else
88c62a74e6Sthorpej * is supposed to return 0. Since pthread_barrier_wait()
89c62a74e6Sthorpej * is not a cancellation point, this is trivial; we
90c62a74e6Sthorpej * simply elect that the thread that causes the barrier
91c62a74e6Sthorpej * to be satisfied gets the special return value. Note
92c62a74e6Sthorpej * that this final thread does not actually need to block,
93c62a74e6Sthorpej * but instead is responsible for waking everyone else up.
94c62a74e6Sthorpej */
95a46df1f1Sad self = pthread__self();
96a46df1f1Sad interlock = pthread__hashlock(barrier);
97a46df1f1Sad pthread_mutex_lock(interlock);
98c62a74e6Sthorpej if (barrier->ptb_curcount + 1 == barrier->ptb_initcount) {
99ded26025Sad barrier->ptb_generation++;
100a46df1f1Sad barrier->ptb_curcount = 0;
1012bcb8bf1Sad pthread__unpark_all(&barrier->ptb_waiters, self,
1022bcb8bf1Sad interlock);
1032bcb8bf1Sad pthread_mutex_unlock(interlock);
104c62a74e6Sthorpej return PTHREAD_BARRIER_SERIAL_THREAD;
105c62a74e6Sthorpej }
1069ff1e2d3Snathanw barrier->ptb_curcount++;
1079ff1e2d3Snathanw gen = barrier->ptb_generation;
108a46df1f1Sad for (;;) {
109c79299e2Sad PTQ_INSERT_TAIL(&barrier->ptb_waiters, self, pt_sleep);
110c79299e2Sad self->pt_sleepobj = &barrier->ptb_waiters;
1112bcb8bf1Sad (void)pthread__park(self, interlock, &barrier->ptb_waiters,
112bc77394cSad NULL, 0);
113a46df1f1Sad if (__predict_true(gen != barrier->ptb_generation)) {
114a46df1f1Sad break;
1159ff1e2d3Snathanw }
116a46df1f1Sad pthread_mutex_lock(interlock);
117a46df1f1Sad if (gen != barrier->ptb_generation) {
1182bcb8bf1Sad pthread_mutex_unlock(interlock);
119a46df1f1Sad break;
120a46df1f1Sad }
121a46df1f1Sad }
122c62a74e6Sthorpej
123c62a74e6Sthorpej return 0;
124c62a74e6Sthorpej }
125c62a74e6Sthorpej
1267cf7644fSchristos #ifdef _PTHREAD_PSHARED
1277cf7644fSchristos int
pthread_barrierattr_getpshared(const pthread_barrierattr_t * __restrict attr,int * __restrict pshared)1287cf7644fSchristos pthread_barrierattr_getpshared(const pthread_barrierattr_t * __restrict attr,
1297cf7644fSchristos int * __restrict pshared)
1307cf7644fSchristos {
1317cf7644fSchristos
1327f6f4173Skamil pthread__error(EINVAL, "Invalid barrier attribute",
1337f6f4173Skamil attr->ptba_magic == _PT_BARRIERATTR_MAGIC);
1347f6f4173Skamil
1357cf7644fSchristos *pshared = PTHREAD_PROCESS_PRIVATE;
1367cf7644fSchristos return 0;
1377cf7644fSchristos }
1387cf7644fSchristos
1397cf7644fSchristos int
pthread_barrierattr_setpshared(pthread_barrierattr_t * attr,int pshared)1407cf7644fSchristos pthread_barrierattr_setpshared(pthread_barrierattr_t *attr, int pshared)
1417cf7644fSchristos {
1427cf7644fSchristos
1437f6f4173Skamil pthread__error(EINVAL, "Invalid barrier attribute",
1447f6f4173Skamil attr->ptba_magic == _PT_BARRIERATTR_MAGIC);
1457f6f4173Skamil
1467cf7644fSchristos switch(pshared) {
1477cf7644fSchristos case PTHREAD_PROCESS_PRIVATE:
1487cf7644fSchristos return 0;
1497cf7644fSchristos case PTHREAD_PROCESS_SHARED:
1507cf7644fSchristos return ENOSYS;
1517cf7644fSchristos }
1527cf7644fSchristos return EINVAL;
1537cf7644fSchristos }
1547cf7644fSchristos #endif
155c62a74e6Sthorpej
156c62a74e6Sthorpej int
pthread_barrierattr_init(pthread_barrierattr_t * attr)157c62a74e6Sthorpej pthread_barrierattr_init(pthread_barrierattr_t *attr)
158c62a74e6Sthorpej {
159c62a74e6Sthorpej
160c62a74e6Sthorpej attr->ptba_magic = _PT_BARRIERATTR_MAGIC;
161c62a74e6Sthorpej return 0;
162c62a74e6Sthorpej }
163c62a74e6Sthorpej
164c62a74e6Sthorpej int
pthread_barrierattr_destroy(pthread_barrierattr_t * attr)165c62a74e6Sthorpej pthread_barrierattr_destroy(pthread_barrierattr_t *attr)
166c62a74e6Sthorpej {
167c62a74e6Sthorpej
1687f6f4173Skamil pthread__error(EINVAL, "Invalid barrier attribute",
1697f6f4173Skamil attr->ptba_magic == _PT_BARRIERATTR_MAGIC);
170c62a74e6Sthorpej attr->ptba_magic = _PT_BARRIERATTR_DEAD;
171c62a74e6Sthorpej return 0;
172c62a74e6Sthorpej }
173