1/* 2 * Copyright (C) 2000 Jason Evans <jasone@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(s), this list of conditions and the following disclaimer as 10 * the first lines of this file unmodified other than the possible 11 * addition of one or more copyright notices. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice(s), this list of conditions and the following disclaimer in 14 * the documentation and/or other materials provided with the 15 * distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``AS IS'' AND ANY 18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 20 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) BE 21 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 24 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 25 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 26 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 27 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 * 29 * $FreeBSD: releng/10.3/lib/libkse/thread/thr_sem.c 174689 2007-12-16 23:29:57Z deischen $ 30 */ 31 32#include "namespace.h" 33#include <sys/types.h> 34#include <sys/queue.h> 35#include <errno.h> 36#include <fcntl.h> 37#include <pthread.h> 38#include <semaphore.h> 39#include <stdlib.h> 40#include <time.h> 41#include <_semaphore.h> 42#include "un-namespace.h" 43#include "libc_private.h" 44#include "thr_private.h" 45 46__weak_reference(_sem_init, sem_init); 47__weak_reference(_sem_wait, sem_wait); 48__weak_reference(_sem_timedwait, sem_timedwait); 49__weak_reference(_sem_post, sem_post); 50 51 52static inline int 53sem_check_validity(sem_t *sem) 54{ 55 56 if ((sem != NULL) && ((*sem)->magic == SEM_MAGIC)) 57 return (0); 58 else { 59 errno = EINVAL; 60 return (-1); 61 } 62} 63 64static void 65decrease_nwaiters(void *arg) 66{ 67 sem_t *sem = (sem_t *)arg; 68 69 (*sem)->nwaiters--; 70 /* 71 * this function is called from cancellation point, 72 * the mutex should already be hold. 73 */ 74 _pthread_mutex_unlock(&(*sem)->lock); 75} 76 77static sem_t 78sem_alloc(unsigned int value, semid_t semid, int system_sem) 79{ 80 sem_t sem; 81 82 if (value > SEM_VALUE_MAX) { 83 errno = EINVAL; 84 return (NULL); 85 } 86 87 sem = (sem_t)malloc(sizeof(struct sem)); 88 if (sem == NULL) { 89 errno = ENOSPC; 90 return (NULL); 91 } 92 93 /* 94 * Initialize the semaphore. 95 */ 96 if (_pthread_mutex_init(&sem->lock, NULL) != 0) { 97 free(sem); 98 errno = ENOSPC; 99 return (NULL); 100 } 101 102 if (_pthread_cond_init(&sem->gtzero, NULL) != 0) { 103 _pthread_mutex_destroy(&sem->lock); 104 free(sem); 105 errno = ENOSPC; 106 return (NULL); 107 } 108 109 sem->count = (u_int32_t)value; 110 sem->nwaiters = 0; 111 sem->magic = SEM_MAGIC; 112 sem->semid = semid; 113 sem->syssem = system_sem; 114 return (sem); 115} 116 117int 118_sem_init(sem_t *sem, int pshared, unsigned int value) 119{ 120 semid_t semid; 121 122 semid = (semid_t)SEM_USER; 123 if ((pshared != 0) && (ksem_init(&semid, value) != 0)) 124 return (-1); 125 126 (*sem) = sem_alloc(value, semid, pshared); 127 if ((*sem) == NULL) { 128 if (pshared != 0) 129 ksem_destroy(semid); 130 return (-1); 131 } 132 return (0); 133} 134 135int 136_sem_wait(sem_t *sem) 137{ 138 struct pthread *curthread; 139 int retval; 140 141 if (sem_check_validity(sem) != 0) 142 return (-1); 143 144 curthread = _get_curthread(); 145 if ((*sem)->syssem != 0) { 146 _thr_cancel_enter(curthread); 147 retval = ksem_wait((*sem)->semid); 148 _thr_cancel_leave(curthread, retval != 0); 149 } 150 else { 151 _pthread_testcancel(); 152 _pthread_mutex_lock(&(*sem)->lock); 153 154 while ((*sem)->count <= 0) { 155 (*sem)->nwaiters++; 156 THR_CLEANUP_PUSH(curthread, decrease_nwaiters, sem); 157 _pthread_cond_wait(&(*sem)->gtzero, &(*sem)->lock); 158 THR_CLEANUP_POP(curthread, 0); 159 (*sem)->nwaiters--; 160 } 161 (*sem)->count--; 162 163 _pthread_mutex_unlock(&(*sem)->lock); 164 165 retval = 0; 166 } 167 return (retval); 168} 169 170int 171_sem_timedwait(sem_t * __restrict sem, 172 const struct timespec * __restrict abs_timeout) 173{ 174 struct pthread *curthread; 175 int retval; 176 int timeout_invalid; 177 178 if (sem_check_validity(sem) != 0) 179 return (-1); 180 181 if ((*sem)->syssem != 0) { 182 curthread = _get_curthread(); 183 _thr_cancel_enter(curthread); 184 retval = ksem_timedwait((*sem)->semid, abs_timeout); 185 _thr_cancel_leave(curthread, retval != 0); 186 } 187 else { 188 /* 189 * The timeout argument is only supposed to 190 * be checked if the thread would have blocked. 191 * This is checked outside of the lock so a 192 * segfault on an invalid address doesn't end 193 * up leaving the mutex locked. 194 */ 195 _pthread_testcancel(); 196 timeout_invalid = (abs_timeout->tv_nsec >= 1000000000) || 197 (abs_timeout->tv_nsec < 0); 198 _pthread_mutex_lock(&(*sem)->lock); 199 200 if ((*sem)->count <= 0) { 201 if (timeout_invalid) { 202 _pthread_mutex_unlock(&(*sem)->lock); 203 errno = EINVAL; 204 return (-1); 205 } 206 (*sem)->nwaiters++; 207 _pthread_cleanup_push(decrease_nwaiters, sem); 208 _pthread_cond_timedwait(&(*sem)->gtzero, 209 &(*sem)->lock, abs_timeout); 210 _pthread_cleanup_pop(0); 211 (*sem)->nwaiters--; 212 } 213 if ((*sem)->count == 0) { 214 errno = ETIMEDOUT; 215 retval = -1; 216 } 217 else { 218 (*sem)->count--; 219 retval = 0; 220 } 221 222 _pthread_mutex_unlock(&(*sem)->lock); 223 } 224 225 return (retval); 226} 227 228int 229_sem_post(sem_t *sem) 230{ 231 struct pthread *curthread; 232 int retval; 233 234 if (sem_check_validity(sem) != 0) 235 return (-1); 236 237 if ((*sem)->syssem != 0) 238 retval = ksem_post((*sem)->semid); 239 else { 240 /* 241 * sem_post() is required to be safe to call from within 242 * signal handlers. Thus, we must enter a critical region. 243 */ 244 curthread = _get_curthread(); 245 _thr_critical_enter(curthread); 246 _pthread_mutex_lock(&(*sem)->lock); 247 248 (*sem)->count++; 249 if ((*sem)->nwaiters > 0) 250 _pthread_cond_signal(&(*sem)->gtzero); 251 252 _pthread_mutex_unlock(&(*sem)->lock); 253 _thr_critical_leave(curthread); 254 retval = 0; 255 } 256 257 return (retval); 258} 259