summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--usr/src/head/pthread.h9
-rw-r--r--usr/src/lib/libc/inc/thr_uberdata.h1
-rw-r--r--usr/src/lib/libc/port/mapfile-vers7
-rw-r--r--usr/src/lib/libc/port/threads/pthr_attr.c97
-rw-r--r--usr/src/lib/libc/port/threads/pthread.c37
-rw-r--r--usr/src/man/man3c/Makefile1
-rw-r--r--usr/src/man/man3c/pthread_attr_get_np.3c198
-rw-r--r--usr/src/man/man3c/pthread_attr_init.3c8
-rw-r--r--usr/src/man/man3lib/libc.3lib5
-rw-r--r--usr/src/pkg/manifests/system-library.man3c.inc1
-rw-r--r--usr/src/pkg/manifests/system-test-libctest.mf1
-rw-r--r--usr/src/test/libc-tests/runfiles/default.run2
-rw-r--r--usr/src/test/libc-tests/tests/Makefile1
-rw-r--r--usr/src/test/libc-tests/tests/threads/Makefile52
-rw-r--r--usr/src/test/libc-tests/tests/threads/pthread_attr_get_np.c348
15 files changed, 750 insertions, 18 deletions
diff --git a/usr/src/head/pthread.h b/usr/src/head/pthread.h
index 8b1bddfa7c..5fe007bf90 100644
--- a/usr/src/head/pthread.h
+++ b/usr/src/head/pthread.h
@@ -21,6 +21,7 @@
/*
* Copyright 2014 Garrett D'Amore <garrett@damore.org>
+ * Copyright 2016 Joyent, Inc.
*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
@@ -337,6 +338,14 @@ extern int pthread_mutexattr_setrobust_np(pthread_mutexattr_t *, int);
extern int pthread_mutexattr_getrobust_np(
const pthread_mutexattr_t *_RESTRICT_KYWD, int *_RESTRICT_KYWD);
+/*
+ * These are non-standardized extensions that we provide. Their origins are
+ * documented in their manual pages.
+ */
+#if !defined(_STRICT_SYMBOLS) || defined(__EXTENSIONS__)
+extern int pthread_attr_get_np(pthread_t, pthread_attr_t *);
+#endif /* !_STRICT_SYMBOLS || __EXTENSIONS__ */
+
#endif /* _ASM */
#ifdef __cplusplus
diff --git a/usr/src/lib/libc/inc/thr_uberdata.h b/usr/src/lib/libc/inc/thr_uberdata.h
index 0cf92cf499..384a23582d 100644
--- a/usr/src/lib/libc/inc/thr_uberdata.h
+++ b/usr/src/lib/libc/inc/thr_uberdata.h
@@ -680,6 +680,7 @@ typedef struct ulwp {
void *ul_unwind_ret; /* used only by _ex_clnup_handler() */
#endif
tumem_t ul_tmem; /* used only by umem */
+ uint_t ul_ptinherit; /* pthreads sched inherit value */
} ulwp_t;
#define ul_cursig ul_cp.s.cursig /* deferred signal number */
diff --git a/usr/src/lib/libc/port/mapfile-vers b/usr/src/lib/libc/port/mapfile-vers
index fa9931c23b..78e2707b49 100644
--- a/usr/src/lib/libc/port/mapfile-vers
+++ b/usr/src/lib/libc/port/mapfile-vers
@@ -93,6 +93,11 @@ $if _x86 && _ELF64
$add amd64
$endif
+SYMBOL_VERSION ILLUMOS_0.21 {
+ protected:
+ pthread_attr_get_np;
+} ILLUMOS_0.20;
+
SYMBOL_VERSION ILLUMOS_0.20 { # C11
protected:
aligned_alloc;
@@ -3024,6 +3029,8 @@ $endif
pset_assign_forced;
pset_bind_lwp;
_psignal;
+ pthread_attr_getdaemonstate_np;
+ pthread_attr_setdaemonstate_np;
_pthread_setcleanupinit;
__putwchar_xpg5;
__putwc_xpg5;
diff --git a/usr/src/lib/libc/port/threads/pthr_attr.c b/usr/src/lib/libc/port/threads/pthr_attr.c
index 51d08b4a5d..b04bcdba7c 100644
--- a/usr/src/lib/libc/port/threads/pthr_attr.c
+++ b/usr/src/lib/libc/port/threads/pthr_attr.c
@@ -24,6 +24,10 @@
* Use is subject to license terms.
*/
+/*
+ * Copyright 2015, Joyent, Inc.
+ */
+
#include "lint.h"
#include "thr_uberdata.h"
#include <sched.h>
@@ -242,7 +246,8 @@ pthread_attr_setdaemonstate_np(pthread_attr_t *attr, int daemonstate)
/*
* pthread_attr_getdaemonstate_np: gets the daemon state.
- * For now, this is a private interface in libc.
+ * 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)
@@ -366,7 +371,7 @@ pthread_attr_getschedpolicy(const pthread_attr_t *attr, int *policy)
*/
int
pthread_attr_setschedparam(pthread_attr_t *attr,
- const struct sched_param *param)
+ const struct sched_param *param)
{
thrattr_t *ap;
@@ -385,7 +390,7 @@ pthread_attr_setschedparam(pthread_attr_t *attr,
#pragma weak _pthread_attr_getschedparam = pthread_attr_getschedparam
int
pthread_attr_getschedparam(const pthread_attr_t *attr,
- struct sched_param *param)
+ struct sched_param *param)
{
thrattr_t *ap;
@@ -437,7 +442,7 @@ pthread_attr_getguardsize(const pthread_attr_t *attr, size_t *guardsize)
*/
int
pthread_attr_setstack(pthread_attr_t *attr,
- void *stackaddr, size_t stacksize)
+ void *stackaddr, size_t stacksize)
{
thrattr_t *ap;
@@ -458,7 +463,7 @@ pthread_attr_setstack(pthread_attr_t *attr,
*/
int
pthread_attr_getstack(const pthread_attr_t *attr,
- void **stackaddr, size_t *stacksize)
+ void **stackaddr, size_t *stacksize)
{
thrattr_t *ap;
@@ -470,3 +475,85 @@ pthread_attr_getstack(const pthread_attr_t *attr,
}
return (EINVAL);
}
+
+/*
+ * 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, it's 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 subtley different in
+ * a rather important way. While the pthread_attr_get_np() expects to be given
+ * a pthread_attr_t that has had pthread_attr_init() called on in,
+ * 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 uwlp
+ * 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;
+
+ ret = 0;
+out:
+ ulwp_unlock(target, udp);
+ return (ret);
+}
diff --git a/usr/src/lib/libc/port/threads/pthread.c b/usr/src/lib/libc/port/threads/pthread.c
index af3ad4b999..6a22995639 100644
--- a/usr/src/lib/libc/port/threads/pthread.c
+++ b/usr/src/lib/libc/port/threads/pthread.c
@@ -23,8 +23,9 @@
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
-
-#pragma ident "%Z%%M% %I% %E% SMI"
+/*
+ * Copyright 2016 Joyent, Inc.
+ */
#include "lint.h"
#include "thr_uberdata.h"
@@ -46,6 +47,22 @@ typedef struct __once {
#define once_flag oflag.pad32_flag[1]
static int
+_thr_setinherit(pthread_t tid, int inherit)
+{
+ ulwp_t *ulwp;
+ int error = 0;
+
+ if ((ulwp = find_lwp(tid)) == NULL) {
+ error = ESRCH;
+ } else {
+ ulwp->ul_ptinherit = inherit;
+ ulwp_unlock(ulwp, curthread->ul_uberdata);
+ }
+
+ return (error);
+}
+
+static int
_thr_setparam(pthread_t tid, int policy, int prio)
{
ulwp_t *ulwp;
@@ -88,7 +105,7 @@ _thr_setparam(pthread_t tid, int policy, int prio)
#pragma weak _pthread_create = pthread_create
int
pthread_create(pthread_t *thread, const pthread_attr_t *attr,
- void * (*start_routine)(void *), void *arg)
+ void * (*start_routine)(void *), void *arg)
{
ulwp_t *self = curthread;
const thrattr_t *ap = attr? attr->__pthread_attrp : def_thrattr();
@@ -113,15 +130,25 @@ pthread_create(pthread_t *thread, const pthread_attr_t *attr,
error = _thrp_create(ap->stkaddr, ap->stksize, start_routine, arg,
flag, &tid, ap->guardsize);
if (error == 0) {
+ /*
+ * Record the original inheritence value for
+ * pthread_getattr_np(). We should always be able to find the
+ * thread.
+ */
+ (void) _thr_setinherit(tid, ap->inherit);
+
if (ap->inherit == PTHREAD_EXPLICIT_SCHED &&
(ap->policy != self->ul_policy ||
- ap->prio != (self->ul_epri? self->ul_epri : self->ul_pri)))
+ ap->prio != (self->ul_epri ? self->ul_epri :
+ self->ul_pri))) {
/*
* The SUSv3 specification requires pthread_create()
* to fail with EPERM if it cannot set the scheduling
* policy and parameters on the new thread.
*/
error = _thr_setparam(tid, ap->policy, ap->prio);
+ }
+
if (error) {
/*
* We couldn't determine this error before
@@ -243,7 +270,7 @@ thr_getprio(thread_t tid, int *priority)
*/
int
pthread_setschedparam(pthread_t tid,
- int policy, const struct sched_param *param)
+ int policy, const struct sched_param *param)
{
return (_thr_setparam(tid, policy, param->sched_priority));
}
diff --git a/usr/src/man/man3c/Makefile b/usr/src/man/man3c/Makefile
index 67861882e4..eebbda3288 100644
--- a/usr/src/man/man3c/Makefile
+++ b/usr/src/man/man3c/Makefile
@@ -304,6 +304,7 @@ MANFILES= __fbufsize.3c \
pthread_attr_getstack.3c \
pthread_attr_getstackaddr.3c \
pthread_attr_getstacksize.3c \
+ pthread_attr_get_np.3c \
pthread_attr_init.3c \
pthread_barrier_destroy.3c \
pthread_barrier_wait.3c \
diff --git a/usr/src/man/man3c/pthread_attr_get_np.3c b/usr/src/man/man3c/pthread_attr_get_np.3c
new file mode 100644
index 0000000000..8ffb28c0fc
--- /dev/null
+++ b/usr/src/man/man3c/pthread_attr_get_np.3c
@@ -0,0 +1,198 @@
+.\"
+.\" This file and its contents are supplied under the terms of the
+.\" Common Development and Distribution License ("CDDL"), version 1.0.
+.\" You may only use this file in accordance with the terms of version
+.\" 1.0 of the CDDL.
+.\"
+.\" A full copy of the text of the CDDL should have accompanied this
+.\" source. A copy of the CDDL is also available via the Internet at
+.\" http://www.illumos.org/license/CDDL.
+.\"
+.\"
+.\" Copyright 2016 Joyent, Inc.
+.\"
+.Dd Feb 07, 2015
+.Dt PTHREAD_ATTR_GET_NP 3C
+.Os
+.Sh NAME
+.Nm pthread_attr_get_np
+.Nd get pthread attributes of a running thread
+.Sh SYNOPSIS
+.In pthread.h
+.Ft int
+.Fo pthread_attr_get_np
+.Fa "pthread_t thread"
+.Fa "pthread_attr_t *attr"
+.Fc
+.Sh DESCRIPTION
+The
+.Fn pthread_attr_get_np
+function provides a way to get the attributes of the thread
+.Fa thread
+after it has been created. This function is most commonly used to obtain
+the actual location and size of a thread's stack.
+.Pp
+The attributes pointer,
+.Fa attr ,
+will be filled in with the current attributes for the thread. The
+attributes should be allocated by a call to
+.Xr pthread_attr_init 3C
+prior to calling the
+.Fn pthrad_attr_get_np
+function. When
+.Fa attr
+is done being used, it should be destroyed through a call to
+.Xr pthread_attr_destroy 3C .
+.Pp
+The attributes of the thread
+.Fa thread
+will be the same as those passed in at the time
+.Xr pthread_create 3C
+was called (or the default set if none were specified), except that the
+following values will be updated:
+.Bl -tag -width Sy
+.It Sy Thread Stack Size
+If no explicit stack size was specified, then
+.Fa attr
+will contain the actual size of the stack.
+.Pp
+If the size of the stack was specified, then it may have been changed to
+ensure that the required alignment of the platform is satisfied.
+.It Sy The Stack Address
+If no stack address was specified, then
+.Fa attr
+will contain the actual address of the stack that the system allocated
+for the thread.
+.It Sy Thread Detach State
+The detach state, whether or not the thread may be joined by a call to
+.Xr pthread_join 3C ,
+may have changed since the process was created due to a call to
+.Xr pthread_detach 3C .
+.Fa attr
+will reflect the current setting of
+.Fa thread .
+.It Sy Thread Scheduling Parameter
+The scheduling parameter attribute will be updated with the current
+scheduling parameter of
+.Fa thread .
+This is the same information as available through
+.Xr pthread_getschedparam 3C
+and it is the preferred interface for obtaining that information.
+.It Sy Thread Scheduling Policy
+The scheduling policy attribute of
+.Fa attr
+will be updated with the current scheduling policy being applied to the
+thread. This may have changed, for example, due to a call to
+.Xr pthread_setschedparam 3C .
+As with the thread's scheduling parameter, the preferred interface for
+obtaining this information is by using
+.Xr pthread_getschedparam 3C .
+.It Sy Thread Guard Size
+The value of the guard size attribute for the thread will be updated to
+reflect the actual size of the guard installed for
+.Fa thread .
+For more information on the guard size of a thread and its purpose, see
+.Xr pthread_attr_getguardsize 3C .
+.El
+.Sh RETURN VALUES
+Upon successful completion, the
+.Fn pthread_attr_get_np
+and
+.Fn pthread_getattr_np
+functions return
+.Sy 0 .
+Otherwise, an error number is returned to indicate the error.
+.Sh EXAMPLES
+The following program demonstrates how to use these functions to get
+the location and stack size of a newly created thread.
+.Bd -literal
+#include <assert.h>
+#include <errno.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static pthread_t g_thr;
+
+void *
+print_stackinfo(void *arg)
+{
+ int ret;
+ pthread_attr_t attr;
+ pthread_t *thrp = arg;
+ void *stk;
+ size_t stksize;
+
+ if (pthread_attr_init(&attr) != 0) {
+ fprintf(stderr, "failed to init attr: %s\\n",
+ strerror(errno));
+ exit(1);
+ }
+
+ if (pthread_attr_get_np(*thrp, &attr) != 0) {
+ fprintf(stderr, "failed to get thread attributes: %s\\n",
+ strerror(errno));
+ exit(1);
+ }
+
+ ret = pthread_attr_getstackaddr(&attr, &stk);
+ assert(ret == 0);
+ ret = pthread_attr_getstacksize(&attr, &stksize);
+ assert(ret == 0);
+ (void) printf("stack base is at %p, it is %d bytes large\\n",
+ stk, stksize);
+ return (NULL);
+}
+
+int
+main(void)
+{
+ int ret;
+
+ if ((ret = pthread_create(&g_thr, NULL, print_stackinfo,
+ &g_thr) != 0)) {
+ fprintf(stderr, "failed to create a thread: %s\\n",
+ strerror(errno));
+ exit(1);
+ }
+
+ pthread_join(g_thr, NULL);
+ return (0);
+}
+.Ed
+.Sh ERRORS
+The
+.Fn pthread_attr_get_np
+function will fail if:
+.Bl -tag -width Er
+.It Er EINVAL
+The pthread_attr_t object
+.Fa attr
+was not properly initialized with a call to
+.Xr pthread_attr_init 3C .
+.It Er ESRCH
+No thread could be found corresponding to the specified thread ID,
+.Fa thread .
+.El
+.Sh INTERFACE STABILITY
+.Sy Committed
+.Sh MT-LEVEL
+.Sy MT-Safe
+.Sh SEE ALSO
+.Xr pthread_attr_destroy 3C ,
+.Xr pthread_attr_getdetachstate 3C ,
+.Xr pthread_attr_getguardsize 3C ,
+.Xr pthread_attr_getinheritsched 3C ,
+.Xr pthread_attr_getschedparam 3C ,
+.Xr pthread_attr_getschedpolicy 3C ,
+.Xr pthread_attr_getscope 3C ,
+.Xr pthread_attr_getstackaddr 3C ,
+.Xr pthread_attr_getstacksize 3C ,
+.Xr pthread_attr_init 3C ,
+.Xr pthread_create 3C ,
+.Xr pthread_detach 3C ,
+.Xr pthread_getschedparam 3C ,
+.Xr pthread_setschedparam 3C ,
+.Xr attributes 5 ,
+.Xr threads 5
diff --git a/usr/src/man/man3c/pthread_attr_init.3c b/usr/src/man/man3c/pthread_attr_init.3c
index 5d5bce0420..6dd32ecee7 100644
--- a/usr/src/man/man3c/pthread_attr_init.3c
+++ b/usr/src/man/man3c/pthread_attr_init.3c
@@ -9,7 +9,7 @@
.\" 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]
-.TH PTHREAD_ATTR_INIT 3C "Apr 1, 2008"
+.TH PTHREAD_ATTR_INIT 3C "Dec 10, 2015"
.SH NAME
pthread_attr_init, pthread_attr_destroy \- initialize or destroy threads
attribute object
@@ -28,7 +28,6 @@ cc -mt [ \fIflag\fR... ] \fIfile\fR... -lpthread [ \fIlibrary\fR... ]
.fi
.SH DESCRIPTION
-.sp
.LP
The function \fBpthread_attr_init()\fR initializes a thread attributes object
\fIattr\fR with the default value for all of the individual attributes used by
@@ -73,13 +72,11 @@ implementation may cause \fBpthread_attr_destroy()\fR to set \fIattr\fR to an
implementation-dependent invalid value. The behavior of using the attribute
after it has been destroyed is undefined.
.SH RETURN VALUES
-.sp
.LP
Upon successful completion, \fBpthread_attr_init()\fR and
\fBpthread_attr_destroy()\fR return a value of \fB0\fR. Otherwise, an error
number is returned to indicate the error.
.SH ERRORS
-.sp
.LP
The \fBpthread_attr_init()\fR function will fail if:
.sp
@@ -104,7 +101,6 @@ The \fBpthread_attr_destroy()\fR function may fail if:
.RE
.SH ATTRIBUTES
-.sp
.LP
See \fBattributes\fR(5) for descriptions of the following attributes:
.sp
@@ -124,9 +120,9 @@ Standard See \fBstandards\fR(5).
.TE
.SH SEE ALSO
-.sp
.LP
\fBsysconf\fR(3C), \fBpthread_attr_getdetachstate\fR(3C),
+\fBpthread_attr_get_np\fR(3C),
\fBpthread_attr_getguardsize\fR(3C), \fBpthread_attr_getinheritsched\fR(3C),
\fBpthread_attr_getschedparam\fR(3C), \fBpthread_attr_getschedpolicy\fR(3C),
\fBpthread_attr_getscope\fR(3C), \fBpthread_attr_getstackaddr\fR(3C),
diff --git a/usr/src/man/man3lib/libc.3lib b/usr/src/man/man3lib/libc.3lib
index 0ed2650dd3..a4ef6025eb 100644
--- a/usr/src/man/man3lib/libc.3lib
+++ b/usr/src/man/man3lib/libc.3lib
@@ -1,12 +1,12 @@
'\" te
.\" Copyright 2014 Garrett D'Amore <garrett@damore.org>
.\" Copyright (c) 2009, Sun Microsystems, Inc. All rights reserved.
-.\" Copyright (c) 2015, Joyent, Inc. All rights reserved.
+.\" Copyright 2016 Joyent, Inc.
.\" 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]
.\" Copyright 2011 by Delphix. All rights reserved.
-.TH LIBC 3LIB "Sep 02, 2015"
+.TH LIBC 3LIB "Dec 10, 2015"
.SH NAME
libc \- C library
.SH DESCRIPTION
@@ -507,6 +507,7 @@ l l .
\fBpset_list\fR \fBpset_setattr\fR
\fBpsiginfo\fR \fBpsignal\fR
\fBpthread_atfork\fR \fBpthread_attr_destroy\fR
+\fBpthread_attr_get_np\fR
\fBpthread_attr_getdetachstate\fR \fBpthread_attr_getguardsize\fR
\fBpthread_attr_getinheritsched\fR \fBpthread_attr_getschedparam\fR
\fBpthread_attr_getschedpolicy\fR \fBpthread_attr_getscope\fR
diff --git a/usr/src/pkg/manifests/system-library.man3c.inc b/usr/src/pkg/manifests/system-library.man3c.inc
index e9d0bda783..f0a33077b4 100644
--- a/usr/src/pkg/manifests/system-library.man3c.inc
+++ b/usr/src/pkg/manifests/system-library.man3c.inc
@@ -290,6 +290,7 @@ file path=usr/share/man/man3c/priv_str_to_set.3c
file path=usr/share/man/man3c/pset_getloadavg.3c
file path=usr/share/man/man3c/psignal.3c
file path=usr/share/man/man3c/pthread_atfork.3c
+file path=usr/share/man/man3c/pthread_attr_get_np.3c
file path=usr/share/man/man3c/pthread_attr_getdetachstate.3c
file path=usr/share/man/man3c/pthread_attr_getguardsize.3c
file path=usr/share/man/man3c/pthread_attr_getinheritsched.3c
diff --git a/usr/src/pkg/manifests/system-test-libctest.mf b/usr/src/pkg/manifests/system-test-libctest.mf
index e09d18574c..66ea7c17db 100644
--- a/usr/src/pkg/manifests/system-test-libctest.mf
+++ b/usr/src/pkg/manifests/system-test-libctest.mf
@@ -89,6 +89,7 @@ file path=opt/libc-tests/tests/nl_langinfo_test mode=0555
file path=opt/libc-tests/tests/nl_langinfo_test.$(ARCH) mode=0555
file path=opt/libc-tests/tests/nl_langinfo_test.$(ARCH64) mode=0555
file path=opt/libc-tests/tests/priv_gettext mode=0555
+file path=opt/libc-tests/tests/pthread_attr_get_np mode=0555
file path=opt/libc-tests/tests/quick_exit mode=0555
file path=opt/libc-tests/tests/quick_exit_order.32 mode=0555
file path=opt/libc-tests/tests/quick_exit_order.64 mode=0555
diff --git a/usr/src/test/libc-tests/runfiles/default.run b/usr/src/test/libc-tests/runfiles/default.run
index e53831e626..44e6c1e3dc 100644
--- a/usr/src/test/libc-tests/runfiles/default.run
+++ b/usr/src/test/libc-tests/runfiles/default.run
@@ -66,6 +66,8 @@ outputdir = /var/tmp/test_results
[/opt/libc-tests/tests/timespec_get.32]
[/opt/libc-tests/tests/timespec_get.64]
+[/opt/libc-tests/tests/pthread_attr_get_np]
+
[/opt/libc-tests/tests/symbols]
pre = setup
tests = [
diff --git a/usr/src/test/libc-tests/tests/Makefile b/usr/src/test/libc-tests/tests/Makefile
index 44e6a4ffff..72634efd10 100644
--- a/usr/src/test/libc-tests/tests/Makefile
+++ b/usr/src/test/libc-tests/tests/Makefile
@@ -24,6 +24,7 @@ SUBDIRS = \
random \
strerror \
symbols \
+ threads \
wcsrtombs \
wctype
diff --git a/usr/src/test/libc-tests/tests/threads/Makefile b/usr/src/test/libc-tests/tests/threads/Makefile
new file mode 100644
index 0000000000..1e73e7b8c7
--- /dev/null
+++ b/usr/src/test/libc-tests/tests/threads/Makefile
@@ -0,0 +1,52 @@
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright 2016 Joyent, Inc.
+#
+
+include $(SRC)/Makefile.master
+
+ROOTOPTPKG = $(ROOT)/opt/libc-tests
+TESTDIR = $(ROOTOPTPKG)/tests
+
+PROGS = pthread_attr_get_np
+
+include $(SRC)/cmd/Makefile.cmd
+include $(SRC)/test/Makefile.com
+
+CPPFLAGS += -D_REENTRANT
+
+CMDS = $(PROGS:%=$(TESTDIR)/%)
+$(CMDS) := FILEMODE = 0555
+
+all: $(PROGS)
+
+install: all $(CMDS)
+
+lint:
+
+clobber: clean
+ -$(RM) $(PROGS)
+
+clean:
+ -$(RM) *.o
+
+$(CMDS): $(TESTDIR) $(PROGS)
+
+$(TESTDIR):
+ $(INS.dir)
+
+$(TESTDIR)/%: %
+ $(INS.file)
+
+$(TESTDIR)/%: %
+ $(INS.file)
diff --git a/usr/src/test/libc-tests/tests/threads/pthread_attr_get_np.c b/usr/src/test/libc-tests/tests/threads/pthread_attr_get_np.c
new file mode 100644
index 0000000000..71a58c30fd
--- /dev/null
+++ b/usr/src/test/libc-tests/tests/threads/pthread_attr_get_np.c
@@ -0,0 +1,348 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2016 Joyent, Inc.
+ */
+
+/*
+ * Test and verify that pthrad_attr_get_np works as we expect.
+ *
+ * Verify the following:
+ * o ESRCH
+ * o stack size is set to a valid value after a thread is created.
+ * o main thread can grab an alternate thread's info.
+ * o custom guard size is honored
+ * o detach state
+ * - detached 1
+ * - joinable 2
+ * - changing 2
+ * o daemon state
+ * - enabled 1
+ * - disabled 2
+ * o scope
+ * - system 1
+ * - process 2
+ * o inheritable
+ * - inherit 1
+ * - explicit 2
+ * o priority
+ * - honors change 2
+ * o policy
+ * - honours change 2
+ *
+ *
+ * For each of the cases above we explicitly go through and create the set of
+ * attributes as marked above and then inside of a thread, verify that the
+ * attributes match what we expect. Because each case ends up in creating a
+ * detached thread, we opt to have both it and the main thread enter a barrier
+ * to indicate that we have completed the test successfully.
+ */
+
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <pthread.h>
+#include <unistd.h>
+#include <ucontext.h>
+#include <sched.h>
+#include <strings.h>
+#include <stdlib.h>
+
+#include <sys/procfs.h>
+#include <sys/debug.h>
+
+/*
+ * Currently these are only defined in thr_uberdata.h. Rather than trying and
+ * fight with libc headers, just explicitly define them here.
+ */
+#define PTHREAD_CREATE_DAEMON_NP 0x100 /* = THR_DAEMON */
+#define PTHREAD_CREATE_NONDAEMON_NP 0
+extern int pthread_attr_setdaemonstate_np(pthread_attr_t *, int);
+extern int pthread_attr_getdaemonstate_np(const pthread_attr_t *, int *);
+
+#define PGN_TEST_PRI 23
+
+static pthread_attr_t pgn_attr;
+static pthread_attr_t pgn_thr_attr;
+static pthread_barrier_t pgn_barrier;
+
+#ifdef __sparc
+#define gregs __gregs
+#endif
+
+/*
+ * Verify that the stack pointer of a context is consistent with where the
+ * attributes indicate.
+ */
+static void
+pgn_verif_thr_stack(pthread_attr_t *attr)
+{
+ size_t stksz;
+ void *stk;
+ ucontext_t ctx;
+ uint32_t sp;
+
+ VERIFY0(getcontext(&ctx));
+ VERIFY0(pthread_attr_getstack(attr, &stk, &stksz));
+ VERIFY3P(stk, !=, NULL);
+ VERIFY3S(stksz, !=, 0);
+ sp = ctx.uc_mcontext.gregs[R_SP];
+ VERIFY3U(sp, >, (uintptr_t)stk);
+ VERIFY3U(sp, <, (uintptr_t)stk + stksz);
+}
+
+#ifdef __sparc
+#undef gregs
+#endif
+
+static void
+pgn_test_fini(void)
+{
+ int ret;
+
+ ret = pthread_barrier_wait(&pgn_barrier);
+ VERIFY(ret == 0 || ret == PTHREAD_BARRIER_SERIAL_THREAD);
+ VERIFY0(pthread_attr_destroy(&pgn_attr));
+ VERIFY0(pthread_attr_destroy(&pgn_thr_attr));
+ VERIFY0(pthread_barrier_destroy(&pgn_barrier));
+}
+
+static void
+pgn_test_init(void)
+{
+ VERIFY0(pthread_attr_init(&pgn_attr));
+ VERIFY0(pthread_attr_init(&pgn_thr_attr));
+ VERIFY0(pthread_barrier_init(&pgn_barrier, NULL, 2));
+}
+
+/* ARGSUSED */
+static void *
+pgn_set_one_thr(void *arg)
+{
+ int odetach, ndetach;
+ int odaemon, ndaemon;
+ int oscope, nscope;
+ int oinherit, ninherit;
+
+ VERIFY0(pthread_attr_get_np(pthread_self(), &pgn_attr));
+ pgn_verif_thr_stack(&pgn_attr);
+
+ VERIFY0(pthread_attr_getdetachstate(&pgn_thr_attr, &odetach));
+ VERIFY0(pthread_attr_getdetachstate(&pgn_attr, &ndetach));
+
+ VERIFY3S(odetach, ==, ndetach);
+ VERIFY3S(ndetach, ==, PTHREAD_CREATE_DETACHED);
+
+ VERIFY0(pthread_attr_getdaemonstate_np(&pgn_thr_attr, &odaemon));
+ VERIFY0(pthread_attr_getdaemonstate_np(&pgn_attr, &ndaemon));
+
+ VERIFY3S(odaemon, ==, ndaemon);
+ VERIFY3S(ndaemon, ==, PTHREAD_CREATE_DAEMON_NP);
+
+ VERIFY0(pthread_attr_getscope(&pgn_thr_attr, &oscope));
+ VERIFY0(pthread_attr_getscope(&pgn_attr, &nscope));
+
+ VERIFY3S(oscope, ==, nscope);
+ VERIFY3S(nscope, ==, PTHREAD_SCOPE_SYSTEM);
+
+ VERIFY0(pthread_attr_getinheritsched(&pgn_thr_attr, &oinherit));
+ VERIFY0(pthread_attr_getinheritsched(&pgn_attr, &ninherit));
+
+ VERIFY3S(oinherit, ==, ninherit);
+ VERIFY3S(ninherit, ==, PTHREAD_INHERIT_SCHED);
+
+ VERIFY3S(pthread_barrier_wait(&pgn_barrier), !=, 1);
+ return (NULL);
+}
+
+static void
+pgn_set_one(void)
+{
+ int ret;
+ pthread_t thr;
+
+ pgn_test_init();
+
+ VERIFY0(pthread_attr_setdetachstate(&pgn_thr_attr,
+ PTHREAD_CREATE_DETACHED));
+ VERIFY0(pthread_attr_setdaemonstate_np(&pgn_thr_attr,
+ PTHREAD_CREATE_DAEMON_NP));
+ VERIFY0(pthread_attr_setscope(&pgn_thr_attr, PTHREAD_SCOPE_SYSTEM));
+ VERIFY0(pthread_attr_setinheritsched(&pgn_thr_attr,
+ PTHREAD_INHERIT_SCHED));
+
+ VERIFY0(pthread_create(&thr, &pgn_thr_attr, pgn_set_one_thr, NULL));
+
+ /*
+ * Verify it's not joinable.
+ */
+ ret = pthread_join(thr, NULL);
+ VERIFY3S(ret, ==, EINVAL);
+
+ /*
+ * At this point we let the test continue and wait on the barrier. We'll
+ * wake up when the other thread is done.
+ */
+ pgn_test_fini();
+}
+
+/* ARGSUSED */
+static void *
+pgn_set_two_thr(void *arg)
+{
+ int odetach, ndetach;
+ int odaemon, ndaemon;
+ int oscope, nscope;
+ int oinherit, ninherit;
+ int opolicy, npolicy;
+ struct sched_param oparam, nparam;
+
+ VERIFY0(pthread_attr_get_np(pthread_self(), &pgn_attr));
+ pgn_verif_thr_stack(&pgn_attr);
+
+ VERIFY0(pthread_attr_getdetachstate(&pgn_thr_attr, &odetach));
+ VERIFY0(pthread_attr_getdetachstate(&pgn_attr, &ndetach));
+
+ VERIFY3S(odetach, ==, ndetach);
+ VERIFY3S(ndetach, ==, PTHREAD_CREATE_JOINABLE);
+
+ VERIFY0(pthread_attr_getdaemonstate_np(&pgn_thr_attr, &odaemon));
+ VERIFY0(pthread_attr_getdaemonstate_np(&pgn_attr, &ndaemon));
+
+ VERIFY3S(odaemon, ==, ndaemon);
+ VERIFY3S(ndaemon, ==, PTHREAD_CREATE_NONDAEMON_NP);
+
+ VERIFY0(pthread_attr_getscope(&pgn_thr_attr, &oscope));
+ VERIFY0(pthread_attr_getscope(&pgn_attr, &nscope));
+
+ VERIFY3S(oscope, ==, nscope);
+ VERIFY3S(nscope, ==, PTHREAD_SCOPE_PROCESS);
+
+ VERIFY0(pthread_attr_getinheritsched(&pgn_thr_attr, &oinherit));
+ VERIFY0(pthread_attr_getinheritsched(&pgn_attr, &ninherit));
+
+ VERIFY3S(oinherit, ==, ninherit);
+ VERIFY3S(ninherit, ==, PTHREAD_EXPLICIT_SCHED);
+
+ VERIFY0(pthread_attr_getschedpolicy(&pgn_thr_attr, &opolicy));
+ VERIFY0(pthread_attr_getschedpolicy(&pgn_attr, &npolicy));
+
+ VERIFY3S(opolicy, ==, npolicy);
+ VERIFY3S(npolicy, ==, SCHED_FSS);
+
+ /*
+ * Now that we've validated the basics, go ahead and test the changes,
+ * which include making sure that we see updates via
+ * pthread_setschedparam() and pthread_detach().
+ */
+ VERIFY0(pthread_detach(pthread_self()));
+
+ opolicy = SCHED_FX;
+ oparam.sched_priority = PGN_TEST_PRI;
+ VERIFY0(pthread_setschedparam(pthread_self(), opolicy, &oparam));
+
+ VERIFY0(pthread_attr_get_np(pthread_self(), &pgn_attr));
+ VERIFY0(pthread_attr_getdetachstate(&pgn_attr, &ndetach));
+
+ VERIFY3S(odetach, !=, ndetach);
+ VERIFY3S(ndetach, ==, PTHREAD_CREATE_DETACHED);
+
+ VERIFY0(pthread_attr_getschedpolicy(&pgn_attr, &npolicy));
+ VERIFY0(pthread_attr_getschedparam(&pgn_attr, &nparam));
+
+ VERIFY3S(opolicy, ==, npolicy);
+ VERIFY3S(npolicy, ==, SCHED_FX);
+
+ VERIFY3S(oparam.sched_priority, ==, nparam.sched_priority);
+ VERIFY3S(nparam.sched_priority, ==, PGN_TEST_PRI);
+
+ VERIFY3S(pthread_barrier_wait(&pgn_barrier), !=, 1);
+
+ return (NULL);
+}
+
+static void
+pgn_set_two(void)
+{
+ pthread_t thr;
+
+ pgn_test_init();
+
+ VERIFY0(pthread_attr_setdetachstate(&pgn_thr_attr,
+ PTHREAD_CREATE_JOINABLE));
+ VERIFY0(pthread_attr_setdaemonstate_np(&pgn_thr_attr,
+ PTHREAD_CREATE_NONDAEMON_NP));
+ VERIFY0(pthread_attr_setscope(&pgn_thr_attr, PTHREAD_SCOPE_PROCESS));
+ VERIFY0(pthread_attr_setinheritsched(&pgn_thr_attr,
+ PTHREAD_EXPLICIT_SCHED));
+ VERIFY0(pthread_attr_setschedpolicy(&pgn_thr_attr, SCHED_FSS));
+
+ VERIFY0(pthread_create(&thr, &pgn_thr_attr, pgn_set_two_thr, NULL));
+
+ /*
+ * At this point we let the test continue and wait on the barrier. We'll
+ * wake up when the other thread is done.
+ */
+ pgn_test_fini();
+}
+
+/* ARGSUSED */
+static void *
+pgn_set_three_thr(void *arg)
+{
+ VERIFY3S(pthread_barrier_wait(&pgn_barrier), !=, 1);
+
+ return (NULL);
+}
+
+void
+pgn_set_three(void)
+{
+ pthread_t thr;
+ pthread_attr_t altattr, selfattr;
+ void *altstk, *selfstk;
+ size_t altsz, selfsz;
+
+ VERIFY0(pthread_attr_init(&altattr));
+ VERIFY0(pthread_attr_init(&selfattr));
+ pgn_test_init();
+
+ VERIFY0(pthread_create(&thr, NULL, pgn_set_three_thr, NULL));
+
+ VERIFY0(pthread_attr_get_np(thr, &altattr));
+ VERIFY0(pthread_attr_get_np(pthread_self(), &selfattr));
+
+ VERIFY0(pthread_attr_getstack(&selfattr, &selfstk, &selfsz));
+ VERIFY0(pthread_attr_getstack(&altattr, &altstk, &altsz));
+ VERIFY3P(altstk, !=, selfstk);
+
+ pgn_test_fini();
+ VERIFY0(pthread_attr_destroy(&selfattr));
+ VERIFY0(pthread_attr_destroy(&altattr));
+}
+
+int
+main(void)
+{
+ int ret;
+
+ VERIFY0(pthread_attr_init(&pgn_attr));
+
+ ret = pthread_attr_get_np(UINT32_MAX, &pgn_attr);
+ VERIFY3S(ret, ==, ESRCH);
+
+ pgn_set_one();
+ pgn_set_two();
+ pgn_set_three();
+
+ exit(0);
+}