Add clockid to _thr_umtx_wait, so that clockid attribute of condition
[dragonfly.git] / lib / libthread_xu / thread / thr_sem.c
1 /*
2  * Copyright (C) 2005 David Xu <davidxu@freebsd.org>.
3  * Copyright (C) 2000 Jason Evans <jasone@freebsd.org>.
4  * All rights reserved.
5  * 
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice(s), this list of conditions and the following disclaimer as
11  *    the first lines of this file unmodified other than the possible
12  *    addition of one or more copyright notices.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice(s), this list of conditions and the following disclaimer in
15  *    the documentation and/or other materials provided with the
16  *    distribution.
17  * 
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``AS IS'' AND ANY
19  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) BE
22  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
25  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
26  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
27  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
28  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  *
30  * $FreeBSD: src/lib/libpthread/thread/thr_sem.c,v 1.16 2004/12/18 18:07:37 deischen Exp $
31  * $DragonFly: src/lib/libthread_xu/thread/thr_sem.c,v 1.2 2005/03/15 11:24:23 davidxu Exp $
32  */
33
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
42 #include "thr_private.h"
43
44 __weak_reference(_sem_close, sem_close);
45 __weak_reference(_sem_destroy, sem_destroy);
46 __weak_reference(_sem_getvalue, sem_getvalue);
47 __weak_reference(_sem_init, sem_init);
48 __weak_reference(_sem_open, sem_open);
49 __weak_reference(_sem_trywait, sem_trywait);
50 __weak_reference(_sem_wait, sem_wait);
51 __weak_reference(_sem_timedwait, sem_timedwait);
52 __weak_reference(_sem_post, sem_post);
53 __weak_reference(_sem_unlink, sem_unlink);
54
55 /*
56  * Semaphore definitions.
57  */
58 struct sem {
59 #define SEM_MAGIC       ((u_int32_t) 0x09fa4012)
60         u_int32_t               magic;
61         volatile umtx_t         count;
62         int                     semid;  /* kernel based semaphore id. */
63 };
64
65 static inline int
66 sem_check_validity(sem_t *sem)
67 {
68
69         if ((sem != NULL) && ((*sem)->magic == SEM_MAGIC))
70                 return (0);
71         else {
72                 errno = EINVAL;
73                 return (-1);
74         }
75 }
76
77 static sem_t
78 sem_alloc(unsigned int value, int semid)
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         sem->magic = SEM_MAGIC;
93         sem->count = (u_int32_t)value;
94         sem->semid = semid;
95         return (sem);
96 }
97
98 int
99 _sem_init(sem_t *sem, int pshared, unsigned int value)
100 {
101         if (pshared != 0) {
102                 /*
103                  * We really can support pshared, but sem_t was
104                  * defined as a pointer, if it is a structure,
105                  * it will work between processes.
106                  */
107                 errno = EPERM;
108                 return (-1);
109         }
110
111         (*sem) = sem_alloc(value, -1);
112         if ((*sem) == NULL)
113                 return (-1);
114         return (0);
115 }
116
117 int
118 _sem_destroy(sem_t *sem)
119 {
120         if (sem_check_validity(sem) != 0)
121                 return (-1);
122
123         free(*sem);
124         return (0);
125 }
126
127 sem_t *
128 _sem_open(const char *name, int oflag, ...)
129 {
130         errno = ENOSYS;
131         return SEM_FAILED;
132 }
133
134 int
135 _sem_close(sem_t *sem)
136 {
137         errno = ENOSYS;
138         return -1;
139 }
140
141 int
142 _sem_unlink(const char *name)
143 {
144         errno = ENOSYS;
145         return -1;
146 }
147
148 int
149 _sem_getvalue(sem_t * __restrict sem, int * __restrict sval)
150 {
151         if (sem_check_validity(sem) != 0)
152                 return (-1);
153
154         *sval = (*sem)->count;
155         return (0);
156 }
157
158 int
159 _sem_trywait(sem_t *sem)
160 {
161         int val;
162
163         if (sem_check_validity(sem) != 0)
164                 return (-1);
165
166         while ((val = (*sem)->count) > 0) {
167                 if (atomic_cmpset_int(&(*sem)->count, val, val - 1))
168                         return (0);
169         }
170         errno = EAGAIN;
171         return (-1);
172 }
173
174 int
175 _sem_wait(sem_t *sem)
176 {
177         struct pthread *curthread;
178         int val, oldcancel, retval;
179
180         if (sem_check_validity(sem) != 0)
181                 return (-1);
182
183         curthread = _get_curthread();
184         _pthread_testcancel();
185         do {
186                 while ((val = (*sem)->count) > 0) {
187                         if (atomic_cmpset_acq_int(&(*sem)->count, val, val - 1))
188                                 return (0);
189                 }
190                 oldcancel = _thr_cancel_enter(curthread);
191                 retval = _thr_umtx_wait(&(*sem)->count, 0, NULL, 0);
192                 _thr_cancel_leave(curthread, oldcancel);
193         } while (retval == 0);
194         errno = retval;
195         return (-1);
196 }
197
198 int
199 _sem_timedwait(sem_t * __restrict sem, struct timespec * __restrict abstime)
200 {
201         struct timespec ts, ts2;
202         struct pthread *curthread;
203         int val, oldcancel, retval;
204
205         if (sem_check_validity(sem) != 0)
206                 return (-1);
207
208         curthread = _get_curthread();
209
210         /*
211          * The timeout argument is only supposed to
212          * be checked if the thread would have blocked.
213          */
214         _pthread_testcancel();
215         do {
216                 while ((val = (*sem)->count) > 0) {
217                         if (atomic_cmpset_acq_int(&(*sem)->count, val, val - 1))
218                                 return (0);
219                 }
220                 if (abstime == NULL) {
221                         errno = EINVAL;
222                         return (-1);
223                 }
224                 clock_gettime(CLOCK_REALTIME, &ts);
225                 TIMESPEC_SUB(&ts2, abstime, &ts);
226                 oldcancel = _thr_cancel_enter(curthread);
227                 retval = _thr_umtx_wait(&(*sem)->count, 0, &ts2,
228                                         CLOCK_REALTIME);
229                 _thr_cancel_leave(curthread, oldcancel);
230         } while (retval == 0);
231         errno = retval;
232         return (-1);
233 }
234
235 int
236 _sem_post(sem_t *sem)
237 {
238         int val;
239         
240         if (sem_check_validity(sem) != 0)
241                 return (-1);
242
243         /*
244          * sem_post() is required to be safe to call from within
245          * signal handlers, these code should work as that.
246          */
247         do {
248                 val = (*sem)->count;
249         } while (!atomic_cmpset_acq_int(&(*sem)->count, val, val + 1));
250         _thr_umtx_wake(&(*sem)->count, val + 1);
251         return (0);
252 }