1 /*- 2 * Copyright (c) 2004 Michael Telahun Makonnen <mtm@FreeBSD.Org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 * $FreeBSD$ 27 */ 28 29 #include <pthread.h> 30 #include <stdlib.h> 31 #include <string.h> 32 33 #include "thr_private.h" 34 35 __weak_reference(_pthread_barrier_destroy, pthread_barrier_destroy); 36 __weak_reference(_pthread_barrier_init, pthread_barrier_init); 37 __weak_reference(_pthread_barrier_wait, pthread_barrier_wait); 38 39 int 40 _pthread_barrier_destroy(pthread_barrier_t *barrier) 41 { 42 if (*barrier == NULL) 43 return (EINVAL); 44 if ((*barrier)->b_subtotal > 0) 45 return (EBUSY); 46 PTHREAD_ASSERT((*barrier)->b_subtotal == 0, 47 "barrier count must be zero when destroyed"); 48 free(*barrier); 49 *barrier = NULL; 50 return (0); 51 } 52 53 int 54 _pthread_barrier_init(pthread_barrier_t *barrier, 55 const pthread_barrierattr_t attr, unsigned int count) 56 { 57 if (count < 1) 58 return (EINVAL); 59 *barrier = 60 (struct pthread_barrier *)malloc(sizeof(struct pthread_barrier)); 61 if (*barrier == NULL) 62 return (ENOMEM); 63 memset((void *)*barrier, 0, sizeof(struct pthread_barrier)); 64 (*barrier)->b_total = count; 65 TAILQ_INIT(&(*barrier)->b_barrq); 66 return (0); 67 } 68 69 int 70 _pthread_barrier_wait(pthread_barrier_t *barrier) 71 { 72 struct pthread_barrier *b; 73 struct pthread *ptd; 74 int error; 75 76 if (*barrier == NULL) 77 return (EINVAL); 78 79 /* 80 * Check if threads waiting on the barrier can be released. If 81 * so, release them and make this last thread the special thread. 82 */ 83 error = 0; 84 b = *barrier; 85 UMTX_LOCK(&b->b_lock); 86 if (b->b_subtotal == (b->b_total - 1)) { 87 TAILQ_FOREACH(ptd, &b->b_barrq, sqe) { 88 PTHREAD_LOCK(ptd); 89 TAILQ_REMOVE(&b->b_barrq, ptd, sqe); 90 ptd->flags &= ~PTHREAD_FLAGS_IN_BARRQ; 91 ptd->flags |= PTHREAD_FLAGS_BARR_REL; 92 PTHREAD_WAKE(ptd); 93 PTHREAD_UNLOCK(ptd); 94 } 95 b->b_subtotal = 0; 96 UMTX_UNLOCK(&b->b_lock); 97 return (PTHREAD_BARRIER_SERIAL_THREAD); 98 } 99 100 /* 101 * More threads need to reach the barrier. Suspend this thread. 102 */ 103 PTHREAD_LOCK(curthread); 104 TAILQ_INSERT_HEAD(&b->b_barrq, curthread, sqe); 105 curthread->flags |= PTHREAD_FLAGS_IN_BARRQ; 106 PTHREAD_UNLOCK(curthread); 107 b->b_subtotal++; 108 PTHREAD_ASSERT(b->b_subtotal < b->b_total, 109 "the number of threads waiting at a barrier is too large"); 110 UMTX_UNLOCK(&b->b_lock); 111 do { 112 error = _thread_suspend(curthread, NULL); 113 if (error == EINTR) { 114 /* 115 * Make sure this thread wasn't released from 116 * the barrier while it was handling the signal. 117 */ 118 PTHREAD_LOCK(curthread); 119 if ((curthread->flags & PTHREAD_FLAGS_BARR_REL) != 0) { 120 curthread->flags &= ~PTHREAD_FLAGS_BARR_REL; 121 PTHREAD_UNLOCK(curthread); 122 error = 0; 123 break; 124 } 125 PTHREAD_UNLOCK(curthread); 126 } 127 } while (error == EINTR); 128 return (error); 129 } 130