/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Copyright 2018, Joyent, Inc. */ #include "lint.h" #include "thr_uberdata.h" #include #include #include /* * Default attribute object for pthread_create() with NULL attr pointer. * Note that the 'guardsize' field is initialized on the first call. */ const thrattr_t * def_thrattr(void) { static thrattr_t thrattr = { 0, /* stksize */ NULL, /* stkaddr */ PTHREAD_CREATE_JOINABLE, /* detachstate */ PTHREAD_CREATE_NONDAEMON_NP, /* daemonstate */ PTHREAD_SCOPE_PROCESS, /* scope */ 0, /* prio */ SCHED_OTHER, /* policy */ PTHREAD_INHERIT_SCHED, /* inherit */ 0, /* guardsize */ { 0 } /* name */ }; if (thrattr.guardsize == 0) thrattr.guardsize = _sysconf(_SC_PAGESIZE); return (&thrattr); } /* * pthread_attr_init: allocates the attribute object and initializes it * with the default values. */ #pragma weak _pthread_attr_init = pthread_attr_init int pthread_attr_init(pthread_attr_t *attr) { thrattr_t *ap; if ((ap = lmalloc(sizeof (thrattr_t))) != NULL) { *ap = *def_thrattr(); attr->__pthread_attrp = ap; return (0); } return (ENOMEM); } /* * pthread_attr_destroy: frees the attribute object and invalidates it * with NULL value. */ int pthread_attr_destroy(pthread_attr_t *attr) { if (attr == NULL || attr->__pthread_attrp == NULL) return (EINVAL); lfree(attr->__pthread_attrp, sizeof (thrattr_t)); attr->__pthread_attrp = NULL; return (0); } /* * pthread_attr_clone: make a copy of a pthread_attr_t. */ int pthread_attr_clone(pthread_attr_t *attr, const pthread_attr_t *old_attr) { thrattr_t *ap; const thrattr_t *old_ap = old_attr ? old_attr->__pthread_attrp : def_thrattr(); if (old_ap == NULL) return (EINVAL); if ((ap = lmalloc(sizeof (thrattr_t))) == NULL) return (ENOMEM); *ap = *old_ap; attr->__pthread_attrp = ap; return (0); } /* * pthread_attr_equal: compare two pthread_attr_t's, return 1 if equal. * A NULL pthread_attr_t pointer implies default attributes. * This is a consolidation-private interface, for librt. */ int pthread_attr_equal(const pthread_attr_t *attr1, const pthread_attr_t *attr2) { const thrattr_t *ap1 = attr1 ? attr1->__pthread_attrp : def_thrattr(); const thrattr_t *ap2 = attr2 ? attr2->__pthread_attrp : def_thrattr(); if (ap1 == NULL || ap2 == NULL) return (0); return (ap1 == ap2 || memcmp(ap1, ap2, sizeof (thrattr_t)) == 0); } /* * pthread_attr_setstacksize: sets the user stack size, minimum should * be PTHREAD_STACK_MIN (MINSTACK). * This is equivalent to stksize argument in thr_create(). */ int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize) { thrattr_t *ap; if (attr != NULL && (ap = attr->__pthread_attrp) != NULL && stacksize >= MINSTACK) { ap->stksize = stacksize; return (0); } return (EINVAL); } /* * pthread_attr_getstacksize: gets the user stack size. */ #pragma weak _pthread_attr_getstacksize = pthread_attr_getstacksize int pthread_attr_getstacksize(const pthread_attr_t *attr, size_t *stacksize) { thrattr_t *ap; if (attr != NULL && (ap = attr->__pthread_attrp) != NULL && stacksize != NULL) { *stacksize = ap->stksize; return (0); } return (EINVAL); } /* * pthread_attr_setstackaddr: sets the user stack addr. * This is equivalent to stkaddr argument in thr_create(). */ int pthread_attr_setstackaddr(pthread_attr_t *attr, void *stackaddr) { thrattr_t *ap; if (attr != NULL && (ap = attr->__pthread_attrp) != NULL) { ap->stkaddr = stackaddr; return (0); } return (EINVAL); } /* * pthread_attr_getstackaddr: gets the user stack addr. */ #pragma weak _pthread_attr_getstackaddr = pthread_attr_getstackaddr int pthread_attr_getstackaddr(const pthread_attr_t *attr, void **stackaddr) { thrattr_t *ap; if (attr != NULL && (ap = attr->__pthread_attrp) != NULL && stackaddr != NULL) { *stackaddr = ap->stkaddr; return (0); } return (EINVAL); } /* * pthread_attr_setdetachstate: sets the detach state to DETACHED or JOINABLE. * PTHREAD_CREATE_DETACHED is equivalent to thr_create(THR_DETACHED). */ int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate) { thrattr_t *ap; if (attr != NULL && (ap = attr->__pthread_attrp) != NULL && (detachstate == PTHREAD_CREATE_DETACHED || detachstate == PTHREAD_CREATE_JOINABLE)) { ap->detachstate = detachstate; return (0); } return (EINVAL); } /* * pthread_attr_getdetachstate: gets the detach state. */ #pragma weak _pthread_attr_getdetachstate = pthread_attr_getdetachstate int pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate) { thrattr_t *ap; if (attr != NULL && (ap = attr->__pthread_attrp) != NULL && detachstate != NULL) { *detachstate = ap->detachstate; return (0); } return (EINVAL); } /* * pthread_attr_setdaemonstate_np: sets the daemon state to DAEMON or NONDAEMON. * PTHREAD_CREATE_DAEMON is equivalent to thr_create(THR_DAEMON). * For now, this is a private interface in libc. */ int pthread_attr_setdaemonstate_np(pthread_attr_t *attr, int daemonstate) { thrattr_t *ap; if (attr != NULL && (ap = attr->__pthread_attrp) != NULL && (daemonstate == PTHREAD_CREATE_DAEMON_NP || daemonstate == PTHREAD_CREATE_NONDAEMON_NP)) { ap->daemonstate = daemonstate; return (0); } return (EINVAL); } /* * pthread_attr_getdaemonstate_np: gets the daemon state. * For now, this is a private interface in libc, but it is exposed in the * mapfile for the purposes of testing only. */ int pthread_attr_getdaemonstate_np(const pthread_attr_t *attr, int *daemonstate) { thrattr_t *ap; if (attr != NULL && (ap = attr->__pthread_attrp) != NULL && daemonstate != NULL) { *daemonstate = ap->daemonstate; return (0); } return (EINVAL); } /* * pthread_attr_setscope: sets the scope to SYSTEM or PROCESS. * This is equivalent to setting THR_BOUND flag in thr_create(). */ int pthread_attr_setscope(pthread_attr_t *attr, int scope) { thrattr_t *ap; if (attr != NULL && (ap = attr->__pthread_attrp) != NULL && (scope == PTHREAD_SCOPE_SYSTEM || scope == PTHREAD_SCOPE_PROCESS)) { ap->scope = scope; return (0); } return (EINVAL); } /* * pthread_attr_getscope: gets the scheduling scope. */ #pragma weak _pthread_attr_getscope = pthread_attr_getscope int pthread_attr_getscope(const pthread_attr_t *attr, int *scope) { thrattr_t *ap; if (attr != NULL && (ap = attr->__pthread_attrp) != NULL && scope != NULL) { *scope = ap->scope; return (0); } return (EINVAL); } /* * pthread_attr_setinheritsched: sets the scheduling parameters to be * EXPLICIT or INHERITED from parent thread. */ int pthread_attr_setinheritsched(pthread_attr_t *attr, int inherit) { thrattr_t *ap; if (attr != NULL && (ap = attr->__pthread_attrp) != NULL && (inherit == PTHREAD_EXPLICIT_SCHED || inherit == PTHREAD_INHERIT_SCHED)) { ap->inherit = inherit; return (0); } return (EINVAL); } /* * pthread_attr_getinheritsched: gets the scheduling inheritance. */ #pragma weak _pthread_attr_getinheritsched = pthread_attr_getinheritsched int pthread_attr_getinheritsched(const pthread_attr_t *attr, int *inherit) { thrattr_t *ap; if (attr != NULL && (ap = attr->__pthread_attrp) != NULL && inherit != NULL) { *inherit = ap->inherit; return (0); } return (EINVAL); } /* * pthread_attr_setschedpolicy: sets the scheduling policy. */ int pthread_attr_setschedpolicy(pthread_attr_t *attr, int policy) { thrattr_t *ap; if (attr != NULL && (ap = attr->__pthread_attrp) != NULL && policy != SCHED_SYS && get_info_by_policy(policy) != NULL) { ap->policy = policy; return (0); } return (EINVAL); } /* * pthread_attr_getpolicy: gets the scheduling policy. */ #pragma weak _pthread_attr_getschedpolicy = pthread_attr_getschedpolicy int pthread_attr_getschedpolicy(const pthread_attr_t *attr, int *policy) { thrattr_t *ap; if (attr != NULL && (ap = attr->__pthread_attrp) != NULL && policy != NULL) { *policy = ap->policy; return (0); } return (EINVAL); } /* * pthread_attr_setschedparam: sets the scheduling parameters. * Currently, we support priority only. */ int pthread_attr_setschedparam(pthread_attr_t *attr, const struct sched_param *param) { thrattr_t *ap; if (attr != NULL && (ap = attr->__pthread_attrp) != NULL && param != NULL) { ap->prio = param->sched_priority; return (0); } return (EINVAL); } /* * pthread_attr_getschedparam: gets the scheduling parameters. * Currently, only priority is defined as sched parameter. */ #pragma weak _pthread_attr_getschedparam = pthread_attr_getschedparam int pthread_attr_getschedparam(const pthread_attr_t *attr, struct sched_param *param) { thrattr_t *ap; if (attr != NULL && (ap = attr->__pthread_attrp) != NULL && param != NULL) { param->sched_priority = ap->prio; return (0); } return (EINVAL); } /* * UNIX98 * pthread_attr_setguardsize: sets the guardsize */ int pthread_attr_setguardsize(pthread_attr_t *attr, size_t guardsize) { thrattr_t *ap; if (attr != NULL && (ap = attr->__pthread_attrp) != NULL) { ap->guardsize = guardsize; return (0); } return (EINVAL); } /* * UNIX98 * pthread_attr_getguardsize: gets the guardsize */ int pthread_attr_getguardsize(const pthread_attr_t *attr, size_t *guardsize) { thrattr_t *ap; if (attr != NULL && (ap = attr->__pthread_attrp) != NULL && guardsize != NULL) { *guardsize = ap->guardsize; return (0); } return (EINVAL); } /* * pthread_attr_setstack: sets the user stack addr and stack size. * This is equivalent to the stack_base and stack_size arguments * to thr_create(). */ int pthread_attr_setstack(pthread_attr_t *attr, void *stackaddr, size_t stacksize) { thrattr_t *ap; if (attr != NULL && (ap = attr->__pthread_attrp) != NULL && stacksize >= MINSTACK) { ap->stkaddr = stackaddr; ap->stksize = stacksize; if (stackaddr != NULL && setup_top_frame(stackaddr, stacksize, NULL) == NULL) return (EACCES); return (0); } return (EINVAL); } /* * pthread_attr_getstack: gets the user stack addr and stack size. */ int pthread_attr_getstack(const pthread_attr_t *attr, void **stackaddr, size_t *stacksize) { thrattr_t *ap; if (attr != NULL && (ap = attr->__pthread_attrp) != NULL && stackaddr != NULL && stacksize != NULL) { *stackaddr = ap->stkaddr; *stacksize = ap->stksize; return (0); } return (EINVAL); } int pthread_attr_setname_np(pthread_attr_t *attr, const char *name) { thrattr_t *ap; if (attr == NULL || (ap = attr->__pthread_attrp) == NULL) return (EINVAL); if (name == NULL) { bzero(ap->name, sizeof (ap->name)); return (0); } if (strlen(name) >= sizeof (ap->name)) return (ERANGE); /* * We really want the ASCII version of isprint() here... */ for (size_t i = 0; name[i] != '\0'; i++) { if (!ISPRINT(name[i])) return (EINVAL); } /* * not having garbage after the end of the string simplifies attr * comparison */ bzero(ap->name, sizeof (ap->name)); (void) strlcpy(ap->name, name, sizeof (ap->name)); return (0); } int pthread_attr_getname_np(pthread_attr_t *attr, char *buf, size_t len) { thrattr_t *ap; if (buf == NULL || attr == NULL || (ap = attr->__pthread_attrp) == NULL) return (EINVAL); if (strlcpy(buf, ap->name, len) > len) return (ERANGE); return (0); } /* * This function is a common BSD extension to pthread which is used to obtain * the attributes of a thread that might have changed after its creation, for * example, its stack address. * * Note, there is no setattr analogue, nor do we desire to add one at this time. * Similarly there is no native threads API analogue (nor should we add one for * C11). * * The astute reader may note that there is a GNU version of this called * pthread_getattr_np(). The two functions are similar, but subtly different in * a rather important way. While pthread_attr_get_np() expects to be given * a pthread_attr_t that has had pthread_attr_init() called on it, * pthread_getattr_np() does not. However, on GNU systems, where the function * originates, the pthread_attr_t is not opaque and thus it is entirely safe to * both call pthread_attr_init() and then call pthread_getattr_np() on the same * attributes object. On illumos, since the pthread_attr_t is opaque, that would * be a memory leak. As such, we don't provide it. */ int pthread_attr_get_np(pthread_t tid, pthread_attr_t *attr) { int ret; ulwp_t *self = curthread; uberdata_t *udp = self->ul_uberdata; ulwp_t *target = NULL; thrattr_t *ap; /* * To ensure that information about the target thread does not change or * disappear while we're trying to interrogate it, we grab the ulwp * lock. */ if (self->ul_lwpid == tid) { ulwp_lock(self, udp); target = self; } else { target = find_lwp(tid); if (target == NULL) return (ESRCH); } if (attr == NULL) { ret = EINVAL; goto out; } if ((ap = attr->__pthread_attrp) == NULL) { ret = EINVAL; goto out; } ap->stksize = target->ul_stksiz; ap->stkaddr = target->ul_stk; if (target->ul_usropts & THR_DETACHED) { ap->detachstate = PTHREAD_CREATE_DETACHED; } else { ap->detachstate = PTHREAD_CREATE_JOINABLE; } if (target->ul_usropts & THR_DAEMON) { ap->daemonstate = PTHREAD_CREATE_DAEMON_NP; } else { ap->daemonstate = PTHREAD_CREATE_NONDAEMON_NP; } if (target->ul_usropts & THR_BOUND) { ap->scope = PTHREAD_SCOPE_SYSTEM; } else { ap->scope = PTHREAD_SCOPE_PROCESS; } ap->prio = target->ul_pri; ap->policy = target->ul_policy; ap->inherit = target->ul_ptinherit; ap->guardsize = target->ul_guardsize; (void) pthread_getname_np(tid, ap->name, sizeof (ap->name)); ret = 0; out: ulwp_unlock(target, udp); return (ret); }