xref: /illumos-gate/usr/src/lib/libc/port/threads/pthread.c (revision 60a3f738d56f92ae8b80e4b62a2331c6e1f2311f)
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 2006 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 			once->once_flag = PTHREAD_ONCE_DONE;
149 		}
150 		(void) _private_mutex_unlock(&once->mlock);
151 	}
152 
153 	return (0);
154 }
155 
156 /*
157  * pthread_equal: equates two thread ids.
158  */
159 #pragma weak	pthread_equal			= _pthread_equal
160 int
161 _pthread_equal(pthread_t t1, pthread_t t2)
162 {
163 	return (t1 == t2);
164 }
165 
166 /*
167  * pthread_getschedparam: gets the sched parameters in a struct.
168  */
169 #pragma weak	pthread_getschedparam		= _pthread_getschedparam
170 int
171 _pthread_getschedparam(pthread_t tid, int *policy, struct sched_param *param)
172 {
173 	uberdata_t *udp = curthread->ul_uberdata;
174 	ulwp_t *ulwp;
175 	int error = 0;
176 
177 	if (param == NULL || policy == NULL)
178 		error = EINVAL;
179 	else if ((ulwp = find_lwp(tid)) == NULL)
180 		error = ESRCH;
181 	else {
182 		if (ulwp->ul_pri_mapped)
183 			param->sched_priority = ulwp->ul_mappedpri;
184 		else
185 			param->sched_priority = ulwp->ul_pri;
186 		*policy = ulwp->ul_policy;
187 		ulwp_unlock(ulwp, udp);
188 	}
189 
190 	return (error);
191 }
192 
193 /*
194  * Besides the obvious arguments, the inheritflag needs to be explained:
195  * If set to PRIO_SET or PRIO_SET_PRIO, it does the normal, expected work
196  * of setting thread's assigned scheduling parameters and policy.
197  * If set to PRIO_INHERIT, it sets the thread's effective priority values
198  * (t_epri, t_empappedpri), and does not update the assigned priority values
199  * (t_pri, t_mappedpri).  If set to PRIO_DISINHERIT, it clears the thread's
200  * effective priority values, and reverts the thread, if necessary, back
201  * to the assigned priority values.
202  */
203 int
204 _thread_setschedparam_main(pthread_t tid, int policy,
205     const struct sched_param *param, int inheritflag)
206 {
207 	uberdata_t *udp = curthread->ul_uberdata;
208 	ulwp_t	*ulwp;
209 	int	error = 0;
210 	int	prio;
211 	int	opolicy;
212 	int	mappedprio;
213 	int	mapped = 0;
214 	pri_t	*mappedprip;
215 
216 	if (param == NULL)
217 		return (EINVAL);
218 	if ((ulwp = find_lwp(tid)) == NULL)
219 		return (ESRCH);
220 	prio = param->sched_priority;
221 	opolicy = ulwp->ul_policy;
222 	if (inheritflag == PRIO_SET_PRIO) {	/* don't change policy */
223 		policy = opolicy;
224 		inheritflag = PRIO_SET;
225 	}
226 	ASSERT(inheritflag == PRIO_SET || opolicy == policy);
227 	if (inheritflag == PRIO_DISINHERIT) {
228 		ulwp->ul_emappedpri = 0;
229 		ulwp->ul_epri = 0;
230 		prio = ulwp->ul_pri;	/* ignore prio in sched_param */
231 	}
232 	if (policy == SCHED_OTHER) {
233 		/*
234 		 * Set thread's policy to OTHER
235 		 */
236 		if (prio < THREAD_MIN_PRIORITY || prio > THREAD_MAX_PRIORITY) {
237 			if (_validate_rt_prio(policy, prio)) {
238 				error = EINVAL;
239 				goto out;
240 			}
241 			mapped = 1;
242 			mappedprio = prio;
243 			prio = map_rtpri_to_gp(prio);
244 			ASSERT(prio >= THREAD_MIN_PRIORITY &&
245 			    prio <= THREAD_MAX_PRIORITY);
246 		}
247 		/*
248 		 * Thread changing from FIFO/RR to OTHER
249 		 */
250 		if (opolicy == SCHED_FIFO || opolicy == SCHED_RR) {
251 			if ((error = _thrp_setlwpprio(tid, policy, prio)) != 0)
252 				goto out;
253 		}
254 		if (inheritflag != PRIO_DISINHERIT) {
255 			if (inheritflag == PRIO_INHERIT)
256 				mappedprip = &ulwp->ul_emappedpri;
257 			else
258 				mappedprip = &ulwp->ul_mappedpri;
259 			if (mapped) {
260 				ulwp->ul_pri_mapped = 1;
261 				*mappedprip = mappedprio;
262 			} else {
263 				ulwp->ul_pri_mapped = 0;
264 				*mappedprip = 0;
265 			}
266 		}
267 		ulwp->ul_policy = policy;
268 		if (inheritflag == PRIO_INHERIT)
269 			ulwp->ul_epri = prio;
270 		else
271 			ulwp->ul_pri = prio;
272 	} else if (policy == SCHED_FIFO || policy == SCHED_RR) {
273 		if (_validate_rt_prio(policy, prio))
274 			error = EINVAL;
275 		else {
276 			int prio_err;
277 
278 			if (_private_geteuid() == 0 &&
279 			    (prio_err = _thrp_setlwpprio(tid, policy, prio))) {
280 				error = prio_err;
281 				goto out;
282 			}
283 
284 			ulwp->ul_policy = policy;
285 			if (inheritflag == PRIO_INHERIT)
286 				ulwp->ul_epri = prio;
287 			else
288 				ulwp->ul_pri = prio;
289 		}
290 	} else {
291 		error = EINVAL;
292 	}
293 
294 out:
295 	ulwp_unlock(ulwp, udp);
296 	return (error);
297 }
298 
299 /*
300  * pthread_setschedparam: sets the sched parameters for a thread.
301  */
302 #pragma weak	pthread_setschedparam		= _pthread_setschedparam
303 int
304 _pthread_setschedparam(pthread_t tid,
305 	int policy, const struct sched_param *param)
306 {
307 	return (_thread_setschedparam_main(tid, policy, param, PRIO_SET));
308 }
309