]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/libc/gen/sem_new.c
MFV: r351091
[FreeBSD/FreeBSD.git] / lib / libc / gen / sem_new.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (C) 2010 David Xu <davidxu@freebsd.org>.
5  * All rights reserved.
6  * 
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice(s), this list of conditions and the following disclaimer as
12  *    the first lines of this file unmodified other than the possible
13  *    addition of one or more copyright notices.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice(s), this list of conditions and the following disclaimer in
16  *    the documentation and/or other materials provided with the
17  *    distribution.
18  * 
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``AS IS'' AND ANY
20  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) BE
23  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
26  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
27  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
28  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
29  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  *
31  * $FreeBSD$
32  */
33
34 #include "namespace.h"
35 #include <sys/types.h>
36 #include <sys/queue.h>
37 #include <sys/mman.h>
38 #include <sys/stat.h>
39 #include <errno.h>
40 #include <machine/atomic.h>
41 #include <sys/umtx.h>
42 #include <limits.h>
43 #include <fcntl.h>
44 #include <pthread.h>
45 #include <stdarg.h>
46 #include <stdbool.h>
47 #include <stdlib.h>
48 #include <string.h>
49 #include <time.h>
50 #include <semaphore.h>
51 #include <unistd.h>
52 #include "un-namespace.h"
53 #include "libc_private.h"
54
55 __weak_reference(_sem_close, sem_close);
56 __weak_reference(_sem_destroy, sem_destroy);
57 __weak_reference(_sem_getvalue, sem_getvalue);
58 __weak_reference(_sem_init, sem_init);
59 __weak_reference(_sem_open, sem_open);
60 __weak_reference(_sem_post, sem_post);
61 __weak_reference(_sem_timedwait, sem_timedwait);
62 __weak_reference(_sem_clockwait_np, sem_clockwait_np);
63 __weak_reference(_sem_trywait, sem_trywait);
64 __weak_reference(_sem_unlink, sem_unlink);
65 __weak_reference(_sem_wait, sem_wait);
66
67 #define SEM_PREFIX      "/tmp/SEMD"
68 #define SEM_MAGIC       ((u_int32_t)0x73656d32)
69
70 _Static_assert(SEM_VALUE_MAX <= USEM_MAX_COUNT, "SEM_VALUE_MAX too large");
71
72 struct sem_nameinfo {
73         int open_count;
74         char *name;
75         dev_t dev;
76         ino_t ino;
77         sem_t *sem;
78         LIST_ENTRY(sem_nameinfo) next;
79 };
80
81 static pthread_once_t once = PTHREAD_ONCE_INIT;
82 static pthread_mutex_t sem_llock;
83 static LIST_HEAD(, sem_nameinfo) sem_list = LIST_HEAD_INITIALIZER(sem_list);
84
85 static void
86 sem_prefork(void)
87 {
88         
89         _pthread_mutex_lock(&sem_llock);
90 }
91
92 static void
93 sem_postfork(void)
94 {
95
96         _pthread_mutex_unlock(&sem_llock);
97 }
98
99 static void
100 sem_child_postfork(void)
101 {
102
103         _pthread_mutex_unlock(&sem_llock);
104 }
105
106 static void
107 sem_module_init(void)
108 {
109
110         _pthread_mutex_init(&sem_llock, NULL);
111         _pthread_atfork(sem_prefork, sem_postfork, sem_child_postfork);
112 }
113
114 static inline int
115 sem_check_validity(sem_t *sem)
116 {
117
118         if (sem->_magic == SEM_MAGIC)
119                 return (0);
120         errno = EINVAL;
121         return (-1);
122 }
123
124 int
125 _sem_init(sem_t *sem, int pshared, unsigned int value)
126 {
127
128         if (value > SEM_VALUE_MAX) {
129                 errno = EINVAL;
130                 return (-1);
131         }
132  
133         bzero(sem, sizeof(sem_t));
134         sem->_magic = SEM_MAGIC;
135         sem->_kern._count = (u_int32_t)value;
136         sem->_kern._flags = pshared ? USYNC_PROCESS_SHARED : 0;
137         return (0);
138 }
139
140 sem_t *
141 _sem_open(const char *name, int flags, ...)
142 {
143         char path[PATH_MAX];
144         struct stat sb;
145         va_list ap;
146         struct sem_nameinfo *ni;
147         sem_t *sem, tmp;
148         int errsave, fd, len, mode, value;
149
150         ni = NULL;
151         sem = NULL;
152         fd = -1;
153         value = 0;
154
155         if (name[0] != '/') {
156                 errno = EINVAL;
157                 return (SEM_FAILED);
158         }
159         name++;
160         strcpy(path, SEM_PREFIX);
161         if (strlcat(path, name, sizeof(path)) >= sizeof(path)) {
162                 errno = ENAMETOOLONG;
163                 return (SEM_FAILED);
164         }
165         if (flags & ~(O_CREAT|O_EXCL)) {
166                 errno = EINVAL;
167                 return (SEM_FAILED);
168         }
169         if ((flags & O_CREAT) != 0) {
170                 va_start(ap, flags);
171                 mode = va_arg(ap, int);
172                 value = va_arg(ap, int);
173                 va_end(ap);
174         }
175         fd = -1;
176         _pthread_once(&once, sem_module_init);
177
178         _pthread_mutex_lock(&sem_llock);
179         LIST_FOREACH(ni, &sem_list, next) {
180                 if (ni->name != NULL && strcmp(name, ni->name) == 0) {
181                         fd = _open(path, flags | O_RDWR | O_CLOEXEC |
182                             O_EXLOCK, mode);
183                         if (fd == -1 || _fstat(fd, &sb) == -1) {
184                                 ni = NULL;
185                                 goto error;
186                         }
187                         if ((flags & (O_CREAT | O_EXCL)) == (O_CREAT |
188                             O_EXCL) || ni->dev != sb.st_dev ||
189                             ni->ino != sb.st_ino) {
190                                 ni->name = NULL;
191                                 ni = NULL;
192                                 break;
193                         }
194                         ni->open_count++;
195                         sem = ni->sem;
196                         _pthread_mutex_unlock(&sem_llock);
197                         _close(fd);
198                         return (sem);
199                 }
200         }
201
202         len = sizeof(*ni) + strlen(name) + 1;
203         ni = (struct sem_nameinfo *)malloc(len);
204         if (ni == NULL) {
205                 errno = ENOSPC;
206                 goto error;
207         }
208
209         ni->name = (char *)(ni+1);
210         strcpy(ni->name, name);
211
212         if (fd == -1) {
213                 fd = _open(path, flags | O_RDWR | O_CLOEXEC | O_EXLOCK, mode);
214                 if (fd == -1 || _fstat(fd, &sb) == -1)
215                         goto error;
216         }
217         if (sb.st_size < sizeof(sem_t)) {
218                 tmp._magic = SEM_MAGIC;
219                 tmp._kern._count = value;
220                 tmp._kern._flags = USYNC_PROCESS_SHARED | SEM_NAMED;
221                 if (_write(fd, &tmp, sizeof(tmp)) != sizeof(tmp))
222                         goto error;
223         }
224         flock(fd, LOCK_UN);
225         sem = mmap(NULL, sizeof(sem_t), PROT_READ | PROT_WRITE,
226             MAP_SHARED | MAP_NOSYNC, fd, 0);
227         if (sem == MAP_FAILED) {
228                 sem = NULL;
229                 if (errno == ENOMEM)
230                         errno = ENOSPC;
231                 goto error;
232         }
233         if (sem->_magic != SEM_MAGIC) {
234                 errno = EINVAL;
235                 goto error;
236         }
237         ni->open_count = 1;
238         ni->sem = sem;
239         ni->dev = sb.st_dev;
240         ni->ino = sb.st_ino;
241         LIST_INSERT_HEAD(&sem_list, ni, next);
242         _close(fd);
243         _pthread_mutex_unlock(&sem_llock);
244         return (sem);
245
246 error:
247         errsave = errno;
248         if (fd != -1)
249                 _close(fd);
250         if (sem != NULL)
251                 munmap(sem, sizeof(sem_t));
252         free(ni);
253         _pthread_mutex_unlock(&sem_llock);
254         errno = errsave;
255         return (SEM_FAILED);
256 }
257
258 int
259 _sem_close(sem_t *sem)
260 {
261         struct sem_nameinfo *ni;
262         bool last;
263
264         if (sem_check_validity(sem) != 0)
265                 return (-1);
266
267         if (!(sem->_kern._flags & SEM_NAMED)) {
268                 errno = EINVAL;
269                 return (-1);
270         }
271
272         _pthread_once(&once, sem_module_init);
273
274         _pthread_mutex_lock(&sem_llock);
275         LIST_FOREACH(ni, &sem_list, next) {
276                 if (sem == ni->sem) {
277                         last = --ni->open_count == 0;
278                         if (last)
279                                 LIST_REMOVE(ni, next);
280                         _pthread_mutex_unlock(&sem_llock);
281                         if (last) {
282                                 munmap(sem, sizeof(*sem));
283                                 free(ni);
284                         }
285                         return (0);
286                 }
287         }
288         _pthread_mutex_unlock(&sem_llock);
289         errno = EINVAL;
290         return (-1);
291 }
292
293 int
294 _sem_unlink(const char *name)
295 {
296         char path[PATH_MAX];
297
298         if (name[0] != '/') {
299                 errno = ENOENT;
300                 return -1;
301         }
302         name++;
303         strcpy(path, SEM_PREFIX);
304         if (strlcat(path, name, sizeof(path)) >= sizeof(path)) {
305                 errno = ENAMETOOLONG;
306                 return (-1);
307         }
308
309         return (unlink(path));
310 }
311
312 int
313 _sem_destroy(sem_t *sem)
314 {
315
316         if (sem_check_validity(sem) != 0)
317                 return (-1);
318
319         if (sem->_kern._flags & SEM_NAMED) {
320                 errno = EINVAL;
321                 return (-1);
322         }
323         sem->_magic = 0;
324         return (0);
325 }
326
327 int
328 _sem_getvalue(sem_t * __restrict sem, int * __restrict sval)
329 {
330
331         if (sem_check_validity(sem) != 0)
332                 return (-1);
333
334         *sval = (int)USEM_COUNT(sem->_kern._count);
335         return (0);
336 }
337
338 static __inline int
339 usem_wake(struct _usem2 *sem)
340 {
341
342         return (_umtx_op(sem, UMTX_OP_SEM2_WAKE, 0, NULL, NULL));
343 }
344
345 static __inline int
346 usem_wait(struct _usem2 *sem, clockid_t clock_id, int flags,
347     const struct timespec *rqtp, struct timespec *rmtp)
348 {
349         struct {
350                 struct _umtx_time timeout;
351                 struct timespec remain;
352         } tms;
353         void *tm_p;
354         size_t tm_size;
355         int retval;
356
357         if (rqtp == NULL) {
358                 tm_p = NULL;
359                 tm_size = 0;
360         } else {
361                 tms.timeout._clockid = clock_id;
362                 tms.timeout._flags = (flags & TIMER_ABSTIME) ? UMTX_ABSTIME : 0;
363                 tms.timeout._timeout = *rqtp;
364                 tm_p = &tms;
365                 tm_size = sizeof(tms);
366         }
367         retval = _umtx_op(sem, UMTX_OP_SEM2_WAIT, 0, (void *)tm_size, tm_p);
368         if (retval == -1 && errno == EINTR && (flags & TIMER_ABSTIME) == 0 &&
369             rqtp != NULL && rmtp != NULL) {
370                 *rmtp = tms.remain;
371         }
372
373         return (retval);
374 }
375
376 int
377 _sem_trywait(sem_t *sem)
378 {
379         int val;
380
381         if (sem_check_validity(sem) != 0)
382                 return (-1);
383
384         while (USEM_COUNT(val = sem->_kern._count) > 0) {
385                 if (atomic_cmpset_acq_int(&sem->_kern._count, val, val - 1))
386                         return (0);
387         }
388         errno = EAGAIN;
389         return (-1);
390 }
391
392 int
393 _sem_clockwait_np(sem_t * __restrict sem, clockid_t clock_id, int flags,
394         const struct timespec *rqtp, struct timespec *rmtp)
395 {
396         int val, retval;
397
398         if (sem_check_validity(sem) != 0)
399                 return (-1);
400
401         retval = 0;
402         _pthread_testcancel();
403         for (;;) {
404                 while (USEM_COUNT(val = sem->_kern._count) > 0) {
405                         if (atomic_cmpset_acq_int(&sem->_kern._count, val,
406                             val - 1))
407                                 return (0);
408                 }
409
410                 if (retval) {
411                         _pthread_testcancel();
412                         break;
413                 }
414
415                 /*
416                  * The timeout argument is only supposed to
417                  * be checked if the thread would have blocked.
418                  */
419                 if (rqtp != NULL) {
420                         if (rqtp->tv_nsec >= 1000000000 || rqtp->tv_nsec < 0) {
421                                 errno = EINVAL;
422                                 return (-1);
423                         }
424                 }
425                 _pthread_cancel_enter(1);
426                 retval = usem_wait(&sem->_kern, clock_id, flags, rqtp, rmtp);
427                 _pthread_cancel_leave(0);
428         }
429         return (retval);
430 }
431
432 int
433 _sem_timedwait(sem_t * __restrict sem,
434         const struct timespec * __restrict abstime)
435 {
436
437         return (_sem_clockwait_np(sem, CLOCK_REALTIME, TIMER_ABSTIME, abstime,
438             NULL));
439 };
440
441 int
442 _sem_wait(sem_t *sem)
443 {
444
445         return (_sem_timedwait(sem, NULL));
446 }
447
448 /*
449  * POSIX:
450  * The sem_post() interface is reentrant with respect to signals and may be
451  * invoked from a signal-catching function. 
452  * The implementation does not use lock, so it should be safe.
453  */
454 int
455 _sem_post(sem_t *sem)
456 {
457         unsigned int count;
458
459         if (sem_check_validity(sem) != 0)
460                 return (-1);
461
462         do {
463                 count = sem->_kern._count;
464                 if (USEM_COUNT(count) + 1 > SEM_VALUE_MAX) {
465                         errno = EOVERFLOW;
466                         return (-1);
467                 }
468         } while (!atomic_cmpset_rel_int(&sem->_kern._count, count, count + 1));
469         if (count & USEM_HAS_WAITERS)
470                 usem_wake(&sem->_kern);
471         return (0);
472 }