thr_sem.c revision 144518
1112918Sjeff/*
2144518Sdavidxu * Copyright (C) 2005 David Xu <davidxu@freebsd.org>.
3112918Sjeff * Copyright (C) 2000 Jason Evans <jasone@freebsd.org>.
4112918Sjeff * All rights reserved.
5112918Sjeff *
6112918Sjeff * Redistribution and use in source and binary forms, with or without
7112918Sjeff * modification, are permitted provided that the following conditions
8112918Sjeff * are met:
9112918Sjeff * 1. Redistributions of source code must retain the above copyright
10112918Sjeff *    notice(s), this list of conditions and the following disclaimer as
11112918Sjeff *    the first lines of this file unmodified other than the possible
12112918Sjeff *    addition of one or more copyright notices.
13112918Sjeff * 2. Redistributions in binary form must reproduce the above copyright
14112918Sjeff *    notice(s), this list of conditions and the following disclaimer in
15112918Sjeff *    the documentation and/or other materials provided with the
16112918Sjeff *    distribution.
17112918Sjeff *
18112918Sjeff * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``AS IS'' AND ANY
19112918Sjeff * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20112918Sjeff * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21112918Sjeff * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) BE
22112918Sjeff * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23112918Sjeff * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24112918Sjeff * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
25112918Sjeff * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
26112918Sjeff * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
27112918Sjeff * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
28112918Sjeff * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29112918Sjeff *
30112918Sjeff * $FreeBSD: head/lib/libthr/thread/thr_sem.c 144518 2005-04-02 01:20:00Z davidxu $
31112918Sjeff */
32112918Sjeff
33144518Sdavidxu#include "namespace.h"
34144518Sdavidxu#include <sys/queue.h>
35112918Sjeff#include <errno.h>
36144518Sdavidxu#include <fcntl.h>
37144518Sdavidxu#include <pthread.h>
38112918Sjeff#include <semaphore.h>
39144518Sdavidxu#include <stdlib.h>
40144518Sdavidxu#include <time.h>
41144518Sdavidxu#include <_semaphore.h>
42144518Sdavidxu#include "un-namespace.h"
43144518Sdavidxu
44112918Sjeff#include "thr_private.h"
45112918Sjeff
46112918Sjeff
47112918Sjeff__weak_reference(_sem_init, sem_init);
48112918Sjeff__weak_reference(_sem_destroy, sem_destroy);
49144518Sdavidxu__weak_reference(_sem_getvalue, sem_getvalue);
50144518Sdavidxu__weak_reference(_sem_trywait, sem_trywait);
51112918Sjeff__weak_reference(_sem_wait, sem_wait);
52144518Sdavidxu__weak_reference(_sem_timedwait, sem_timedwait);
53112918Sjeff__weak_reference(_sem_post, sem_post);
54112918Sjeff
55112918Sjeff
56144518Sdavidxustatic inline int
57144518Sdavidxusem_check_validity(sem_t *sem)
58112918Sjeff{
59112918Sjeff
60144518Sdavidxu	if ((sem != NULL) && ((*sem)->magic == SEM_MAGIC))
61144518Sdavidxu		return (0);
62144518Sdavidxu	else {
63144518Sdavidxu		errno = EINVAL;
64144518Sdavidxu		return (-1);
65112918Sjeff	}
66144518Sdavidxu}
67112918Sjeff
68144518Sdavidxustatic sem_t
69144518Sdavidxusem_alloc(unsigned int value, semid_t semid, int system_sem)
70144518Sdavidxu{
71144518Sdavidxu	sem_t sem;
72144518Sdavidxu
73112918Sjeff	if (value > SEM_VALUE_MAX) {
74112918Sjeff		errno = EINVAL;
75144518Sdavidxu		return (NULL);
76112918Sjeff	}
77112918Sjeff
78144518Sdavidxu	sem = (sem_t)malloc(sizeof(struct sem));
79144518Sdavidxu	if (sem == NULL) {
80112918Sjeff		errno = ENOSPC;
81144518Sdavidxu		return (NULL);
82112918Sjeff	}
83144518Sdavidxu	_thr_umtx_init((umtx_t *)&sem->lock);
84112918Sjeff	/*
85144518Sdavidxu	 * Fortunatly count and nwaiters are adjacency, so we can
86144518Sdavidxu	 * use umtx_wait to wait on it, umtx_wait needs an address
87144518Sdavidxu	 * can be accessed as a long interger.
88112918Sjeff	 */
89144518Sdavidxu	sem->count = (u_int32_t)value;
90144518Sdavidxu	sem->nwaiters = 0;
91144518Sdavidxu	sem->magic = SEM_MAGIC;
92144518Sdavidxu	sem->semid = semid;
93144518Sdavidxu	sem->syssem = system_sem;
94144518Sdavidxu	return (sem);
95144518Sdavidxu}
96112918Sjeff
97144518Sdavidxuint
98144518Sdavidxu_sem_init(sem_t *sem, int pshared, unsigned int value)
99144518Sdavidxu{
100144518Sdavidxu	semid_t semid;
101144518Sdavidxu
102144518Sdavidxu	semid = (semid_t)SEM_USER;
103144518Sdavidxu	if ((pshared != 0) && (ksem_init(&semid, value) != 0))
104144518Sdavidxu		return (-1);
105144518Sdavidxu
106144518Sdavidxu	(*sem) = sem_alloc(value, semid, pshared);
107144518Sdavidxu	if ((*sem) == NULL) {
108144518Sdavidxu		if (pshared != 0)
109144518Sdavidxu			ksem_destroy(semid);
110144518Sdavidxu		return (-1);
111112918Sjeff	}
112144518Sdavidxu	return (0);
113112918Sjeff}
114112918Sjeff
115112918Sjeffint
116112918Sjeff_sem_destroy(sem_t *sem)
117112918Sjeff{
118144518Sdavidxu	int retval;
119112918Sjeff
120144518Sdavidxu	if (sem_check_validity(sem) != 0)
121144518Sdavidxu		return (-1);
122144518Sdavidxu
123144518Sdavidxu	/*
124144518Sdavidxu	 * If this is a system semaphore let the kernel track it otherwise
125144518Sdavidxu	 * make sure there are no waiters.
126144518Sdavidxu	 */
127144518Sdavidxu	if ((*sem)->syssem != 0)
128144518Sdavidxu		retval = ksem_destroy((*sem)->semid);
129144518Sdavidxu	else {
130144518Sdavidxu		retval = 0;
131144518Sdavidxu		(*sem)->magic = 0;
132112918Sjeff	}
133144518Sdavidxu	if (retval == 0)
134144518Sdavidxu		free(*sem);
135144518Sdavidxu	return (retval);
136112918Sjeff}
137112918Sjeff
138112918Sjeffint
139144518Sdavidxu_sem_getvalue(sem_t * __restrict sem, int * __restrict sval)
140112918Sjeff{
141144518Sdavidxu	int retval;
142112918Sjeff
143144518Sdavidxu	if (sem_check_validity(sem) != 0)
144144518Sdavidxu		return (-1);
145144518Sdavidxu
146144518Sdavidxu	if ((*sem)->syssem != 0)
147144518Sdavidxu		retval = ksem_getvalue((*sem)->semid, sval);
148144518Sdavidxu        else {
149144518Sdavidxu		*sval = (int)(*sem)->count;
150144518Sdavidxu		retval = 0;
151144518Sdavidxu	}
152144518Sdavidxu	return (retval);
153112918Sjeff}
154112918Sjeff
155112918Sjeffint
156144518Sdavidxu_sem_trywait(sem_t *sem)
157112918Sjeff{
158144518Sdavidxu	int val;
159112918Sjeff
160144518Sdavidxu	if (sem_check_validity(sem) != 0)
161144518Sdavidxu		return (-1);
162112918Sjeff
163144518Sdavidxu	if ((*sem)->syssem != 0)
164144518Sdavidxu 		return (ksem_trywait((*sem)->semid));
165112918Sjeff
166144518Sdavidxu	while ((val = (*sem)->count) > 0) {
167144518Sdavidxu		if (atomic_cmpset_acq_int(&(*sem)->count, val, val - 1))
168144518Sdavidxu			return (0);
169112918Sjeff	}
170144518Sdavidxu	errno = EAGAIN;
171144518Sdavidxu	return (-1);
172112918Sjeff}
173112918Sjeff
174112918Sjeffint
175144518Sdavidxu_sem_wait(sem_t *sem)
176112918Sjeff{
177144518Sdavidxu	struct pthread *curthread;
178144518Sdavidxu	int val, oldcancel, retval;
179112918Sjeff
180144518Sdavidxu	if (sem_check_validity(sem) != 0)
181144518Sdavidxu		return (-1);
182112918Sjeff
183144518Sdavidxu	curthread = _get_curthread();
184144518Sdavidxu	if ((*sem)->syssem != 0) {
185144518Sdavidxu		oldcancel = _thr_cancel_enter(curthread);
186144518Sdavidxu		retval = ksem_wait((*sem)->semid);
187144518Sdavidxu		_thr_cancel_leave(curthread, oldcancel);
188144518Sdavidxu		return (retval);
189112918Sjeff	}
190112918Sjeff
191144518Sdavidxu	_pthread_testcancel();
192144518Sdavidxu	do {
193144518Sdavidxu		while ((val = (*sem)->count) > 0) {
194144518Sdavidxu			if (atomic_cmpset_acq_int(&(*sem)->count, val, val - 1))
195144518Sdavidxu				return (0);
196144518Sdavidxu		}
197144518Sdavidxu		oldcancel = _thr_cancel_enter(curthread);
198144518Sdavidxu		retval = _thr_umtx_wait((umtx_t *)&(*sem)->count, 0, NULL);
199144518Sdavidxu		_thr_cancel_leave(curthread, oldcancel);
200144518Sdavidxu	} while (retval == 0);
201144518Sdavidxu	errno = retval;
202144518Sdavidxu	return (-1);
203112918Sjeff}
204112918Sjeff
205112918Sjeffint
206144518Sdavidxu_sem_timedwait(sem_t * __restrict sem, struct timespec * __restrict abstime)
207112918Sjeff{
208144518Sdavidxu	struct timespec ts, ts2;
209144518Sdavidxu	struct pthread *curthread;
210144518Sdavidxu	int val, oldcancel, retval;
211112918Sjeff
212144518Sdavidxu	if (sem_check_validity(sem) != 0)
213144518Sdavidxu		return (-1);
214112918Sjeff
215144518Sdavidxu	curthread = _get_curthread();
216144518Sdavidxu	if ((*sem)->syssem != 0) {
217144518Sdavidxu		oldcancel = _thr_cancel_enter(curthread);
218144518Sdavidxu		retval = ksem_timedwait((*sem)->semid, abstime);
219144518Sdavidxu		_thr_cancel_leave(curthread, oldcancel);
220144518Sdavidxu		return (retval);
221144518Sdavidxu	}
222144518Sdavidxu
223112918Sjeff	/*
224144518Sdavidxu	 * The timeout argument is only supposed to
225144518Sdavidxu	 * be checked if the thread would have blocked.
226112918Sjeff	 */
227144518Sdavidxu	_pthread_testcancel();
228144518Sdavidxu	do {
229144518Sdavidxu		while ((val = (*sem)->count) > 0) {
230144518Sdavidxu			if (atomic_cmpset_acq_int(&(*sem)->count, val, val - 1))
231144518Sdavidxu				return (0);
232144518Sdavidxu		}
233144518Sdavidxu		if (abstime == NULL) {
234144518Sdavidxu			errno = EINVAL;
235144518Sdavidxu			return (-1);
236144518Sdavidxu		}
237144518Sdavidxu		clock_gettime(CLOCK_REALTIME, &ts);
238144518Sdavidxu		TIMESPEC_SUB(&ts2, abstime, &ts);
239144518Sdavidxu		oldcancel = _thr_cancel_enter(curthread);
240144518Sdavidxu		retval = _thr_umtx_wait((umtx_t *)&(*sem)->count, 0, &ts2);
241144518Sdavidxu		_thr_cancel_leave(curthread, oldcancel);
242144518Sdavidxu	} while (retval == 0);
243144518Sdavidxu	errno = retval;
244144518Sdavidxu	return (-1);
245112918Sjeff}
246112918Sjeff
247112918Sjeffint
248144518Sdavidxu_sem_post(sem_t *sem)
249112918Sjeff{
250144518Sdavidxu	int val, retval;
251144518Sdavidxu
252144518Sdavidxu	if (sem_check_validity(sem) != 0)
253144518Sdavidxu		return (-1);
254112918Sjeff
255144518Sdavidxu	if ((*sem)->syssem != 0)
256144518Sdavidxu		return (ksem_post((*sem)->semid));
257112918Sjeff
258144518Sdavidxu	/*
259144518Sdavidxu	 * sem_post() is required to be safe to call from within
260144518Sdavidxu	 * signal handlers, these code should work as that.
261144518Sdavidxu	 */
262144518Sdavidxu	do {
263144518Sdavidxu		val = (*sem)->count;
264144518Sdavidxu	} while (!atomic_cmpset_acq_int(&(*sem)->count, val, val + 1));
265144518Sdavidxu	retval = _thr_umtx_wake((umtx_t *)&(*sem)->count, val + 1);
266144518Sdavidxu	if (retval > 0)
267144518Sdavidxu		retval = 0;
268144518Sdavidxu	return (retval);
269112918Sjeff}
270