xref: /illumos-gate/usr/src/lib/libc/port/threads/pthread.c (revision e56998eefc33ead0f12b364be915dd6bfc12a3f5)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
534709573Sraf  * Common Development and Distribution License (the "License").
634709573Sraf  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
2134709573Sraf 
227c478bd9Sstevel@tonic-gate /*
23d4204c85Sraf  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
247c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
257c478bd9Sstevel@tonic-gate  */
26*e56998eeSRobert Mustacchi /*
27*e56998eeSRobert Mustacchi  * Copyright 2016 Joyent, Inc.
28*e56998eeSRobert Mustacchi  */
297c478bd9Sstevel@tonic-gate 
307c478bd9Sstevel@tonic-gate #include "lint.h"
317c478bd9Sstevel@tonic-gate #include "thr_uberdata.h"
327c478bd9Sstevel@tonic-gate 
337c478bd9Sstevel@tonic-gate /*
347c478bd9Sstevel@tonic-gate  * pthread_once related data
357c478bd9Sstevel@tonic-gate  * This structure is exported as pthread_once_t in pthread.h.
367c478bd9Sstevel@tonic-gate  * We export only the size of this structure. so check
377c478bd9Sstevel@tonic-gate  * pthread_once_t in pthread.h before making a change here.
387c478bd9Sstevel@tonic-gate  */
397c478bd9Sstevel@tonic-gate typedef struct  __once {
407c478bd9Sstevel@tonic-gate 	mutex_t	mlock;
417c478bd9Sstevel@tonic-gate 	union {
427c478bd9Sstevel@tonic-gate 		uint32_t	pad32_flag[2];
437c478bd9Sstevel@tonic-gate 		uint64_t	pad64_flag;
447c478bd9Sstevel@tonic-gate 	} oflag;
457c478bd9Sstevel@tonic-gate } __once_t;
467c478bd9Sstevel@tonic-gate 
477c478bd9Sstevel@tonic-gate #define	once_flag	oflag.pad32_flag[1]
487c478bd9Sstevel@tonic-gate 
49*e56998eeSRobert Mustacchi static int
50*e56998eeSRobert Mustacchi _thr_setinherit(pthread_t tid, int inherit)
51*e56998eeSRobert Mustacchi {
52*e56998eeSRobert Mustacchi 	ulwp_t *ulwp;
53*e56998eeSRobert Mustacchi 	int error = 0;
54*e56998eeSRobert Mustacchi 
55*e56998eeSRobert Mustacchi 	if ((ulwp = find_lwp(tid)) == NULL) {
56*e56998eeSRobert Mustacchi 		error = ESRCH;
57*e56998eeSRobert Mustacchi 	} else {
58*e56998eeSRobert Mustacchi 		ulwp->ul_ptinherit = inherit;
59*e56998eeSRobert Mustacchi 		ulwp_unlock(ulwp, curthread->ul_uberdata);
60*e56998eeSRobert Mustacchi 	}
61*e56998eeSRobert Mustacchi 
62*e56998eeSRobert Mustacchi 	return (error);
63*e56998eeSRobert Mustacchi }
64*e56998eeSRobert Mustacchi 
65d4204c85Sraf static int
66d4204c85Sraf _thr_setparam(pthread_t tid, int policy, int prio)
67d4204c85Sraf {
68d4204c85Sraf 	ulwp_t *ulwp;
69d4204c85Sraf 	id_t cid;
70d4204c85Sraf 	int error = 0;
71d4204c85Sraf 
72d4204c85Sraf 	if ((ulwp = find_lwp(tid)) == NULL) {
73d4204c85Sraf 		error = ESRCH;
74d4204c85Sraf 	} else {
75d4204c85Sraf 		if (policy == ulwp->ul_policy &&
76d4204c85Sraf 		    (policy == SCHED_FIFO || policy == SCHED_RR) &&
77d4204c85Sraf 		    ulwp->ul_epri != 0) {
78d4204c85Sraf 			/*
79d4204c85Sraf 			 * Don't change the ceiling priority,
80d4204c85Sraf 			 * just the base priority.
81d4204c85Sraf 			 */
82d4204c85Sraf 			if (prio > ulwp->ul_epri)
83d4204c85Sraf 				error = EPERM;
84d4204c85Sraf 			else
85d4204c85Sraf 				ulwp->ul_pri = prio;
86d4204c85Sraf 		} else if ((cid = setparam(P_LWPID, tid, policy, prio)) == -1) {
87d4204c85Sraf 			error = errno;
88d4204c85Sraf 		} else {
89e84487aeSraf 			if (policy == SCHED_FIFO || policy == SCHED_RR)
90e84487aeSraf 				ulwp->ul_rtclassid = cid;
91d4204c85Sraf 			ulwp->ul_cid = cid;
92d4204c85Sraf 			ulwp->ul_pri = prio;
937257d1b4Sraf 			membar_producer();
94d4204c85Sraf 			ulwp->ul_policy = policy;
95d4204c85Sraf 		}
96d4204c85Sraf 		ulwp_unlock(ulwp, curthread->ul_uberdata);
97d4204c85Sraf 	}
98d4204c85Sraf 	return (error);
99d4204c85Sraf }
100d4204c85Sraf 
1017c478bd9Sstevel@tonic-gate /*
1027c478bd9Sstevel@tonic-gate  * pthread_create: creates a thread in the current process.
1037c478bd9Sstevel@tonic-gate  * calls common _thrp_create() after copying the attributes.
1047c478bd9Sstevel@tonic-gate  */
1057257d1b4Sraf #pragma weak _pthread_create = pthread_create
1067c478bd9Sstevel@tonic-gate int
1077257d1b4Sraf pthread_create(pthread_t *thread, const pthread_attr_t *attr,
108*e56998eeSRobert Mustacchi     void * (*start_routine)(void *), void *arg)
1097c478bd9Sstevel@tonic-gate {
1107c478bd9Sstevel@tonic-gate 	ulwp_t		*self = curthread;
11134709573Sraf 	const thrattr_t	*ap = attr? attr->__pthread_attrp : def_thrattr();
112d4204c85Sraf 	const pcclass_t	*pccp;
1137c478bd9Sstevel@tonic-gate 	long		flag;
1147c478bd9Sstevel@tonic-gate 	pthread_t	tid;
1157c478bd9Sstevel@tonic-gate 	int		error;
116d4204c85Sraf 
117d4204c85Sraf 	update_sched(self);
1187c478bd9Sstevel@tonic-gate 
11934709573Sraf 	if (ap == NULL)
1207c478bd9Sstevel@tonic-gate 		return (EINVAL);
1217c478bd9Sstevel@tonic-gate 
122d4204c85Sraf 	/* validate explicit scheduling attributes */
123d4204c85Sraf 	if (ap->inherit == PTHREAD_EXPLICIT_SCHED &&
124d4204c85Sraf 	    (ap->policy == SCHED_SYS ||
125d4204c85Sraf 	    (pccp = get_info_by_policy(ap->policy)) == NULL ||
126d4204c85Sraf 	    ap->prio < pccp->pcc_primin || ap->prio > pccp->pcc_primax))
127d4204c85Sraf 		return (EINVAL);
1287c478bd9Sstevel@tonic-gate 
12934709573Sraf 	flag = ap->scope | ap->detachstate | ap->daemonstate | THR_SUSPENDED;
1307c478bd9Sstevel@tonic-gate 	error = _thrp_create(ap->stkaddr, ap->stksize, start_routine, arg,
131d4204c85Sraf 	    flag, &tid, ap->guardsize);
1327c478bd9Sstevel@tonic-gate 	if (error == 0) {
133*e56998eeSRobert Mustacchi 		/*
134*e56998eeSRobert Mustacchi 		 * Record the original inheritence value for
135*e56998eeSRobert Mustacchi 		 * pthread_getattr_np(). We should always be able to find the
136*e56998eeSRobert Mustacchi 		 * thread.
137*e56998eeSRobert Mustacchi 		 */
138*e56998eeSRobert Mustacchi 		(void) _thr_setinherit(tid, ap->inherit);
139*e56998eeSRobert Mustacchi 
140d4204c85Sraf 		if (ap->inherit == PTHREAD_EXPLICIT_SCHED &&
141d4204c85Sraf 		    (ap->policy != self->ul_policy ||
142*e56998eeSRobert Mustacchi 		    ap->prio != (self->ul_epri ? self->ul_epri :
143*e56998eeSRobert Mustacchi 		    self->ul_pri))) {
144d4204c85Sraf 			/*
145d4204c85Sraf 			 * The SUSv3 specification requires pthread_create()
146d4204c85Sraf 			 * to fail with EPERM if it cannot set the scheduling
147d4204c85Sraf 			 * policy and parameters on the new thread.
148d4204c85Sraf 			 */
149d4204c85Sraf 			error = _thr_setparam(tid, ap->policy, ap->prio);
150*e56998eeSRobert Mustacchi 		}
151*e56998eeSRobert Mustacchi 
152d4204c85Sraf 		if (error) {
153d4204c85Sraf 			/*
154d4204c85Sraf 			 * We couldn't determine this error before
155d4204c85Sraf 			 * actually creating the thread.  To recover,
156d4204c85Sraf 			 * mark the thread detached and cancel it.
157d4204c85Sraf 			 * It is as though it was never created.
158d4204c85Sraf 			 */
1597c478bd9Sstevel@tonic-gate 			ulwp_t *ulwp = find_lwp(tid);
160d4204c85Sraf 			if (ulwp->ul_detached == 0) {
161d4204c85Sraf 				ulwp->ul_detached = 1;
162d4204c85Sraf 				ulwp->ul_usropts |= THR_DETACHED;
163d4204c85Sraf 				(void) __lwp_detach(tid);
164d4204c85Sraf 			}
165d4204c85Sraf 			ulwp->ul_cancel_pending = 2; /* cancelled on creation */
166d4204c85Sraf 			ulwp->ul_cancel_disabled = 0;
167d4204c85Sraf 			ulwp_unlock(ulwp, self->ul_uberdata);
168d4204c85Sraf 		} else if (thread) {
1697c478bd9Sstevel@tonic-gate 			*thread = tid;
170d4204c85Sraf 		}
1717257d1b4Sraf 		(void) thr_continue(tid);
1727c478bd9Sstevel@tonic-gate 	}
1737c478bd9Sstevel@tonic-gate 
1747c478bd9Sstevel@tonic-gate 	/* posix version expects EAGAIN for lack of memory */
1757c478bd9Sstevel@tonic-gate 	if (error == ENOMEM)
1767c478bd9Sstevel@tonic-gate 		error = EAGAIN;
1777c478bd9Sstevel@tonic-gate 	return (error);
1787c478bd9Sstevel@tonic-gate }
1797c478bd9Sstevel@tonic-gate 
1807c478bd9Sstevel@tonic-gate /*
1817c478bd9Sstevel@tonic-gate  * pthread_once: calls given function only once.
1827c478bd9Sstevel@tonic-gate  * it synchronizes via mutex in pthread_once_t structure
1837c478bd9Sstevel@tonic-gate  */
1847c478bd9Sstevel@tonic-gate int
1857257d1b4Sraf pthread_once(pthread_once_t *once_control, void (*init_routine)(void))
1867c478bd9Sstevel@tonic-gate {
1877c478bd9Sstevel@tonic-gate 	__once_t *once = (__once_t *)once_control;
1887c478bd9Sstevel@tonic-gate 
1897c478bd9Sstevel@tonic-gate 	if (once == NULL || init_routine == NULL)
1907c478bd9Sstevel@tonic-gate 		return (EINVAL);
1917c478bd9Sstevel@tonic-gate 
1927c478bd9Sstevel@tonic-gate 	if (once->once_flag == PTHREAD_ONCE_NOTDONE) {
1938cd45542Sraf 		(void) mutex_lock(&once->mlock);
1947c478bd9Sstevel@tonic-gate 		if (once->once_flag == PTHREAD_ONCE_NOTDONE) {
1958cd45542Sraf 			pthread_cleanup_push(mutex_unlock, &once->mlock);
1967c478bd9Sstevel@tonic-gate 			(*init_routine)();
1977c478bd9Sstevel@tonic-gate 			pthread_cleanup_pop(0);
1987257d1b4Sraf 			membar_producer();
1997c478bd9Sstevel@tonic-gate 			once->once_flag = PTHREAD_ONCE_DONE;
2007c478bd9Sstevel@tonic-gate 		}
2018cd45542Sraf 		(void) mutex_unlock(&once->mlock);
2027c478bd9Sstevel@tonic-gate 	}
2037257d1b4Sraf 	membar_consumer();
2047c478bd9Sstevel@tonic-gate 
2057c478bd9Sstevel@tonic-gate 	return (0);
2067c478bd9Sstevel@tonic-gate }
2077c478bd9Sstevel@tonic-gate 
2087c478bd9Sstevel@tonic-gate /*
2097c478bd9Sstevel@tonic-gate  * pthread_equal: equates two thread ids.
2107c478bd9Sstevel@tonic-gate  */
2117c478bd9Sstevel@tonic-gate int
2127257d1b4Sraf pthread_equal(pthread_t t1, pthread_t t2)
2137c478bd9Sstevel@tonic-gate {
2147c478bd9Sstevel@tonic-gate 	return (t1 == t2);
2157c478bd9Sstevel@tonic-gate }
2167c478bd9Sstevel@tonic-gate 
2177c478bd9Sstevel@tonic-gate /*
218d4204c85Sraf  * pthread_getschedparam: get the thread's sched parameters.
2197c478bd9Sstevel@tonic-gate  */
2207257d1b4Sraf #pragma weak _pthread_getschedparam = pthread_getschedparam
2217c478bd9Sstevel@tonic-gate int
2227257d1b4Sraf pthread_getschedparam(pthread_t tid, int *policy, struct sched_param *param)
2237c478bd9Sstevel@tonic-gate {
2247c478bd9Sstevel@tonic-gate 	ulwp_t *ulwp;
225d4204c85Sraf 	id_t cid;
2267c478bd9Sstevel@tonic-gate 	int error = 0;
2277c478bd9Sstevel@tonic-gate 
228d4204c85Sraf 	if ((ulwp = find_lwp(tid)) == NULL) {
2297c478bd9Sstevel@tonic-gate 		error = ESRCH;
230d4204c85Sraf 	} else {
231d4204c85Sraf 		cid = getparam(P_LWPID, ulwp->ul_lwpid, policy, param);
232d4204c85Sraf 		if (cid == -1) {
233d4204c85Sraf 			error = errno;
234d4204c85Sraf 		} else if (*policy == ulwp->ul_policy && cid == ulwp->ul_cid &&
235d4204c85Sraf 		    (*policy == SCHED_FIFO || *policy == SCHED_RR)) {
236d4204c85Sraf 			/*
237d4204c85Sraf 			 * Return the defined priority, not the effective
238d4204c85Sraf 			 * priority from priority ceiling mutexes.
239d4204c85Sraf 			 */
2407c478bd9Sstevel@tonic-gate 			param->sched_priority = ulwp->ul_pri;
241d4204c85Sraf 		} else {
242e84487aeSraf 			if (*policy == SCHED_FIFO || *policy == SCHED_RR)
243e84487aeSraf 				ulwp->ul_rtclassid = cid;
244d4204c85Sraf 			ulwp->ul_cid = cid;
245d4204c85Sraf 			ulwp->ul_pri = param->sched_priority;
2467257d1b4Sraf 			membar_producer();
247d4204c85Sraf 			ulwp->ul_policy = *policy;
248d4204c85Sraf 		}
249d4204c85Sraf 		ulwp_unlock(ulwp, curthread->ul_uberdata);
2507c478bd9Sstevel@tonic-gate 	}
2517c478bd9Sstevel@tonic-gate 
2527c478bd9Sstevel@tonic-gate 	return (error);
2537c478bd9Sstevel@tonic-gate }
2547c478bd9Sstevel@tonic-gate 
2557257d1b4Sraf #pragma weak _thr_getprio = thr_getprio
2567c478bd9Sstevel@tonic-gate int
2577257d1b4Sraf thr_getprio(thread_t tid, int *priority)
2587c478bd9Sstevel@tonic-gate {
259d4204c85Sraf 	struct sched_param param;
260d4204c85Sraf 	int policy;
261d4204c85Sraf 	int error;
2629acbbeafSnn 
2637257d1b4Sraf 	if ((error = pthread_getschedparam(tid, &policy, &param)) == 0)
264d4204c85Sraf 		*priority = param.sched_priority;
2657c478bd9Sstevel@tonic-gate 	return (error);
2667c478bd9Sstevel@tonic-gate }
2677c478bd9Sstevel@tonic-gate 
2687c478bd9Sstevel@tonic-gate /*
2697c478bd9Sstevel@tonic-gate  * pthread_setschedparam: sets the sched parameters for a thread.
2707c478bd9Sstevel@tonic-gate  */
2717c478bd9Sstevel@tonic-gate int
2727257d1b4Sraf pthread_setschedparam(pthread_t tid,
273*e56998eeSRobert Mustacchi     int policy, const struct sched_param *param)
2747c478bd9Sstevel@tonic-gate {
275d4204c85Sraf 	return (_thr_setparam(tid, policy, param->sched_priority));
276d4204c85Sraf }
277d4204c85Sraf 
2787257d1b4Sraf #pragma weak pthread_setschedprio = thr_setprio
279d4204c85Sraf int
2807257d1b4Sraf thr_setprio(thread_t tid, int prio)
281d4204c85Sraf {
282d4204c85Sraf 	struct sched_param param;
283d4204c85Sraf 	int policy;
284d4204c85Sraf 	int error;
285d4204c85Sraf 
286d4204c85Sraf 	/*
2877257d1b4Sraf 	 * pthread_getschedparam() has the side-effect of setting
288d4204c85Sraf 	 * the target thread's ul_policy, ul_pri and ul_cid correctly.
289d4204c85Sraf 	 */
2907257d1b4Sraf 	if ((error = pthread_getschedparam(tid, &policy, &param)) != 0)
291d4204c85Sraf 		return (error);
292d4204c85Sraf 	if (param.sched_priority == prio)	/* no change */
293d4204c85Sraf 		return (0);
294d4204c85Sraf 	return (_thr_setparam(tid, policy, prio));
2957c478bd9Sstevel@tonic-gate }
296