]> CyberLeo.Net >> Repos - FreeBSD/releng/7.2.git/blob - lib/libkse/thread/thr_sem.c
Create releng/7.2 from stable/7 in preparation for 7.2-RELEASE.
[FreeBSD/releng/7.2.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 LT10_COMPAT_PRIVATE(_sem_init);
47 LT10_COMPAT_DEFAULT(sem_init);
48 LT10_COMPAT_PRIVATE(_sem_wait);
49 LT10_COMPAT_DEFAULT(sem_wait);
50 LT10_COMPAT_PRIVATE(_sem_timedwait);
51 LT10_COMPAT_DEFAULT(sem_timedwait);
52 LT10_COMPAT_PRIVATE(_sem_post);
53 LT10_COMPAT_DEFAULT(sem_post);
54
55 __weak_reference(_sem_init, sem_init);
56 __weak_reference(_sem_wait, sem_wait);
57 __weak_reference(_sem_timedwait, sem_timedwait);
58 __weak_reference(_sem_post, sem_post);
59
60
61 static inline int
62 sem_check_validity(sem_t *sem)
63 {
64
65         if ((sem != NULL) && ((*sem)->magic == SEM_MAGIC))
66                 return (0);
67         else {
68                 errno = EINVAL;
69                 return (-1);
70         }
71 }
72
73 static void
74 decrease_nwaiters(void *arg)
75 {
76         sem_t *sem = (sem_t *)arg;
77
78         (*sem)->nwaiters--;
79         /*
80          * this function is called from cancellation point,
81          * the mutex should already be hold.
82          */
83         _pthread_mutex_unlock(&(*sem)->lock);
84 }
85
86 static sem_t
87 sem_alloc(unsigned int value, semid_t semid, int system_sem)
88 {
89         sem_t sem;
90
91         if (value > SEM_VALUE_MAX) {
92                 errno = EINVAL;
93                 return (NULL);
94         }
95
96         sem = (sem_t)malloc(sizeof(struct sem));
97         if (sem == NULL) {
98                 errno = ENOSPC;
99                 return (NULL);
100         }
101
102         /*
103          * Initialize the semaphore.
104          */
105         if (_pthread_mutex_init(&sem->lock, NULL) != 0) {
106                 free(sem);
107                 errno = ENOSPC;
108                 return (NULL);
109         }
110
111         if (_pthread_cond_init(&sem->gtzero, NULL) != 0) {
112                 _pthread_mutex_destroy(&sem->lock);
113                 free(sem);
114                 errno = ENOSPC;
115                 return (NULL);
116         }
117
118         sem->count = (u_int32_t)value;
119         sem->nwaiters = 0;
120         sem->magic = SEM_MAGIC;
121         sem->semid = semid;
122         sem->syssem = system_sem;
123         return (sem);
124 }
125
126 int
127 _sem_init(sem_t *sem, int pshared, unsigned int value)
128 {
129         semid_t semid;
130
131         semid = (semid_t)SEM_USER;
132         if ((pshared != 0) && (ksem_init(&semid, value) != 0))
133                 return (-1);
134
135         (*sem) = sem_alloc(value, semid, pshared);
136         if ((*sem) == NULL) {
137                 if (pshared != 0)
138                         ksem_destroy(semid);
139                 return (-1);
140         }
141         return (0);
142 }
143
144 int
145 _sem_wait(sem_t *sem)
146 {
147         struct pthread *curthread;
148         int retval;
149
150         if (sem_check_validity(sem) != 0)
151                 return (-1);
152
153         curthread = _get_curthread();
154         if ((*sem)->syssem != 0) {
155                 _thr_cancel_enter(curthread);
156                 retval = ksem_wait((*sem)->semid);
157                 _thr_cancel_leave(curthread, retval != 0);
158         }
159         else {
160                 _pthread_testcancel();
161                 _pthread_mutex_lock(&(*sem)->lock);
162
163                 while ((*sem)->count <= 0) {
164                         (*sem)->nwaiters++;
165                         THR_CLEANUP_PUSH(curthread, decrease_nwaiters, sem);
166                         _pthread_cond_wait(&(*sem)->gtzero, &(*sem)->lock);
167                         THR_CLEANUP_POP(curthread, 0);
168                         (*sem)->nwaiters--;
169                 }
170                 (*sem)->count--;
171
172                 _pthread_mutex_unlock(&(*sem)->lock);
173
174                 retval = 0;
175         }
176         return (retval);
177 }
178
179 int
180 _sem_timedwait(sem_t * __restrict sem,
181     const struct timespec * __restrict abs_timeout)
182 {
183         struct pthread *curthread;
184         int retval;
185         int timeout_invalid;
186
187         if (sem_check_validity(sem) != 0)
188                 return (-1);
189
190         if ((*sem)->syssem != 0) {
191                 curthread = _get_curthread();
192                 _thr_cancel_enter(curthread);
193                 retval = ksem_timedwait((*sem)->semid, abs_timeout);
194                 _thr_cancel_leave(curthread, retval != 0);
195         }
196         else {
197                 /*
198                  * The timeout argument is only supposed to
199                  * be checked if the thread would have blocked.
200                  * This is checked outside of the lock so a
201                  * segfault on an invalid address doesn't end
202                  * up leaving the mutex locked.
203                  */
204                 _pthread_testcancel();
205                 timeout_invalid = (abs_timeout->tv_nsec >= 1000000000) ||
206                     (abs_timeout->tv_nsec < 0);
207                 _pthread_mutex_lock(&(*sem)->lock);
208
209                 if ((*sem)->count <= 0) {
210                         if (timeout_invalid) {
211                                 _pthread_mutex_unlock(&(*sem)->lock);
212                                 errno = EINVAL;
213                                 return (-1);
214                         }
215                         (*sem)->nwaiters++;
216                         _pthread_cleanup_push(decrease_nwaiters, sem);
217                         _pthread_cond_timedwait(&(*sem)->gtzero,
218                             &(*sem)->lock, abs_timeout);
219                         _pthread_cleanup_pop(0);
220                         (*sem)->nwaiters--;
221                 }
222                 if ((*sem)->count == 0) {
223                         errno = ETIMEDOUT;
224                         retval = -1;
225                 }
226                 else {
227                         (*sem)->count--;
228                         retval = 0;
229                 }       
230
231                 _pthread_mutex_unlock(&(*sem)->lock);
232         }
233
234         return (retval);
235 }
236
237 int
238 _sem_post(sem_t *sem)
239 {
240         struct pthread *curthread;
241         int retval;
242         
243         if (sem_check_validity(sem) != 0)
244                 return (-1);
245
246         if ((*sem)->syssem != 0)
247                 retval = ksem_post((*sem)->semid);
248         else {
249                 /*
250                  * sem_post() is required to be safe to call from within
251                  * signal handlers.  Thus, we must enter a critical region.
252                  */
253                 curthread = _get_curthread();
254                 _thr_critical_enter(curthread);
255                 _pthread_mutex_lock(&(*sem)->lock);
256
257                 (*sem)->count++;
258                 if ((*sem)->nwaiters > 0)
259                         _pthread_cond_signal(&(*sem)->gtzero);
260
261                 _pthread_mutex_unlock(&(*sem)->lock);
262                 _thr_critical_leave(curthread);
263                 retval = 0;
264         }
265
266         return (retval);
267 }