1 /* $NetBSD: pthread_barrier.c,v 1.23 2022/02/12 14:59:32 riastradh Exp $ */ 2 3 /*- 4 * Copyright (c) 2001, 2003, 2006, 2007, 2009, 2020 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Nathan J. Williams, by Jason R. Thorpe, and by Andrew Doran. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 #include <sys/cdefs.h> 33 __RCSID("$NetBSD: pthread_barrier.c,v 1.23 2022/02/12 14:59:32 riastradh Exp $"); 34 35 /* Need to use libc-private names for atomic operations. */ 36 #include "../../common/lib/libc/atomic/atomic_op_namespace.h" 37 38 #include <errno.h> 39 40 #include "pthread.h" 41 #include "pthread_int.h" 42 43 int 44 pthread_barrier_init(pthread_barrier_t *barrier, 45 const pthread_barrierattr_t *attr, unsigned int count) 46 { 47 48 pthread__error(EINVAL, "Invalid barrier attribute", 49 attr == NULL || attr->ptba_magic == _PT_BARRIERATTR_MAGIC); 50 if (count == 0) 51 return EINVAL; 52 53 barrier->ptb_magic = _PT_BARRIER_MAGIC; 54 PTQ_INIT(&barrier->ptb_waiters); 55 barrier->ptb_initcount = count; 56 barrier->ptb_curcount = 0; 57 barrier->ptb_generation = 0; 58 return 0; 59 } 60 61 int 62 pthread_barrier_destroy(pthread_barrier_t *barrier) 63 { 64 65 pthread__error(EINVAL, "Invalid barrier", 66 barrier->ptb_magic == _PT_BARRIER_MAGIC); 67 if (barrier->ptb_curcount != 0) 68 return EBUSY; 69 70 barrier->ptb_magic = _PT_BARRIER_DEAD; 71 72 return 0; 73 } 74 75 int 76 pthread_barrier_wait(pthread_barrier_t *barrier) 77 { 78 pthread_mutex_t *interlock; 79 pthread_t self; 80 unsigned int gen; 81 82 pthread__error(EINVAL, "Invalid barrier", 83 barrier->ptb_magic == _PT_BARRIER_MAGIC); 84 85 /* 86 * A single arbitrary thread is supposed to return 87 * PTHREAD_BARRIER_SERIAL_THREAD, and everone else 88 * is supposed to return 0. Since pthread_barrier_wait() 89 * is not a cancellation point, this is trivial; we 90 * simply elect that the thread that causes the barrier 91 * to be satisfied gets the special return value. Note 92 * that this final thread does not actually need to block, 93 * but instead is responsible for waking everyone else up. 94 */ 95 self = pthread__self(); 96 interlock = pthread__hashlock(barrier); 97 pthread_mutex_lock(interlock); 98 if (barrier->ptb_curcount + 1 == barrier->ptb_initcount) { 99 barrier->ptb_generation++; 100 barrier->ptb_curcount = 0; 101 pthread__unpark_all(&barrier->ptb_waiters, self, 102 interlock); 103 pthread_mutex_unlock(interlock); 104 return PTHREAD_BARRIER_SERIAL_THREAD; 105 } 106 barrier->ptb_curcount++; 107 gen = barrier->ptb_generation; 108 for (;;) { 109 PTQ_INSERT_TAIL(&barrier->ptb_waiters, self, pt_sleep); 110 self->pt_sleepobj = &barrier->ptb_waiters; 111 (void)pthread__park(self, interlock, &barrier->ptb_waiters, 112 NULL, 0); 113 if (__predict_true(gen != barrier->ptb_generation)) { 114 break; 115 } 116 pthread_mutex_lock(interlock); 117 if (gen != barrier->ptb_generation) { 118 pthread_mutex_unlock(interlock); 119 break; 120 } 121 } 122 123 return 0; 124 } 125 126 #ifdef _PTHREAD_PSHARED 127 int 128 pthread_barrierattr_getpshared(const pthread_barrierattr_t * __restrict attr, 129 int * __restrict pshared) 130 { 131 132 pthread__error(EINVAL, "Invalid barrier attribute", 133 attr->ptba_magic == _PT_BARRIERATTR_MAGIC); 134 135 *pshared = PTHREAD_PROCESS_PRIVATE; 136 return 0; 137 } 138 139 int 140 pthread_barrierattr_setpshared(pthread_barrierattr_t *attr, int pshared) 141 { 142 143 pthread__error(EINVAL, "Invalid barrier attribute", 144 attr->ptba_magic == _PT_BARRIERATTR_MAGIC); 145 146 switch(pshared) { 147 case PTHREAD_PROCESS_PRIVATE: 148 return 0; 149 case PTHREAD_PROCESS_SHARED: 150 return ENOSYS; 151 } 152 return EINVAL; 153 } 154 #endif 155 156 int 157 pthread_barrierattr_init(pthread_barrierattr_t *attr) 158 { 159 160 attr->ptba_magic = _PT_BARRIERATTR_MAGIC; 161 return 0; 162 } 163 164 int 165 pthread_barrierattr_destroy(pthread_barrierattr_t *attr) 166 { 167 168 pthread__error(EINVAL, "Invalid barrier attribute", 169 attr->ptba_magic == _PT_BARRIERATTR_MAGIC); 170 attr->ptba_magic = _PT_BARRIERATTR_DEAD; 171 return 0; 172 } 173