Home | History | Annotate | Download | only in threads
      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