xref: /illumos-gate/usr/src/lib/libc/port/threads/sema.c (revision 2f172c55ef76964744bc62b4500ece87f3089b4d)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include "lint.h"
30 #include "thr_uberdata.h"
31 
32 static uint32_t _semvaluemax;
33 
34 /*
35  * Check to see if anyone is waiting for this semaphore.
36  */
37 #pragma weak _sema_held = sema_held
38 int
39 sema_held(sema_t *sp)
40 {
41 	return (sp->count == 0);
42 }
43 
44 #pragma weak _sema_init = sema_init
45 /* ARGSUSED3 */
46 int
47 sema_init(sema_t *sp, unsigned int count, int type, void *arg)
48 {
49 	if (_semvaluemax == 0)
50 		_semvaluemax = (uint32_t)_sysconf(_SC_SEM_VALUE_MAX);
51 	if ((type != USYNC_THREAD && type != USYNC_PROCESS) ||
52 	    (count > _semvaluemax))
53 		return (EINVAL);
54 	(void) memset(sp, 0, sizeof (*sp));
55 	sp->count = count;
56 	sp->type = (uint16_t)type;
57 	sp->magic = SEMA_MAGIC;
58 
59 	/*
60 	 * This should be at the beginning of the function,
61 	 * but for the sake of old broken applications that
62 	 * do not have proper alignment for their semaphores
63 	 * (and don't check the return code from sema_init),
64 	 * we put it here, after initializing the semaphore regardless.
65 	 */
66 	if (((uintptr_t)sp & (_LONG_LONG_ALIGNMENT - 1)) &&
67 	    curthread->ul_misaligned == 0)
68 		return (EINVAL);
69 
70 	return (0);
71 }
72 
73 #pragma weak _sema_destroy = sema_destroy
74 int
75 sema_destroy(sema_t *sp)
76 {
77 	sp->magic = 0;
78 	tdb_sync_obj_deregister(sp);
79 	return (0);
80 }
81 
82 static int
83 sema_wait_impl(sema_t *sp, timespec_t *tsp)
84 {
85 	lwp_sema_t *lsp = (lwp_sema_t *)sp;
86 	ulwp_t *self = curthread;
87 	uberdata_t *udp = self->ul_uberdata;
88 	tdb_sema_stats_t *ssp = SEMA_STATS(sp, udp);
89 	hrtime_t begin_sleep = 0;
90 	uint_t count;
91 	int error = 0;
92 
93 	/*
94 	 * All variations of sema_wait() are cancellation points.
95 	 */
96 	_cancelon();
97 
98 	if (ssp)
99 		tdb_incr(ssp->sema_wait);
100 
101 	self->ul_sp = stkptr();
102 	self->ul_wchan = lsp;
103 	if (__td_event_report(self, TD_SLEEP, udp)) {
104 		self->ul_td_evbuf.eventnum = TD_SLEEP;
105 		self->ul_td_evbuf.eventdata = lsp;
106 		tdb_event(TD_SLEEP, udp);
107 	}
108 	/* just a guess, but it looks like we will sleep */
109 	if (ssp && lsp->count == 0) {
110 		begin_sleep = gethrtime();
111 		if (lsp->count == 0)	/* still looks like sleep */
112 			tdb_incr(ssp->sema_wait_sleep);
113 		else			/* we changed our mind */
114 			begin_sleep = 0;
115 	}
116 
117 	if (lsp->type == USYNC_PROCESS) {		/* kernel-level */
118 		set_parking_flag(self, 1);
119 		if (self->ul_cursig != 0 ||
120 		    (self->ul_cancelable && self->ul_cancel_pending))
121 			set_parking_flag(self, 0);
122 		/* the kernel always does FIFO queueing */
123 		error = ___lwp_sema_timedwait(lsp, tsp, 1);
124 		set_parking_flag(self, 0);
125 	} else if (!udp->uberflags.uf_mt &&		/* single threaded */
126 	    lsp->count != 0) {				/* and non-blocking */
127 		/*
128 		 * Since we are single-threaded, we don't need the
129 		 * protection of queue_lock().  However, we do need
130 		 * to block signals while modifying the count.
131 		 */
132 		sigoff(self);
133 		lsp->count--;
134 		sigon(self);
135 	} else {				/* multithreaded or blocking */
136 		queue_head_t *qp;
137 		ulwp_t *ulwp;
138 		lwpid_t lwpid = 0;
139 
140 		qp = queue_lock(lsp, CV);
141 		while (error == 0 && lsp->count == 0) {
142 			/*
143 			 * SUSV3 requires FIFO queueing for semaphores,
144 			 * at least for SCHED_FIFO and SCHED_RR scheduling.
145 			 */
146 			enqueue(qp, self, 1);
147 			lsp->sema_waiters = 1;
148 			set_parking_flag(self, 1);
149 			queue_unlock(qp);
150 			/*
151 			 * We may have received SIGCANCEL before we
152 			 * called queue_lock().  If so and we are
153 			 * cancelable we should return EINTR.
154 			 */
155 			if (self->ul_cursig != 0 ||
156 			    (self->ul_cancelable && self->ul_cancel_pending))
157 				set_parking_flag(self, 0);
158 			error = __lwp_park(tsp, 0);
159 			set_parking_flag(self, 0);
160 			qp = queue_lock(lsp, CV);
161 			if (self->ul_sleepq)	/* timeout or spurious wakeup */
162 				lsp->sema_waiters = dequeue_self(qp);
163 		}
164 		if (error == 0)
165 			lsp->count--;
166 		if (lsp->count != 0 && lsp->sema_waiters) {
167 			int more;
168 			if ((ulwp = dequeue(qp, &more)) != NULL) {
169 				no_preempt(self);
170 				lwpid = ulwp->ul_lwpid;
171 			}
172 			lsp->sema_waiters = more;
173 		}
174 		queue_unlock(qp);
175 		if (lwpid) {
176 			(void) __lwp_unpark(lwpid);
177 			preempt(self);
178 		}
179 	}
180 
181 	self->ul_wchan = NULL;
182 	self->ul_sp = 0;
183 	if (ssp) {
184 		if (error == 0) {
185 			/* we just decremented the count */
186 			count = lsp->count;
187 			if (ssp->sema_min_count > count)
188 				ssp->sema_min_count = count;
189 		}
190 		if (begin_sleep)
191 			ssp->sema_wait_sleep_time += gethrtime() - begin_sleep;
192 	}
193 
194 	if (error == EINTR)
195 		_canceloff();
196 	else
197 		_canceloff_nocancel();
198 	return (error);
199 }
200 
201 #pragma weak _sema_wait = sema_wait
202 int
203 sema_wait(sema_t *sp)
204 {
205 	ASSERT(!curthread->ul_critical || curthread->ul_bindflags);
206 	return (sema_wait_impl(sp, NULL));
207 }
208 
209 int
210 sema_reltimedwait(sema_t *sp, const timespec_t *reltime)
211 {
212 	timespec_t tslocal = *reltime;
213 
214 	ASSERT(!curthread->ul_critical || curthread->ul_bindflags);
215 	return (sema_wait_impl(sp, &tslocal));
216 }
217 
218 int
219 sema_timedwait(sema_t *sp, const timespec_t *abstime)
220 {
221 	timespec_t tslocal;
222 
223 	ASSERT(!curthread->ul_critical || curthread->ul_bindflags);
224 	abstime_to_reltime(CLOCK_REALTIME, abstime, &tslocal);
225 	return (sema_wait_impl(sp, &tslocal));
226 }
227 
228 #pragma weak _sema_trywait = sema_trywait
229 int
230 sema_trywait(sema_t *sp)
231 {
232 	lwp_sema_t *lsp = (lwp_sema_t *)sp;
233 	ulwp_t *self = curthread;
234 	uberdata_t *udp = self->ul_uberdata;
235 	tdb_sema_stats_t *ssp = SEMA_STATS(sp, udp);
236 	uint_t count;
237 	int error = 0;
238 
239 	ASSERT(!curthread->ul_critical || curthread->ul_bindflags);
240 
241 	if (ssp)
242 		tdb_incr(ssp->sema_trywait);
243 
244 	if (lsp->type == USYNC_PROCESS) {	/* kernel-level */
245 		error = _lwp_sema_trywait(lsp);
246 	} else if (!udp->uberflags.uf_mt) {	/* single threaded */
247 		sigoff(self);
248 		if (lsp->count == 0)
249 			error = EBUSY;
250 		else
251 			lsp->count--;
252 		sigon(self);
253 	} else {				/* multithreaded */
254 		queue_head_t *qp;
255 		ulwp_t *ulwp;
256 		lwpid_t lwpid = 0;
257 
258 		qp = queue_lock(lsp, CV);
259 		if (lsp->count == 0)
260 			error = EBUSY;
261 		else if (--lsp->count != 0 && lsp->sema_waiters) {
262 			int more;
263 			if ((ulwp = dequeue(qp, &more)) != NULL) {
264 				no_preempt(self);
265 				lwpid = ulwp->ul_lwpid;
266 			}
267 			lsp->sema_waiters = more;
268 		}
269 		queue_unlock(qp);
270 		if (lwpid) {
271 			(void) __lwp_unpark(lwpid);
272 			preempt(self);
273 		}
274 	}
275 
276 	if (error == 0) {
277 		if (ssp) {
278 			/* we just decremented the count */
279 			count = lsp->count;
280 			if (ssp->sema_min_count > count)
281 				ssp->sema_min_count = count;
282 		}
283 	} else {
284 		if (ssp)
285 			tdb_incr(ssp->sema_trywait_fail);
286 		if (__td_event_report(self, TD_LOCK_TRY, udp)) {
287 			self->ul_td_evbuf.eventnum = TD_LOCK_TRY;
288 			tdb_event(TD_LOCK_TRY, udp);
289 		}
290 	}
291 
292 	return (error);
293 }
294 
295 #pragma weak _sema_post = sema_post
296 int
297 sema_post(sema_t *sp)
298 {
299 	lwp_sema_t *lsp = (lwp_sema_t *)sp;
300 	ulwp_t *self = curthread;
301 	uberdata_t *udp = self->ul_uberdata;
302 	tdb_sema_stats_t *ssp = SEMA_STATS(sp, udp);
303 	uint_t count;
304 	int error = 0;
305 
306 	if (ssp)
307 		tdb_incr(ssp->sema_post);
308 	if (_semvaluemax == 0)
309 		_semvaluemax = (uint32_t)_sysconf(_SC_SEM_VALUE_MAX);
310 
311 	if (lsp->type == USYNC_PROCESS) {	/* kernel-level */
312 		error = _lwp_sema_post(lsp);
313 	} else if (!udp->uberflags.uf_mt) {	/* single threaded */
314 		sigoff(self);
315 		if (lsp->count >= _semvaluemax)
316 			error = EOVERFLOW;
317 		else
318 			lsp->count++;
319 		sigon(self);
320 	} else {				/* multithreaded */
321 		queue_head_t *qp;
322 		ulwp_t *ulwp;
323 		lwpid_t lwpid = 0;
324 
325 		qp = queue_lock(lsp, CV);
326 		if (lsp->count >= _semvaluemax)
327 			error = EOVERFLOW;
328 		else if (lsp->count++ == 0 && lsp->sema_waiters) {
329 			int more;
330 			if ((ulwp = dequeue(qp, &more)) != NULL) {
331 				no_preempt(self);
332 				lwpid = ulwp->ul_lwpid;
333 			}
334 			lsp->sema_waiters = more;
335 		}
336 		queue_unlock(qp);
337 		if (lwpid) {
338 			(void) __lwp_unpark(lwpid);
339 			preempt(self);
340 		}
341 	}
342 
343 	if (error == 0) {
344 		if (ssp) {
345 			/* we just incremented the count */
346 			count = lsp->count;
347 			if (ssp->sema_max_count < count)
348 				ssp->sema_max_count = count;
349 		}
350 	}
351 
352 	return (error);
353 }
354