pthread.c revision 2712:f74a135872bc
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 */
38typedef 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
53int
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
133int
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
160int
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
170int
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 */
203int
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
294out:
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
303int
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