1 /* $NetBSD: pthread_barrier.c,v 1.18 2008/05/25 17:05:28 ad Exp $ */ 2 3 /*- 4 * Copyright (c) 2001, 2003, 2006, 2007 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.18 2008/05/25 17:05:28 ad Exp $"); 34 35 #include <errno.h> 36 37 #include "pthread.h" 38 #include "pthread_int.h" 39 40 int 41 pthread_barrier_init(pthread_barrier_t *barrier, 42 const pthread_barrierattr_t *attr, unsigned int count) 43 { 44 pthread_mutex_t *interlock; 45 46 #ifdef ERRORCHECK 47 if ((barrier == NULL) || 48 (attr && (attr->ptba_magic != _PT_BARRIERATTR_MAGIC))) 49 return EINVAL; 50 #endif 51 52 if (count == 0) 53 return EINVAL; 54 55 if (barrier->ptb_magic == _PT_BARRIER_MAGIC) { 56 interlock = pthread__hashlock(barrier); 57 58 /* 59 * We're simply reinitializing the barrier to a 60 * new count. 61 */ 62 pthread_mutex_lock(interlock); 63 64 if (barrier->ptb_magic != _PT_BARRIER_MAGIC) { 65 pthread_mutex_unlock(interlock); 66 return EINVAL; 67 } 68 69 if (!PTQ_EMPTY(&barrier->ptb_waiters)) { 70 pthread_mutex_unlock(interlock); 71 return EBUSY; 72 } 73 74 barrier->ptb_initcount = count; 75 barrier->ptb_curcount = 0; 76 barrier->ptb_generation = 0; 77 78 pthread_mutex_unlock(interlock); 79 80 return 0; 81 } 82 83 barrier->ptb_magic = _PT_BARRIER_MAGIC; 84 PTQ_INIT(&barrier->ptb_waiters); 85 barrier->ptb_initcount = count; 86 barrier->ptb_curcount = 0; 87 barrier->ptb_generation = 0; 88 89 return 0; 90 } 91 92 93 int 94 pthread_barrier_destroy(pthread_barrier_t *barrier) 95 { 96 pthread_mutex_t *interlock; 97 98 #ifdef ERRORCHECK 99 if ((barrier == NULL) || (barrier->ptb_magic != _PT_BARRIER_MAGIC)) 100 return EINVAL; 101 #endif 102 103 interlock = pthread__hashlock(barrier); 104 pthread_mutex_lock(interlock); 105 106 if (barrier->ptb_magic != _PT_BARRIER_MAGIC) { 107 pthread_mutex_unlock(interlock); 108 return EINVAL; 109 } 110 111 if (!PTQ_EMPTY(&barrier->ptb_waiters)) { 112 pthread_mutex_unlock(interlock); 113 return EBUSY; 114 } 115 116 barrier->ptb_magic = _PT_BARRIER_DEAD; 117 118 pthread_mutex_unlock(interlock); 119 120 return 0; 121 } 122 123 124 int 125 pthread_barrier_wait(pthread_barrier_t *barrier) 126 { 127 pthread_mutex_t *interlock; 128 pthread_t self; 129 unsigned int gen; 130 131 #ifdef ERRORCHECK 132 if ((barrier == NULL) || (barrier->ptb_magic != _PT_BARRIER_MAGIC)) 133 return EINVAL; 134 #endif 135 self = pthread__self(); 136 interlock = pthread__hashlock(barrier); 137 138 pthread_mutex_lock(interlock); 139 140 /* 141 * A single arbitrary thread is supposed to return 142 * PTHREAD_BARRIER_SERIAL_THREAD, and everone else 143 * is supposed to return 0. Since pthread_barrier_wait() 144 * is not a cancellation point, this is trivial; we 145 * simply elect that the thread that causes the barrier 146 * to be satisfied gets the special return value. Note 147 * that this final thread does not actually need to block, 148 * but instead is responsible for waking everyone else up. 149 */ 150 if (barrier->ptb_curcount + 1 == barrier->ptb_initcount) { 151 barrier->ptb_generation++; 152 pthread__unpark_all(&barrier->ptb_waiters, self, 153 interlock); 154 pthread_mutex_unlock(interlock); 155 return PTHREAD_BARRIER_SERIAL_THREAD; 156 } 157 158 barrier->ptb_curcount++; 159 gen = barrier->ptb_generation; 160 while (gen == barrier->ptb_generation) { 161 PTQ_INSERT_TAIL(&barrier->ptb_waiters, self, pt_sleep); 162 self->pt_sleepobj = &barrier->ptb_waiters; 163 (void)pthread__park(self, interlock, &barrier->ptb_waiters, 164 NULL, 0, __UNVOLATILE(&interlock->ptm_waiters)); 165 pthread_mutex_lock(interlock); 166 } 167 pthread_mutex_unlock(interlock); 168 169 return 0; 170 } 171 172 173 int 174 pthread_barrierattr_init(pthread_barrierattr_t *attr) 175 { 176 177 #ifdef ERRORCHECK 178 if (attr == NULL) 179 return EINVAL; 180 #endif 181 182 attr->ptba_magic = _PT_BARRIERATTR_MAGIC; 183 184 return 0; 185 } 186 187 188 int 189 pthread_barrierattr_destroy(pthread_barrierattr_t *attr) 190 { 191 192 #ifdef ERRORCHECK 193 if ((attr == NULL) || 194 (attr->ptba_magic != _PT_BARRIERATTR_MAGIC)) 195 return EINVAL; 196 #endif 197 198 attr->ptba_magic = _PT_BARRIERATTR_DEAD; 199 200 return 0; 201 } 202