xref: /illumos-gate/usr/src/lib/libc/port/threads/pthr_attr.c (revision 25befe07d3c1488cbbdecdb765cd0558e12cc364)
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 /*
28  * Copyright 2018, Joyent, Inc.
29  */
30 
31 #include "lint.h"
32 #include "thr_uberdata.h"
33 #include <sys/ctype.h>
34 #include <strings.h>
35 #include <sched.h>
36 
37 /*
38  * Default attribute object for pthread_create() with NULL attr pointer.
39  * Note that the 'guardsize' field is initialized on the first call.
40  */
41 const thrattr_t *
def_thrattr(void)42 def_thrattr(void)
43 {
44 	static thrattr_t thrattr = {
45 		0,				/* stksize */
46 		NULL,				/* stkaddr */
47 		PTHREAD_CREATE_JOINABLE,	/* detachstate */
48 		PTHREAD_CREATE_NONDAEMON_NP,	/* daemonstate */
49 		PTHREAD_SCOPE_PROCESS,		/* scope */
50 		0,				/* prio */
51 		SCHED_OTHER,			/* policy */
52 		PTHREAD_INHERIT_SCHED,		/* inherit */
53 		0,				/* guardsize */
54 		{ 0 }				/* name */
55 	};
56 	if (thrattr.guardsize == 0)
57 		thrattr.guardsize = _sysconf(_SC_PAGESIZE);
58 	return (&thrattr);
59 }
60 
61 /*
62  * pthread_attr_init: allocates the attribute object and initializes it
63  * with the default values.
64  */
65 #pragma weak _pthread_attr_init = pthread_attr_init
66 int
pthread_attr_init(pthread_attr_t * attr)67 pthread_attr_init(pthread_attr_t *attr)
68 {
69 	thrattr_t *ap;
70 
71 	if ((ap = lmalloc(sizeof (thrattr_t))) != NULL) {
72 		*ap = *def_thrattr();
73 		attr->__pthread_attrp = ap;
74 		return (0);
75 	}
76 	return (ENOMEM);
77 }
78 
79 /*
80  * pthread_attr_destroy: frees the attribute object and invalidates it
81  * with NULL value.
82  */
83 int
pthread_attr_destroy(pthread_attr_t * attr)84 pthread_attr_destroy(pthread_attr_t *attr)
85 {
86 	if (attr == NULL || attr->__pthread_attrp == NULL)
87 		return (EINVAL);
88 	lfree(attr->__pthread_attrp, sizeof (thrattr_t));
89 	attr->__pthread_attrp = NULL;
90 	return (0);
91 }
92 
93 /*
94  * pthread_attr_clone: make a copy of a pthread_attr_t.
95  */
96 int
pthread_attr_clone(pthread_attr_t * attr,const pthread_attr_t * old_attr)97 pthread_attr_clone(pthread_attr_t *attr, const pthread_attr_t *old_attr)
98 {
99 	thrattr_t *ap;
100 	const thrattr_t *old_ap =
101 	    old_attr ? old_attr->__pthread_attrp : def_thrattr();
102 
103 	if (old_ap == NULL)
104 		return (EINVAL);
105 	if ((ap = lmalloc(sizeof (thrattr_t))) == NULL)
106 		return (ENOMEM);
107 	*ap = *old_ap;
108 	attr->__pthread_attrp = ap;
109 	return (0);
110 }
111 
112 /*
113  * pthread_attr_equal: compare two pthread_attr_t's, return 1 if equal.
114  * A NULL pthread_attr_t pointer implies default attributes.
115  * This is a consolidation-private interface, for librt.
116  */
117 int
pthread_attr_equal(const pthread_attr_t * attr1,const pthread_attr_t * attr2)118 pthread_attr_equal(const pthread_attr_t *attr1, const pthread_attr_t *attr2)
119 {
120 	const thrattr_t *ap1 = attr1 ? attr1->__pthread_attrp : def_thrattr();
121 	const thrattr_t *ap2 = attr2 ? attr2->__pthread_attrp : def_thrattr();
122 
123 	if (ap1 == NULL || ap2 == NULL)
124 		return (0);
125 	return (ap1 == ap2 || memcmp(ap1, ap2, sizeof (thrattr_t)) == 0);
126 }
127 
128 /*
129  * pthread_attr_setstacksize: sets the user stack size, minimum should
130  * be PTHREAD_STACK_MIN (MINSTACK).
131  * This is equivalent to stksize argument in thr_create().
132  */
133 int
pthread_attr_setstacksize(pthread_attr_t * attr,size_t stacksize)134 pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize)
135 {
136 	thrattr_t *ap;
137 
138 	if (attr != NULL && (ap = attr->__pthread_attrp) != NULL &&
139 	    stacksize >= MINSTACK) {
140 		ap->stksize = stacksize;
141 		return (0);
142 	}
143 	return (EINVAL);
144 }
145 
146 /*
147  * pthread_attr_getstacksize: gets the user stack size.
148  */
149 #pragma weak _pthread_attr_getstacksize = pthread_attr_getstacksize
150 int
pthread_attr_getstacksize(const pthread_attr_t * attr,size_t * stacksize)151 pthread_attr_getstacksize(const pthread_attr_t *attr, size_t *stacksize)
152 {
153 	thrattr_t *ap;
154 
155 	if (attr != NULL && (ap = attr->__pthread_attrp) != NULL &&
156 	    stacksize != NULL) {
157 		*stacksize = ap->stksize;
158 		return (0);
159 	}
160 	return (EINVAL);
161 }
162 
163 /*
164  * pthread_attr_setstackaddr: sets the user stack addr.
165  * This is equivalent to stkaddr argument in thr_create().
166  */
167 int
pthread_attr_setstackaddr(pthread_attr_t * attr,void * stackaddr)168 pthread_attr_setstackaddr(pthread_attr_t *attr, void *stackaddr)
169 {
170 	thrattr_t *ap;
171 
172 	if (attr != NULL && (ap = attr->__pthread_attrp) != NULL) {
173 		ap->stkaddr = stackaddr;
174 		return (0);
175 	}
176 	return (EINVAL);
177 }
178 
179 /*
180  * pthread_attr_getstackaddr: gets the user stack addr.
181  */
182 #pragma weak _pthread_attr_getstackaddr = pthread_attr_getstackaddr
183 int
pthread_attr_getstackaddr(const pthread_attr_t * attr,void ** stackaddr)184 pthread_attr_getstackaddr(const pthread_attr_t *attr, void **stackaddr)
185 {
186 	thrattr_t *ap;
187 
188 	if (attr != NULL && (ap = attr->__pthread_attrp) != NULL &&
189 	    stackaddr != NULL) {
190 		*stackaddr = ap->stkaddr;
191 		return (0);
192 	}
193 	return (EINVAL);
194 }
195 
196 /*
197  * pthread_attr_setdetachstate: sets the detach state to DETACHED or JOINABLE.
198  * PTHREAD_CREATE_DETACHED is equivalent to thr_create(THR_DETACHED).
199  */
200 int
pthread_attr_setdetachstate(pthread_attr_t * attr,int detachstate)201 pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate)
202 {
203 	thrattr_t *ap;
204 
205 	if (attr != NULL && (ap = attr->__pthread_attrp) != NULL &&
206 	    (detachstate == PTHREAD_CREATE_DETACHED ||
207 	    detachstate == PTHREAD_CREATE_JOINABLE)) {
208 		ap->detachstate = detachstate;
209 		return (0);
210 	}
211 	return (EINVAL);
212 }
213 
214 /*
215  * pthread_attr_getdetachstate: gets the detach state.
216  */
217 #pragma weak _pthread_attr_getdetachstate = pthread_attr_getdetachstate
218 int
pthread_attr_getdetachstate(const pthread_attr_t * attr,int * detachstate)219 pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate)
220 {
221 	thrattr_t *ap;
222 
223 	if (attr != NULL && (ap = attr->__pthread_attrp) != NULL &&
224 	    detachstate != NULL) {
225 		*detachstate = ap->detachstate;
226 		return (0);
227 	}
228 	return (EINVAL);
229 }
230 
231 /*
232  * pthread_attr_setdaemonstate_np: sets the daemon state to DAEMON or NONDAEMON.
233  * PTHREAD_CREATE_DAEMON is equivalent to thr_create(THR_DAEMON).
234  * For now, this is a private interface in libc.
235  */
236 int
pthread_attr_setdaemonstate_np(pthread_attr_t * attr,int daemonstate)237 pthread_attr_setdaemonstate_np(pthread_attr_t *attr, int daemonstate)
238 {
239 	thrattr_t *ap;
240 
241 	if (attr != NULL && (ap = attr->__pthread_attrp) != NULL &&
242 	    (daemonstate == PTHREAD_CREATE_DAEMON_NP ||
243 	    daemonstate == PTHREAD_CREATE_NONDAEMON_NP)) {
244 		ap->daemonstate = daemonstate;
245 		return (0);
246 	}
247 	return (EINVAL);
248 }
249 
250 /*
251  * pthread_attr_getdaemonstate_np: gets the daemon state.
252  * For now, this is a private interface in libc, but it is exposed in the
253  * mapfile for the purposes of testing only.
254  */
255 int
pthread_attr_getdaemonstate_np(const pthread_attr_t * attr,int * daemonstate)256 pthread_attr_getdaemonstate_np(const pthread_attr_t *attr, int *daemonstate)
257 {
258 	thrattr_t *ap;
259 
260 	if (attr != NULL && (ap = attr->__pthread_attrp) != NULL &&
261 	    daemonstate != NULL) {
262 		*daemonstate = ap->daemonstate;
263 		return (0);
264 	}
265 	return (EINVAL);
266 }
267 
268 /*
269  * pthread_attr_setscope: sets the scope to SYSTEM or PROCESS.
270  * This is equivalent to setting THR_BOUND flag in thr_create().
271  */
272 int
pthread_attr_setscope(pthread_attr_t * attr,int scope)273 pthread_attr_setscope(pthread_attr_t *attr, int scope)
274 {
275 	thrattr_t *ap;
276 
277 	if (attr != NULL && (ap = attr->__pthread_attrp) != NULL &&
278 	    (scope == PTHREAD_SCOPE_SYSTEM ||
279 	    scope == PTHREAD_SCOPE_PROCESS)) {
280 		ap->scope = scope;
281 		return (0);
282 	}
283 	return (EINVAL);
284 }
285 
286 /*
287  * pthread_attr_getscope: gets the scheduling scope.
288  */
289 #pragma weak _pthread_attr_getscope = pthread_attr_getscope
290 int
pthread_attr_getscope(const pthread_attr_t * attr,int * scope)291 pthread_attr_getscope(const pthread_attr_t *attr, int *scope)
292 {
293 	thrattr_t *ap;
294 
295 	if (attr != NULL && (ap = attr->__pthread_attrp) != NULL &&
296 	    scope != NULL) {
297 		*scope = ap->scope;
298 		return (0);
299 	}
300 	return (EINVAL);
301 }
302 
303 /*
304  * pthread_attr_setinheritsched: sets the scheduling parameters to be
305  * EXPLICIT or INHERITED from parent thread.
306  */
307 int
pthread_attr_setinheritsched(pthread_attr_t * attr,int inherit)308 pthread_attr_setinheritsched(pthread_attr_t *attr, int inherit)
309 {
310 	thrattr_t *ap;
311 
312 	if (attr != NULL && (ap = attr->__pthread_attrp) != NULL &&
313 	    (inherit == PTHREAD_EXPLICIT_SCHED ||
314 	    inherit == PTHREAD_INHERIT_SCHED)) {
315 		ap->inherit = inherit;
316 		return (0);
317 	}
318 	return (EINVAL);
319 }
320 
321 /*
322  * pthread_attr_getinheritsched: gets the scheduling inheritance.
323  */
324 #pragma weak _pthread_attr_getinheritsched = pthread_attr_getinheritsched
325 int
pthread_attr_getinheritsched(const pthread_attr_t * attr,int * inherit)326 pthread_attr_getinheritsched(const pthread_attr_t *attr, int *inherit)
327 {
328 	thrattr_t *ap;
329 
330 	if (attr != NULL && (ap = attr->__pthread_attrp) != NULL &&
331 	    inherit != NULL) {
332 		*inherit = ap->inherit;
333 		return (0);
334 	}
335 	return (EINVAL);
336 }
337 
338 /*
339  * pthread_attr_setschedpolicy: sets the scheduling policy.
340  */
341 int
pthread_attr_setschedpolicy(pthread_attr_t * attr,int policy)342 pthread_attr_setschedpolicy(pthread_attr_t *attr, int policy)
343 {
344 	thrattr_t *ap;
345 
346 	if (attr != NULL && (ap = attr->__pthread_attrp) != NULL &&
347 	    policy != SCHED_SYS && get_info_by_policy(policy) != NULL) {
348 		ap->policy = policy;
349 		return (0);
350 	}
351 	return (EINVAL);
352 }
353 
354 /*
355  * pthread_attr_getpolicy: gets the scheduling policy.
356  */
357 #pragma weak _pthread_attr_getschedpolicy = pthread_attr_getschedpolicy
358 int
pthread_attr_getschedpolicy(const pthread_attr_t * attr,int * policy)359 pthread_attr_getschedpolicy(const pthread_attr_t *attr, int *policy)
360 {
361 	thrattr_t *ap;
362 
363 	if (attr != NULL && (ap = attr->__pthread_attrp) != NULL &&
364 	    policy != NULL) {
365 		*policy = ap->policy;
366 		return (0);
367 	}
368 	return (EINVAL);
369 }
370 
371 /*
372  * pthread_attr_setschedparam: sets the scheduling parameters.
373  * Currently, we support priority only.
374  */
375 int
pthread_attr_setschedparam(pthread_attr_t * attr,const struct sched_param * param)376 pthread_attr_setschedparam(pthread_attr_t *attr,
377     const struct sched_param *param)
378 {
379 	thrattr_t *ap;
380 
381 	if (attr != NULL && (ap = attr->__pthread_attrp) != NULL &&
382 	    param != NULL) {
383 		ap->prio = param->sched_priority;
384 		return (0);
385 	}
386 	return (EINVAL);
387 }
388 
389 /*
390  * pthread_attr_getschedparam: gets the scheduling parameters.
391  * Currently, only priority is defined as sched parameter.
392  */
393 #pragma weak _pthread_attr_getschedparam = pthread_attr_getschedparam
394 int
pthread_attr_getschedparam(const pthread_attr_t * attr,struct sched_param * param)395 pthread_attr_getschedparam(const pthread_attr_t *attr,
396     struct sched_param *param)
397 {
398 	thrattr_t *ap;
399 
400 	if (attr != NULL && (ap = attr->__pthread_attrp) != NULL &&
401 	    param != NULL) {
402 		param->sched_priority = ap->prio;
403 		return (0);
404 	}
405 	return (EINVAL);
406 }
407 
408 /*
409  * UNIX98
410  * pthread_attr_setguardsize: sets the guardsize
411  */
412 int
pthread_attr_setguardsize(pthread_attr_t * attr,size_t guardsize)413 pthread_attr_setguardsize(pthread_attr_t *attr, size_t guardsize)
414 {
415 	thrattr_t *ap;
416 
417 	if (attr != NULL && (ap = attr->__pthread_attrp) != NULL) {
418 		ap->guardsize = guardsize;
419 		return (0);
420 	}
421 	return (EINVAL);
422 }
423 
424 /*
425  * UNIX98
426  * pthread_attr_getguardsize: gets the guardsize
427  */
428 int
pthread_attr_getguardsize(const pthread_attr_t * attr,size_t * guardsize)429 pthread_attr_getguardsize(const pthread_attr_t *attr, size_t *guardsize)
430 {
431 	thrattr_t *ap;
432 
433 	if (attr != NULL && (ap = attr->__pthread_attrp) != NULL &&
434 	    guardsize != NULL) {
435 		*guardsize = ap->guardsize;
436 		return (0);
437 	}
438 	return (EINVAL);
439 }
440 
441 /*
442  * pthread_attr_setstack: sets the user stack addr and stack size.
443  * This is equivalent to the stack_base and stack_size arguments
444  * to thr_create().
445  */
446 int
pthread_attr_setstack(pthread_attr_t * attr,void * stackaddr,size_t stacksize)447 pthread_attr_setstack(pthread_attr_t *attr,
448     void *stackaddr, size_t stacksize)
449 {
450 	thrattr_t *ap;
451 
452 	if (attr != NULL && (ap = attr->__pthread_attrp) != NULL &&
453 	    stacksize >= MINSTACK) {
454 		ap->stkaddr = stackaddr;
455 		ap->stksize = stacksize;
456 		if (stackaddr != NULL &&
457 		    setup_top_frame(stackaddr, stacksize, NULL) == NULL)
458 			return (EACCES);
459 		return (0);
460 	}
461 	return (EINVAL);
462 }
463 
464 /*
465  * pthread_attr_getstack: gets the user stack addr and stack size.
466  */
467 int
pthread_attr_getstack(const pthread_attr_t * attr,void ** stackaddr,size_t * stacksize)468 pthread_attr_getstack(const pthread_attr_t *attr,
469     void **stackaddr, size_t *stacksize)
470 {
471 	thrattr_t *ap;
472 
473 	if (attr != NULL && (ap = attr->__pthread_attrp) != NULL &&
474 	    stackaddr != NULL && stacksize != NULL) {
475 		*stackaddr = ap->stkaddr;
476 		*stacksize = ap->stksize;
477 		return (0);
478 	}
479 	return (EINVAL);
480 }
481 
482 int
pthread_attr_setname_np(pthread_attr_t * attr,const char * name)483 pthread_attr_setname_np(pthread_attr_t *attr, const char *name)
484 {
485 	thrattr_t *ap;
486 
487 	if (attr == NULL || (ap = attr->__pthread_attrp) == NULL)
488 		return (EINVAL);
489 
490 	if (name == NULL) {
491 		bzero(ap->name, sizeof (ap->name));
492 		return (0);
493 	}
494 
495 	if (strlen(name) >= sizeof (ap->name))
496 		return (ERANGE);
497 
498 	/*
499 	 * We really want the ASCII version of isprint() here...
500 	 */
501 	for (size_t i = 0; name[i] != '\0'; i++) {
502 		if (!ISPRINT(name[i]))
503 			return (EINVAL);
504 	}
505 
506 	/*
507 	 * not having garbage after the end of the string simplifies attr
508 	 * comparison
509 	 */
510 	bzero(ap->name, sizeof (ap->name));
511 	(void) strlcpy(ap->name, name, sizeof (ap->name));
512 	return (0);
513 }
514 
515 int
pthread_attr_getname_np(pthread_attr_t * attr,char * buf,size_t len)516 pthread_attr_getname_np(pthread_attr_t *attr, char *buf, size_t len)
517 {
518 	thrattr_t *ap;
519 
520 	if (buf == NULL || attr == NULL ||
521 	    (ap = attr->__pthread_attrp) == NULL)
522 		return (EINVAL);
523 
524 	if (strlcpy(buf, ap->name, len) > len)
525 		return (ERANGE);
526 	return (0);
527 }
528 
529 /*
530  * This function is a common BSD extension to pthread which is used to obtain
531  * the attributes of a thread that might have changed after its creation, for
532  * example, its stack address.
533  *
534  * Note, there is no setattr analogue, nor do we desire to add one at this time.
535  * Similarly there is no native threads API analogue (nor should we add one for
536  * C11).
537  *
538  * The astute reader may note that there is a GNU version of this called
539  * pthread_getattr_np(). The two functions are similar, but subtly different in
540  * a rather important way. While pthread_attr_get_np() expects to be given
541  * a pthread_attr_t that has had pthread_attr_init() called on it,
542  * pthread_getattr_np() does not. However, on GNU systems, where the function
543  * originates, the pthread_attr_t is not opaque and thus it is entirely safe to
544  * both call pthread_attr_init() and then call pthread_getattr_np() on the same
545  * attributes object. On illumos, since the pthread_attr_t is opaque, that would
546  * be a memory leak. As such, we don't provide it.
547  */
548 int
pthread_attr_get_np(pthread_t tid,pthread_attr_t * attr)549 pthread_attr_get_np(pthread_t tid, pthread_attr_t *attr)
550 {
551 	int ret;
552 	ulwp_t *self = curthread;
553 	uberdata_t *udp = self->ul_uberdata;
554 	ulwp_t *target = NULL;
555 	thrattr_t *ap;
556 
557 	/*
558 	 * To ensure that information about the target thread does not change or
559 	 * disappear while we're trying to interrogate it, we grab the ulwp
560 	 * lock.
561 	 */
562 	if (self->ul_lwpid == tid) {
563 		ulwp_lock(self, udp);
564 		target = self;
565 	} else {
566 		target = find_lwp(tid);
567 		if (target == NULL)
568 			return (ESRCH);
569 	}
570 
571 	if (attr == NULL) {
572 		ret = EINVAL;
573 		goto out;
574 	}
575 
576 	if ((ap = attr->__pthread_attrp) == NULL) {
577 		ret = EINVAL;
578 		goto out;
579 	}
580 
581 	ap->stksize = target->ul_stksiz;
582 	ap->stkaddr = target->ul_stk;
583 	if (target->ul_usropts & THR_DETACHED) {
584 		ap->detachstate = PTHREAD_CREATE_DETACHED;
585 	} else {
586 		ap->detachstate = PTHREAD_CREATE_JOINABLE;
587 	}
588 
589 	if (target->ul_usropts & THR_DAEMON) {
590 		ap->daemonstate = PTHREAD_CREATE_DAEMON_NP;
591 	} else {
592 		ap->daemonstate = PTHREAD_CREATE_NONDAEMON_NP;
593 	}
594 
595 	if (target->ul_usropts & THR_BOUND) {
596 		ap->scope = PTHREAD_SCOPE_SYSTEM;
597 	} else {
598 		ap->scope = PTHREAD_SCOPE_PROCESS;
599 	}
600 	ap->prio = target->ul_pri;
601 	ap->policy = target->ul_policy;
602 	ap->inherit = target->ul_ptinherit;
603 	ap->guardsize = target->ul_guardsize;
604 	(void) pthread_getname_np(tid, ap->name, sizeof (ap->name));
605 
606 	ret = 0;
607 out:
608 	ulwp_unlock(target, udp);
609 	return (ret);
610 }
611