summaryrefslogtreecommitdiff
path: root/usr/src/lib/libc/port/threads/pthread.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/lib/libc/port/threads/pthread.c')
-rw-r--r--usr/src/lib/libc/port/threads/pthread.c316
1 files changed, 316 insertions, 0 deletions
diff --git a/usr/src/lib/libc/port/threads/pthread.c b/usr/src/lib/libc/port/threads/pthread.c
new file mode 100644
index 0000000000..3be02fbbbd
--- /dev/null
+++ b/usr/src/lib/libc/port/threads/pthread.c
@@ -0,0 +1,316 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include "lint.h"
+#include "thr_uberdata.h"
+
+/*
+ * pthread_once related data
+ * This structure is exported as pthread_once_t in pthread.h.
+ * We export only the size of this structure. so check
+ * pthread_once_t in pthread.h before making a change here.
+ */
+typedef struct __once {
+ mutex_t mlock;
+ union {
+ uint32_t pad32_flag[2];
+ uint64_t pad64_flag;
+ } oflag;
+} __once_t;
+
+#define once_flag oflag.pad32_flag[1]
+
+/*
+ * Default attribute object for pthread_create() with NULL attr pointer.
+ * Note that the 'guardsize' field is initialized on the first call
+ * to pthread_create() with a NULL attr pointer.
+ */
+static thrattr_t _defattr =
+ {0, NULL, PTHREAD_CREATE_JOINABLE, PTHREAD_SCOPE_PROCESS,
+ 0, SCHED_OTHER, PTHREAD_EXPLICIT_SCHED, 0};
+
+/*
+ * pthread_create: creates a thread in the current process.
+ * calls common _thrp_create() after copying the attributes.
+ */
+#pragma weak pthread_create = _pthread_create
+int
+_pthread_create(pthread_t *thread, const pthread_attr_t *attr,
+ void * (*start_routine)(void *), void *arg)
+{
+ ulwp_t *self = curthread;
+ uberdata_t *udp = self->ul_uberdata;
+ long flag;
+ thrattr_t *ap;
+ pthread_t tid;
+ int policy;
+ pri_t priority;
+ int error;
+ int mapped = 0;
+ int mappedpri;
+ int rt = 0;
+
+ if (attr == NULL) {
+ ap = &_defattr;
+ if (ap->guardsize == 0) {
+ if (_lpagesize == 0)
+ _lpagesize = _sysconf(_SC_PAGESIZE);
+ ap->guardsize = _lpagesize;
+ }
+ } else if ((ap = attr->__pthread_attrp) == NULL) {
+ return (EINVAL);
+ }
+
+ if (ap->inherit == PTHREAD_INHERIT_SCHED) {
+ policy = self->ul_policy;
+ priority = self->ul_pri;
+ mapped = self->ul_pri_mapped;
+ mappedpri = self->ul_mappedpri;
+ } else {
+ policy = ap->policy;
+ priority = ap->prio;
+ if (policy == SCHED_OTHER) {
+ if (priority < THREAD_MIN_PRIORITY ||
+ priority > THREAD_MAX_PRIORITY) {
+ if (_validate_rt_prio(policy, priority))
+ return (EINVAL);
+ mapped = 1;
+ mappedpri = priority;
+ priority = _map_rtpri_to_gp(priority);
+ ASSERT(priority >= THREAD_MIN_PRIORITY &&
+ priority <= THREAD_MAX_PRIORITY);
+ }
+ } else if (policy == SCHED_FIFO || policy == SCHED_RR) {
+ if (_validate_rt_prio(policy, priority))
+ return (EINVAL);
+ if (_private_geteuid() == 0)
+ rt = 1;
+ } else {
+ return (EINVAL);
+ }
+ }
+
+ flag = ap->scope | ap->detachstate | THR_SUSPENDED;
+ error = _thrp_create(ap->stkaddr, ap->stksize, start_routine, arg,
+ flag, &tid, priority, policy, ap->guardsize);
+ if (error == 0) {
+ if (mapped) {
+ ulwp_t *ulwp = find_lwp(tid);
+ ulwp->ul_pri_mapped = 1;
+ ulwp->ul_mappedpri = mappedpri;
+ ulwp_unlock(ulwp, udp);
+ }
+ if (rt && _thrp_setlwpprio(tid, policy, priority))
+ thr_panic("_thrp_setlwpprio() failed");
+ if (thread)
+ *thread = tid;
+ (void) _thr_continue(tid);
+ }
+
+ /* posix version expects EAGAIN for lack of memory */
+ if (error == ENOMEM)
+ error = EAGAIN;
+ return (error);
+}
+
+/*
+ * pthread_once: calls given function only once.
+ * it synchronizes via mutex in pthread_once_t structure
+ */
+#pragma weak pthread_once = _pthread_once
+int
+_pthread_once(pthread_once_t *once_control, void (*init_routine)(void))
+{
+ __once_t *once = (__once_t *)once_control;
+
+ if (once == NULL || init_routine == NULL)
+ return (EINVAL);
+
+ if (once->once_flag == PTHREAD_ONCE_NOTDONE) {
+ (void) _private_mutex_lock(&once->mlock);
+ if (once->once_flag == PTHREAD_ONCE_NOTDONE) {
+ pthread_cleanup_push(_private_mutex_unlock,
+ &once->mlock);
+ (*init_routine)();
+ pthread_cleanup_pop(0);
+ once->once_flag = PTHREAD_ONCE_DONE;
+ }
+ (void) _private_mutex_unlock(&once->mlock);
+ }
+
+ return (0);
+}
+
+/*
+ * pthread_equal: equates two thread ids.
+ */
+#pragma weak pthread_equal = _pthread_equal
+int
+_pthread_equal(pthread_t t1, pthread_t t2)
+{
+ return (t1 == t2);
+}
+
+/*
+ * pthread_getschedparam: gets the sched parameters in a struct.
+ */
+#pragma weak pthread_getschedparam = _pthread_getschedparam
+int
+_pthread_getschedparam(pthread_t tid, int *policy, struct sched_param *param)
+{
+ uberdata_t *udp = curthread->ul_uberdata;
+ ulwp_t *ulwp;
+ int error = 0;
+
+ if (param == NULL || policy == NULL)
+ error = EINVAL;
+ else if ((ulwp = find_lwp(tid)) == NULL)
+ error = ESRCH;
+ else {
+ if (ulwp->ul_pri_mapped)
+ param->sched_priority = ulwp->ul_mappedpri;
+ else
+ param->sched_priority = ulwp->ul_pri;
+ *policy = ulwp->ul_policy;
+ ulwp_unlock(ulwp, udp);
+ }
+
+ return (error);
+}
+
+/*
+ * Besides the obvious arguments, the inheritflag needs to be explained:
+ * If set to PRIO_SET or PRIO_SET_PRIO, it does the normal, expected work
+ * of setting thread's assigned scheduling parameters and policy.
+ * If set to PRIO_INHERIT, it sets the thread's effective priority values
+ * (t_epri, t_empappedpri), and does not update the assigned priority values
+ * (t_pri, t_mappedpri). If set to PRIO_DISINHERIT, it clears the thread's
+ * effective priority values, and reverts the thread, if necessary, back
+ * to the assigned priority values.
+ */
+int
+_thread_setschedparam_main(pthread_t tid, int policy,
+ const struct sched_param *param, int inheritflag)
+{
+ uberdata_t *udp = curthread->ul_uberdata;
+ ulwp_t *ulwp;
+ int error = 0;
+ int prio;
+ int opolicy;
+ int mappedprio;
+ int mapped = 0;
+ pri_t *mappedprip;
+
+ if (param == NULL)
+ return (EINVAL);
+ if ((ulwp = find_lwp(tid)) == NULL)
+ return (ESRCH);
+ prio = param->sched_priority;
+ opolicy = ulwp->ul_policy;
+ if (inheritflag == PRIO_SET_PRIO) { /* don't change policy */
+ policy = opolicy;
+ inheritflag = PRIO_SET;
+ }
+ ASSERT(inheritflag == PRIO_SET || opolicy == policy);
+ if (inheritflag == PRIO_DISINHERIT) {
+ ulwp->ul_emappedpri = 0;
+ ulwp->ul_epri = 0;
+ prio = ulwp->ul_pri; /* ignore prio in sched_param */
+ }
+ if (policy == SCHED_OTHER) {
+ /*
+ * Set thread's policy to OTHER
+ */
+ if (prio < THREAD_MIN_PRIORITY || prio > THREAD_MAX_PRIORITY) {
+ if (_validate_rt_prio(policy, prio)) {
+ error = EINVAL;
+ goto out;
+ }
+ mapped = 1;
+ mappedprio = prio;
+ prio = _map_rtpri_to_gp(prio);
+ ASSERT(prio >= THREAD_MIN_PRIORITY &&
+ prio <= THREAD_MAX_PRIORITY);
+ }
+ /*
+ * Thread changing from FIFO/RR to OTHER
+ */
+ if (opolicy == SCHED_FIFO || opolicy == SCHED_RR) {
+ if ((error = _thrp_setlwpprio(tid, policy, prio)) != 0)
+ goto out;
+ }
+ if (inheritflag != PRIO_DISINHERIT) {
+ if (inheritflag == PRIO_INHERIT)
+ mappedprip = &ulwp->ul_emappedpri;
+ else
+ mappedprip = &ulwp->ul_mappedpri;
+ if (mapped) {
+ ulwp->ul_pri_mapped = 1;
+ *mappedprip = mappedprio;
+ } else {
+ ulwp->ul_pri_mapped = 0;
+ *mappedprip = 0;
+ }
+ }
+ ulwp->ul_policy = policy;
+ if (inheritflag == PRIO_INHERIT)
+ ulwp->ul_epri = prio;
+ else
+ ulwp->ul_pri = prio;
+ } else if (policy == SCHED_FIFO || policy == SCHED_RR) {
+ if (_validate_rt_prio(policy, prio))
+ error = EINVAL;
+ else {
+ if (_private_geteuid() == 0 &&
+ _thrp_setlwpprio(tid, policy, prio))
+ thr_panic("_thrp_setlwpprio failed");
+ ulwp->ul_policy = policy;
+ if (inheritflag == PRIO_INHERIT)
+ ulwp->ul_epri = prio;
+ else
+ ulwp->ul_pri = prio;
+ }
+ } else {
+ error = EINVAL;
+ }
+
+out:
+ ulwp_unlock(ulwp, udp);
+ return (error);
+}
+
+/*
+ * pthread_setschedparam: sets the sched parameters for a thread.
+ */
+#pragma weak pthread_setschedparam = _pthread_setschedparam
+int
+_pthread_setschedparam(pthread_t tid,
+ int policy, const struct sched_param *param)
+{
+ return (_thread_setschedparam_main(tid, policy, param, PRIO_SET));
+}