xref: /illumos-gate/usr/src/lib/libc/port/threads/pthread.c (revision 8548bf79039833dba8615afdf63258b2cb122121)
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 2007 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 /*
33  * pthread_once related data
34  * This structure is exported as pthread_once_t in pthread.h.
35  * We export only the size of this structure. so check
36  * pthread_once_t in pthread.h before making a change here.
37  */
38 typedef struct  __once {
39 	mutex_t	mlock;
40 	union {
41 		uint32_t	pad32_flag[2];
42 		uint64_t	pad64_flag;
43 	} oflag;
44 } __once_t;
45 
46 #define	once_flag	oflag.pad32_flag[1]
47 
48 /*
49  * pthread_create: creates a thread in the current process.
50  * calls common _thrp_create() after copying the attributes.
51  */
52 #pragma weak	pthread_create			= _pthread_create
53 int
54 _pthread_create(pthread_t *thread, const pthread_attr_t *attr,
55 	void * (*start_routine)(void *), void *arg)
56 {
57 	ulwp_t		*self = curthread;
58 	uberdata_t	*udp = self->ul_uberdata;
59 	const thrattr_t	*ap = attr? attr->__pthread_attrp : def_thrattr();
60 	long		flag;
61 	pthread_t	tid;
62 	int		policy;
63 	pri_t		priority;
64 	int		error;
65 	int		mapped = 0;
66 	int		mappedpri;
67 	int		rt = 0;
68 
69 	if (ap == NULL)
70 		return (EINVAL);
71 
72 	if (ap->inherit == PTHREAD_INHERIT_SCHED) {
73 		policy = self->ul_policy;
74 		priority = self->ul_pri;
75 		mapped = self->ul_pri_mapped;
76 		mappedpri = self->ul_mappedpri;
77 	} else {
78 		policy = ap->policy;
79 		priority = ap->prio;
80 		if (policy == SCHED_OTHER) {
81 			if (priority < THREAD_MIN_PRIORITY ||
82 			    priority > THREAD_MAX_PRIORITY) {
83 				if (_validate_rt_prio(policy, priority))
84 					return (EINVAL);
85 				mapped = 1;
86 				mappedpri = priority;
87 				priority = map_rtpri_to_gp(priority);
88 				ASSERT(priority >= THREAD_MIN_PRIORITY &&
89 				    priority <= THREAD_MAX_PRIORITY);
90 			}
91 		} else if (policy == SCHED_FIFO || policy == SCHED_RR) {
92 			if (_validate_rt_prio(policy, priority))
93 				return (EINVAL);
94 			if (_private_geteuid() == 0)
95 				rt = 1;
96 		} else {
97 			return (EINVAL);
98 		}
99 	}
100 
101 	flag = ap->scope | ap->detachstate | ap->daemonstate | THR_SUSPENDED;
102 	error = _thrp_create(ap->stkaddr, ap->stksize, start_routine, arg,
103 		flag, &tid, priority, policy, ap->guardsize);
104 	if (error == 0) {
105 		int prio_err;
106 
107 		if (mapped) {
108 			ulwp_t *ulwp = find_lwp(tid);
109 			ulwp->ul_pri_mapped = 1;
110 			ulwp->ul_mappedpri = mappedpri;
111 			ulwp_unlock(ulwp, udp);
112 		}
113 
114 		if (rt && (prio_err = _thrp_setlwpprio(tid, policy, priority)))
115 			return (prio_err);
116 
117 		if (thread)
118 			*thread = tid;
119 		(void) _thr_continue(tid);
120 	}
121 
122 	/* posix version expects EAGAIN for lack of memory */
123 	if (error == ENOMEM)
124 		error = EAGAIN;
125 	return (error);
126 }
127 
128 /*
129  * pthread_once: calls given function only once.
130  * it synchronizes via mutex in pthread_once_t structure
131  */
132 #pragma weak	pthread_once			= _pthread_once
133 int
134 _pthread_once(pthread_once_t *once_control, void (*init_routine)(void))
135 {
136 	__once_t *once = (__once_t *)once_control;
137 
138 	if (once == NULL || init_routine == NULL)
139 		return (EINVAL);
140 
141 	if (once->once_flag == PTHREAD_ONCE_NOTDONE) {
142 		(void) _private_mutex_lock(&once->mlock);
143 		if (once->once_flag == PTHREAD_ONCE_NOTDONE) {
144 			pthread_cleanup_push(_private_mutex_unlock,
145 			    &once->mlock);
146 			(*init_routine)();
147 			pthread_cleanup_pop(0);
148 			_membar_producer();
149 			once->once_flag = PTHREAD_ONCE_DONE;
150 		}
151 		(void) _private_mutex_unlock(&once->mlock);
152 	}
153 	_membar_consumer();
154 
155 	return (0);
156 }
157 
158 /*
159  * pthread_equal: equates two thread ids.
160  */
161 #pragma weak	pthread_equal			= _pthread_equal
162 int
163 _pthread_equal(pthread_t t1, pthread_t t2)
164 {
165 	return (t1 == t2);
166 }
167 
168 /*
169  * pthread_getschedparam: gets the sched parameters in a struct.
170  */
171 #pragma weak	pthread_getschedparam		= _pthread_getschedparam
172 int
173 _pthread_getschedparam(pthread_t tid, int *policy, struct sched_param *param)
174 {
175 	uberdata_t *udp = curthread->ul_uberdata;
176 	ulwp_t *ulwp;
177 	int error = 0;
178 
179 	if (param == NULL || policy == NULL)
180 		error = EINVAL;
181 	else if ((ulwp = find_lwp(tid)) == NULL)
182 		error = ESRCH;
183 	else {
184 		if (ulwp->ul_pri_mapped)
185 			param->sched_priority = ulwp->ul_mappedpri;
186 		else
187 			param->sched_priority = ulwp->ul_pri;
188 		*policy = ulwp->ul_policy;
189 		ulwp_unlock(ulwp, udp);
190 	}
191 
192 	return (error);
193 }
194 
195 /*
196  * Besides the obvious arguments, the inheritflag needs to be explained:
197  * If set to PRIO_SET or PRIO_SET_PRIO, it does the normal, expected work
198  * of setting thread's assigned scheduling parameters and policy.
199  * If set to PRIO_INHERIT, it sets the thread's effective priority values
200  * (t_epri, t_empappedpri), and does not update the assigned priority values
201  * (t_pri, t_mappedpri).  If set to PRIO_DISINHERIT, it clears the thread's
202  * effective priority values, and reverts the thread, if necessary, back
203  * to the assigned priority values.
204  */
205 int
206 _thread_setschedparam_main(pthread_t tid, int policy,
207     const struct sched_param *param, int inheritflag)
208 {
209 	uberdata_t *udp = curthread->ul_uberdata;
210 	ulwp_t	*ulwp;
211 	int	error = 0;
212 	int	prio;
213 	int	opolicy;
214 	int	mappedprio;
215 	int	mapped = 0;
216 	pri_t	*mappedprip;
217 
218 	if (param == NULL)
219 		return (EINVAL);
220 	if ((ulwp = find_lwp(tid)) == NULL)
221 		return (ESRCH);
222 	prio = param->sched_priority;
223 	opolicy = ulwp->ul_policy;
224 	if (inheritflag == PRIO_SET_PRIO) {	/* don't change policy */
225 		policy = opolicy;
226 		inheritflag = PRIO_SET;
227 	}
228 	ASSERT(inheritflag == PRIO_SET || opolicy == policy);
229 	if (inheritflag == PRIO_DISINHERIT) {
230 		ulwp->ul_emappedpri = 0;
231 		ulwp->ul_epri = 0;
232 		prio = ulwp->ul_pri;	/* ignore prio in sched_param */
233 	}
234 	if (policy == SCHED_OTHER) {
235 		/*
236 		 * Set thread's policy to OTHER
237 		 */
238 		if (prio < THREAD_MIN_PRIORITY || prio > THREAD_MAX_PRIORITY) {
239 			if (_validate_rt_prio(policy, prio)) {
240 				error = EINVAL;
241 				goto out;
242 			}
243 			mapped = 1;
244 			mappedprio = prio;
245 			prio = map_rtpri_to_gp(prio);
246 			ASSERT(prio >= THREAD_MIN_PRIORITY &&
247 			    prio <= THREAD_MAX_PRIORITY);
248 		}
249 		/*
250 		 * Thread changing from FIFO/RR to OTHER
251 		 */
252 		if (opolicy == SCHED_FIFO || opolicy == SCHED_RR) {
253 			if ((error = _thrp_setlwpprio(tid, policy, prio)) != 0)
254 				goto out;
255 		}
256 		if (inheritflag != PRIO_DISINHERIT) {
257 			if (inheritflag == PRIO_INHERIT)
258 				mappedprip = &ulwp->ul_emappedpri;
259 			else
260 				mappedprip = &ulwp->ul_mappedpri;
261 			if (mapped) {
262 				ulwp->ul_pri_mapped = 1;
263 				*mappedprip = mappedprio;
264 			} else {
265 				ulwp->ul_pri_mapped = 0;
266 				*mappedprip = 0;
267 			}
268 		}
269 		ulwp->ul_policy = policy;
270 		if (inheritflag == PRIO_INHERIT)
271 			ulwp->ul_epri = prio;
272 		else
273 			ulwp->ul_pri = prio;
274 	} else if (policy == SCHED_FIFO || policy == SCHED_RR) {
275 		if (_validate_rt_prio(policy, prio))
276 			error = EINVAL;
277 		else {
278 			int prio_err;
279 
280 			if (_private_geteuid() == 0 &&
281 			    (prio_err = _thrp_setlwpprio(tid, policy, prio))) {
282 				error = prio_err;
283 				goto out;
284 			}
285 
286 			ulwp->ul_policy = policy;
287 			if (inheritflag == PRIO_INHERIT)
288 				ulwp->ul_epri = prio;
289 			else
290 				ulwp->ul_pri = prio;
291 		}
292 	} else {
293 		error = EINVAL;
294 	}
295 
296 out:
297 	ulwp_unlock(ulwp, udp);
298 	return (error);
299 }
300 
301 /*
302  * pthread_setschedparam: sets the sched parameters for a thread.
303  */
304 #pragma weak	pthread_setschedparam		= _pthread_setschedparam
305 int
306 _pthread_setschedparam(pthread_t tid,
307 	int policy, const struct sched_param *param)
308 {
309 	return (_thread_setschedparam_main(tid, policy, param, PRIO_SET));
310 }
311