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 /*
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 static int
_thr_setparam(pthread_t tid,int policy,int prio)49 _thr_setparam(pthread_t tid, int policy, int prio)
50 {
51 ulwp_t *ulwp;
52 id_t cid;
53 int error = 0;
54
55 if ((ulwp = find_lwp(tid)) == NULL) {
56 error = ESRCH;
57 } else {
58 if (policy == ulwp->ul_policy &&
59 (policy == SCHED_FIFO || policy == SCHED_RR) &&
60 ulwp->ul_epri != 0) {
61 /*
62 * Don't change the ceiling priority,
63 * just the base priority.
64 */
65 if (prio > ulwp->ul_epri)
66 error = EPERM;
67 else
68 ulwp->ul_pri = prio;
69 } else if ((cid = setparam(P_LWPID, tid, policy, prio)) == -1) {
70 error = errno;
71 } else {
72 if (policy == SCHED_FIFO || policy == SCHED_RR)
73 ulwp->ul_rtclassid = cid;
74 ulwp->ul_cid = cid;
75 ulwp->ul_pri = prio;
76 membar_producer();
77 ulwp->ul_policy = policy;
78 }
79 ulwp_unlock(ulwp, curthread->ul_uberdata);
80 }
81 return (error);
82 }
83
84 /*
85 * pthread_create: creates a thread in the current process.
86 * calls common _thrp_create() after copying the attributes.
87 */
88 #pragma weak _pthread_create = pthread_create
89 int
pthread_create(pthread_t * thread,const pthread_attr_t * attr,void * (* start_routine)(void *),void * arg)90 pthread_create(pthread_t *thread, const pthread_attr_t *attr,
91 void * (*start_routine)(void *), void *arg)
92 {
93 ulwp_t *self = curthread;
94 const thrattr_t *ap = attr? attr->__pthread_attrp : def_thrattr();
95 const pcclass_t *pccp;
96 long flag;
97 pthread_t tid;
98 int error;
99
100 update_sched(self);
101
102 if (ap == NULL)
103 return (EINVAL);
104
105 /* validate explicit scheduling attributes */
106 if (ap->inherit == PTHREAD_EXPLICIT_SCHED &&
107 (ap->policy == SCHED_SYS ||
108 (pccp = get_info_by_policy(ap->policy)) == NULL ||
109 ap->prio < pccp->pcc_primin || ap->prio > pccp->pcc_primax))
110 return (EINVAL);
111
112 flag = ap->scope | ap->detachstate | ap->daemonstate | THR_SUSPENDED;
113 error = _thrp_create(ap->stkaddr, ap->stksize, start_routine, arg,
114 flag, &tid, ap->guardsize);
115 if (error == 0) {
116 if (ap->inherit == PTHREAD_EXPLICIT_SCHED &&
117 (ap->policy != self->ul_policy ||
118 ap->prio != (self->ul_epri? self->ul_epri : self->ul_pri)))
119 /*
120 * The SUSv3 specification requires pthread_create()
121 * to fail with EPERM if it cannot set the scheduling
122 * policy and parameters on the new thread.
123 */
124 error = _thr_setparam(tid, ap->policy, ap->prio);
125 if (error) {
126 /*
127 * We couldn't determine this error before
128 * actually creating the thread. To recover,
129 * mark the thread detached and cancel it.
130 * It is as though it was never created.
131 */
132 ulwp_t *ulwp = find_lwp(tid);
133 if (ulwp->ul_detached == 0) {
134 ulwp->ul_detached = 1;
135 ulwp->ul_usropts |= THR_DETACHED;
136 (void) __lwp_detach(tid);
137 }
138 ulwp->ul_cancel_pending = 2; /* cancelled on creation */
139 ulwp->ul_cancel_disabled = 0;
140 ulwp_unlock(ulwp, self->ul_uberdata);
141 } else if (thread) {
142 *thread = tid;
143 }
144 (void) thr_continue(tid);
145 }
146
147 /* posix version expects EAGAIN for lack of memory */
148 if (error == ENOMEM)
149 error = EAGAIN;
150 return (error);
151 }
152
153 /*
154 * pthread_once: calls given function only once.
155 * it synchronizes via mutex in pthread_once_t structure
156 */
157 int
pthread_once(pthread_once_t * once_control,void (* init_routine)(void))158 pthread_once(pthread_once_t *once_control, void (*init_routine)(void))
159 {
160 __once_t *once = (__once_t *)once_control;
161
162 if (once == NULL || init_routine == NULL)
163 return (EINVAL);
164
165 if (once->once_flag == PTHREAD_ONCE_NOTDONE) {
166 (void) mutex_lock(&once->mlock);
167 if (once->once_flag == PTHREAD_ONCE_NOTDONE) {
168 pthread_cleanup_push(mutex_unlock, &once->mlock);
169 (*init_routine)();
170 pthread_cleanup_pop(0);
171 membar_producer();
172 once->once_flag = PTHREAD_ONCE_DONE;
173 }
174 (void) mutex_unlock(&once->mlock);
175 }
176 membar_consumer();
177
178 return (0);
179 }
180
181 /*
182 * pthread_equal: equates two thread ids.
183 */
184 int
pthread_equal(pthread_t t1,pthread_t t2)185 pthread_equal(pthread_t t1, pthread_t t2)
186 {
187 return (t1 == t2);
188 }
189
190 /*
191 * pthread_getschedparam: get the thread's sched parameters.
192 */
193 #pragma weak _pthread_getschedparam = pthread_getschedparam
194 int
pthread_getschedparam(pthread_t tid,int * policy,struct sched_param * param)195 pthread_getschedparam(pthread_t tid, int *policy, struct sched_param *param)
196 {
197 ulwp_t *ulwp;
198 id_t cid;
199 int error = 0;
200
201 if ((ulwp = find_lwp(tid)) == NULL) {
202 error = ESRCH;
203 } else {
204 cid = getparam(P_LWPID, ulwp->ul_lwpid, policy, param);
205 if (cid == -1) {
206 error = errno;
207 } else if (*policy == ulwp->ul_policy && cid == ulwp->ul_cid &&
208 (*policy == SCHED_FIFO || *policy == SCHED_RR)) {
209 /*
210 * Return the defined priority, not the effective
211 * priority from priority ceiling mutexes.
212 */
213 param->sched_priority = ulwp->ul_pri;
214 } else {
215 if (*policy == SCHED_FIFO || *policy == SCHED_RR)
216 ulwp->ul_rtclassid = cid;
217 ulwp->ul_cid = cid;
218 ulwp->ul_pri = param->sched_priority;
219 membar_producer();
220 ulwp->ul_policy = *policy;
221 }
222 ulwp_unlock(ulwp, curthread->ul_uberdata);
223 }
224
225 return (error);
226 }
227
228 #pragma weak _thr_getprio = thr_getprio
229 int
thr_getprio(thread_t tid,int * priority)230 thr_getprio(thread_t tid, int *priority)
231 {
232 struct sched_param param;
233 int policy;
234 int error;
235
236 if ((error = pthread_getschedparam(tid, &policy, ¶m)) == 0)
237 *priority = param.sched_priority;
238 return (error);
239 }
240
241 /*
242 * pthread_setschedparam: sets the sched parameters for a thread.
243 */
244 int
pthread_setschedparam(pthread_t tid,int policy,const struct sched_param * param)245 pthread_setschedparam(pthread_t tid,
246 int policy, const struct sched_param *param)
247 {
248 return (_thr_setparam(tid, policy, param->sched_priority));
249 }
250
251 #pragma weak pthread_setschedprio = thr_setprio
252 int
thr_setprio(thread_t tid,int prio)253 thr_setprio(thread_t tid, int prio)
254 {
255 struct sched_param param;
256 int policy;
257 int error;
258
259 /*
260 * pthread_getschedparam() has the side-effect of setting
261 * the target thread's ul_policy, ul_pri and ul_cid correctly.
262 */
263 if ((error = pthread_getschedparam(tid, &policy, ¶m)) != 0)
264 return (error);
265 if (param.sched_priority == prio) /* no change */
266 return (0);
267 return (_thr_setparam(tid, policy, prio));
268 }
269