]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - lib/libkse/thread/thr_sem.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / lib / libkse / thread / thr_sem.c
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$
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
52 static inline int
53 sem_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
64 static void
65 decrease_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
77 static sem_t
78 sem_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
117 int
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
135 int
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
170 int
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
228 int
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 }