Module Name:    src
Committed By:   thorpej
Date:           Sat Apr  4 20:20:12 UTC 2020

Modified Files:
        src/sys/compat/netbsd32: syscalls.master
        src/sys/kern: kern_exit.c kern_lwp.c sys_lwp.c syscalls.master
        src/sys/sys: lwp.h
Added Files:
        src/tests/lib/libc/sys: t_lwp_tid.c

Log Message:
Add support for lazily generating a "global thread ID" for a LWP.  This
identifier uniquely identifies an LWP across the entire system, and will
be used in future improvements in user-space synchronization primitives.

(Test disabled and libc stub not included intentionally so as to avoid
multiple libc version bumps.)


To generate a diff of this commit:
cvs rdiff -u -r1.133 -r1.134 src/sys/compat/netbsd32/syscalls.master
cvs rdiff -u -r1.286 -r1.287 src/sys/kern/kern_exit.c
cvs rdiff -u -r1.232 -r1.233 src/sys/kern/kern_lwp.c
cvs rdiff -u -r1.75 -r1.76 src/sys/kern/sys_lwp.c
cvs rdiff -u -r1.297 -r1.298 src/sys/kern/syscalls.master
cvs rdiff -u -r1.204 -r1.205 src/sys/sys/lwp.h
cvs rdiff -u -r0 -r1.1 src/tests/lib/libc/sys/t_lwp_tid.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/compat/netbsd32/syscalls.master
diff -u src/sys/compat/netbsd32/syscalls.master:1.133 src/sys/compat/netbsd32/syscalls.master:1.134
--- src/sys/compat/netbsd32/syscalls.master:1.133	Thu Mar 12 15:02:29 2020
+++ src/sys/compat/netbsd32/syscalls.master	Sat Apr  4 20:20:12 2020
@@ -1,4 +1,4 @@
-	$NetBSD: syscalls.master,v 1.133 2020/03/12 15:02:29 pgoyette Exp $
+	$NetBSD: syscalls.master,v 1.134 2020/04/04 20:20:12 thorpej Exp $
 
 ;	from: NetBSD: syscalls.master,v 1.81 1998/07/05 08:49:50 jonathan Exp
 ;	@(#)syscalls.master	8.2 (Berkeley) 1/13/94
@@ -747,7 +747,7 @@
 				netbsd32_charp name, netbsd32_size_t len); }
 325	STD 		{ int|netbsd32||_lwp_ctl(int features, \
 				netbsd32_pointer_t address); }
-326	UNIMPL
+326	NOARGS 		{ lwptid_t|sys||_lwp_gettid(void); }
 327	UNIMPL
 328	UNIMPL
 329	UNIMPL

Index: src/sys/kern/kern_exit.c
diff -u src/sys/kern/kern_exit.c:1.286 src/sys/kern/kern_exit.c:1.287
--- src/sys/kern/kern_exit.c:1.286	Thu Mar 26 21:31:55 2020
+++ src/sys/kern/kern_exit.c	Sat Apr  4 20:20:12 2020
@@ -1,4 +1,4 @@
-/*	$NetBSD: kern_exit.c,v 1.286 2020/03/26 21:31:55 ad Exp $	*/
+/*	$NetBSD: kern_exit.c,v 1.287 2020/04/04 20:20:12 thorpej Exp $	*/
 
 /*-
  * Copyright (c) 1998, 1999, 2006, 2007, 2008, 2020 The NetBSD Foundation, Inc.
@@ -67,7 +67,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: kern_exit.c,v 1.286 2020/03/26 21:31:55 ad Exp $");
+__KERNEL_RCSID(0, "$NetBSD: kern_exit.c,v 1.287 2020/04/04 20:20:12 thorpej Exp $");
 
 #include "opt_ktrace.h"
 #include "opt_dtrace.h"
@@ -265,7 +265,16 @@ exit1(struct lwp *l, int exitcode, int s
 	sigfillset(&p->p_sigctx.ps_sigignore);
 	sigclearall(p, NULL, &kq);
 	p->p_stat = SDYING;
-	mutex_exit(p->p_lock);
+
+	/*
+	 * Perform any required thread cleanup.  Do this early so
+	 * anyone wanting to look us up by our global thread ID
+	 * will fail to find us.
+	 *
+	 * N.B. this will unlock p->p_lock on our behalf.
+	 */
+	lwp_thread_cleanup(l);
+
 	ksiginfo_queue_drain(&kq);
 
 	/* Destroy any lwpctl info. */
@@ -559,9 +568,7 @@ exit1(struct lwp *l, int exitcode, int s
 	/* Free the linux lwp id */
 	if ((l->l_pflag & LP_PIDLID) != 0 && l->l_lid != p->p_pid)
 		proc_free_pid(l->l_lid);
-	if (l->l_refcnt > 0) {
-		lwp_drainrefs(l);
-	}
+	lwp_drainrefs(l);
 	lwp_lock(l);
 	l->l_prflag &= ~LPR_DETACHED;
 	l->l_stat = LSZOMB;

Index: src/sys/kern/kern_lwp.c
diff -u src/sys/kern/kern_lwp.c:1.232 src/sys/kern/kern_lwp.c:1.233
--- src/sys/kern/kern_lwp.c:1.232	Sat Apr  4 06:51:46 2020
+++ src/sys/kern/kern_lwp.c	Sat Apr  4 20:20:12 2020
@@ -1,4 +1,4 @@
-/*	$NetBSD: kern_lwp.c,v 1.232 2020/04/04 06:51:46 maxv Exp $	*/
+/*	$NetBSD: kern_lwp.c,v 1.233 2020/04/04 20:20:12 thorpej Exp $	*/
 
 /*-
  * Copyright (c) 2001, 2006, 2007, 2008, 2009, 2019, 2020
@@ -211,7 +211,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: kern_lwp.c,v 1.232 2020/04/04 06:51:46 maxv Exp $");
+__KERNEL_RCSID(0, "$NetBSD: kern_lwp.c,v 1.233 2020/04/04 20:20:12 thorpej Exp $");
 
 #include "opt_ddb.h"
 #include "opt_lockdebug.h"
@@ -245,6 +245,8 @@ __KERNEL_RCSID(0, "$NetBSD: kern_lwp.c,v
 #include <sys/psref.h>
 #include <sys/msan.h>
 #include <sys/kcov.h>
+#include <sys/thmap.h>
+#include <sys/cprng.h>
 
 #include <uvm/uvm_extern.h>
 #include <uvm/uvm_object.h>
@@ -252,6 +254,64 @@ __KERNEL_RCSID(0, "$NetBSD: kern_lwp.c,v
 static pool_cache_t	lwp_cache	__read_mostly;
 struct lwplist		alllwp		__cacheline_aligned;
 
+/*
+ * Lookups by global thread ID operate outside of the normal LWP
+ * locking protocol.
+ *
+ * We are using a thmap, which internally can perform lookups lock-free.
+ * However, we still need to serialize lookups against LWP exit.  We
+ * achieve this as follows:
+ *
+ * => Assignment of TID is performed lazily by the LWP itself, when it
+ *    is first requested.  Insertion into the thmap is done completely
+ *    lock-free (other than the internal locking performed by thmap itself).
+ *    Once the TID is published in the map, the l___tid field in the LWP
+ *    is protected by p_lock.
+ *
+ * => When we look up an LWP in the thmap, we take lwp_threadid_lock as
+ *    a READER.  While still holding the lock, we add a reference to
+ *    the LWP (using atomics).  After adding the reference, we drop the
+ *    lwp_threadid_lock.  We now take p_lock and check the state of the
+ *    LWP.  If the LWP is draining its references or if the l___tid field
+ *    has been invalidated, we drop the reference we took and return NULL.
+ *    Otherwise, the lookup has succeeded and the LWP is returned with a
+ *    reference count that the caller is responsible for dropping.
+ *
+ * => When a LWP is exiting it releases its TID.  While holding the
+ *    p_lock, the entry is deleted from the thmap and the l___tid field
+ *    invalidated.  Once the field is invalidated, p_lock is released.
+ *    It is done in this sequence because the l___tid field is used as
+ *    the lookup key storage in the thmap in order to conserve memory.
+ *    Even if a lookup races with this process and succeeds only to have
+ *    the TID invalidated, it's OK because it also results in a reference
+ *    that will be drained later.
+ *
+ * => Deleting a node also requires GC of now-unused thmap nodes.  The
+ *    serialization point between stage_gc and gc is performed by simply
+ *    taking the lwp_threadid_lock as a WRITER and immediately releasing
+ *    it.  By doing this, we know that any busy readers will have drained.
+ *
+ * => When a LWP is exiting, it also drains off any references being
+ *    held by others.  However, the reference in the lookup path is taken
+ *    outside the normal locking protocol.  There needs to be additional
+ *    serialization so that EITHER lwp_drainrefs() sees the incremented
+ *    reference count so that it knows to wait, OR lwp_getref_tid() sees
+ *    that the LWP is waiting to drain and thus drops the reference
+ *    immediately.  This is achieved by taking lwp_threadid_lock as a
+ *    WRITER when setting LPR_DRAINING.  Note the locking order:
+ *
+ *		p_lock -> lwp_threadid_lock
+ *
+ * Note that this scheme could easily use pserialize(9) in place of the
+ * lwp_threadid_lock rwlock lock.  However, this would require placing a
+ * pserialize_perform() call in the LWP exit path, which is arguably more
+ * expensive than briefly taking a global lock that should be relatively
+ * uncontended.  This issue can be revisited if the rwlock proves to be
+ * a performance problem.
+ */
+static krwlock_t	lwp_threadid_lock	__cacheline_aligned;
+static thmap_t *	lwp_threadid_map	__read_mostly;
+
 static void		lwp_dtor(void *, void *);
 
 /* DTrace proc provider probes */
@@ -285,6 +345,7 @@ struct lwp lwp0 __aligned(MIN_LWP_ALIGNM
 	.l_fd = &filedesc0,
 };
 
+static void lwp_threadid_init(void);
 static int sysctl_kern_maxlwp(SYSCTLFN_PROTO);
 
 /*
@@ -337,6 +398,7 @@ lwpinit(void)
 
 	maxlwp = cpu_maxlwp();
 	sysctl_kern_lwp_setup();
+	lwp_threadid_init();
 }
 
 void
@@ -1126,7 +1188,15 @@ lwp_exit(struct lwp *l)
 		/* NOTREACHED */
 	}
 	p->p_nzlwps++;
-	mutex_exit(p->p_lock);
+
+	/*
+	 * Perform any required thread cleanup.  Do this early so
+	 * anyone wanting to look us up by our global thread ID
+	 * will fail to find us.
+	 *
+	 * N.B. this will unlock p->p_lock on our behalf.
+	 */
+	lwp_thread_cleanup(l);
 
 	if (p->p_emul->e_lwp_exit)
 		(*p->p_emul->e_lwp_exit)(l);
@@ -1187,10 +1257,8 @@ lwp_exit(struct lwp *l)
 	 */
 	mutex_enter(p->p_lock);
 	for (;;) {
-		if (l->l_refcnt > 0) {
-			lwp_drainrefs(l);
+		if (lwp_drainrefs(l))
 			continue;
-		}
 		if ((l->l_prflag & LPR_DETACHED) != 0) {
 			if ((l2 = p->p_zomblwp) != NULL) {
 				p->p_zomblwp = NULL;
@@ -1693,6 +1761,19 @@ lwp_need_userret(struct lwp *l)
 }
 
 /*
+ * Add one reference to an LWP.  Interlocked against lwp_drainrefs()
+ * either by holding the proc's lock or by holding lwp_threadid_lock.
+ */
+static void
+lwp_addref2(struct lwp *l)
+{
+
+	KASSERT(l->l_stat != LSZOMB);
+
+	atomic_inc_uint(&l->l_refcnt);
+}
+
+/*
  * Add one reference to an LWP.  This will prevent the LWP from
  * exiting, thus keep the lwp structure and PCB around to inspect.
  */
@@ -1701,9 +1782,7 @@ lwp_addref(struct lwp *l)
 {
 
 	KASSERT(mutex_owned(l->l_proc->p_lock));
-	KASSERT(l->l_stat != LSZOMB);
-
-	l->l_refcnt++;
+	lwp_addref2(l);
 }
 
 /*
@@ -1732,24 +1811,40 @@ lwp_delref2(struct lwp *l)
 
 	KASSERT(mutex_owned(p->p_lock));
 	KASSERT(l->l_stat != LSZOMB);
-	KASSERT(l->l_refcnt > 0);
+	KASSERT(atomic_load_relaxed(&l->l_refcnt) > 0);
 
-	if (--l->l_refcnt == 0)
+	if (atomic_dec_uint_nv(&l->l_refcnt) == 0)
 		cv_broadcast(&p->p_lwpcv);
 }
 
 /*
- * Drain all references to the current LWP.
+ * Drain all references to the current LWP.  Returns true if
+ * we blocked.
  */
-void
+bool
 lwp_drainrefs(struct lwp *l)
 {
 	struct proc *p = l->l_proc;
+	bool rv = false;
 
 	KASSERT(mutex_owned(p->p_lock));
 
-	while (l->l_refcnt > 0)
+	/*
+	 * Lookups in the lwp_threadid_map hold lwp_threadid_lock
+	 * as a reader, increase l_refcnt, release it, and then
+	 * acquire p_lock to check for LPR_DRAINING.  By taking
+	 * lwp_threadid_lock as a writer here we ensure that either
+	 * we see the increase in l_refcnt or that they see LPR_DRAINING.
+	 */
+	rw_enter(&lwp_threadid_lock, RW_WRITER);
+	l->l_prflag |= LPR_DRAINING;
+	rw_exit(&lwp_threadid_lock);
+
+	while (atomic_load_relaxed(&l->l_refcnt) > 0) {
+		rv = true;
 		cv_wait(&p->p_lwpcv, p->p_lock);
+	}
+	return rv;
 }
 
 /*
@@ -2066,6 +2161,169 @@ lwp_renumber(lwp_t *l, lwpid_t lid)
 	}
 }
 
+#define	LWP_TID_MASK	0x3fffffff		/* placeholder */
+
+static void
+lwp_threadid_init(void)
+{
+	rw_init(&lwp_threadid_lock);
+	lwp_threadid_map = thmap_create(0, NULL, THMAP_NOCOPY);
+}
+
+static void
+lwp_threadid_alloc(struct lwp * const l)
+{
+
+	KASSERT(l == curlwp);
+	KASSERT(l->l___tid == 0);
+
+	for (;;) {
+		l->l___tid = cprng_fast32() & LWP_TID_MASK;
+		if (l->l___tid != 0 &&
+		    /*
+		     * There is no need to take the lwp_threadid_lock
+		     * while inserting into the map: internally, the
+		     * map is already concurrency-safe, and the lock
+		     * is only needed to serialize removal with respect
+		     * to lookup.
+		     */
+		    thmap_put(lwp_threadid_map,
+			      &l->l___tid, sizeof(l->l___tid), l) == l) {
+			/* claimed! */
+			return;
+		}
+		preempt_point();
+	}
+}
+
+static inline void
+lwp_threadid_gc_serialize(void)
+{
+
+	/*
+	 * By acquiring the lock as a writer, we will know that
+	 * all of the existing readers have drained away and thus
+	 * the GC is safe.
+	 */
+	rw_enter(&lwp_threadid_lock, RW_WRITER);
+	rw_exit(&lwp_threadid_lock);
+}
+
+static void
+lwp_threadid_free(struct lwp * const l)
+{
+
+	KASSERT(l == curlwp);
+	KASSERT(l->l___tid != 0);
+
+	/*
+	 * Ensure that anyone who finds this entry in the lock-free lookup
+	 * path sees that the key has been deleted by serialzing with the
+	 * examination of l___tid.
+	 *
+	 * N.B. l___tid field must be zapped *after* deleting from the map
+	 * because that field is being used as the key storage by thmap.
+	 */
+	KASSERT(mutex_owned(l->l_proc->p_lock));
+	struct lwp * const ldiag __diagused = thmap_del(lwp_threadid_map,
+	    &l->l___tid, sizeof(l->l___tid));
+	l->l___tid = 0;
+	mutex_exit(l->l_proc->p_lock);
+
+	KASSERT(l == ldiag);
+
+	void * const gc_ref = thmap_stage_gc(lwp_threadid_map);
+	lwp_threadid_gc_serialize();
+	thmap_gc(lwp_threadid_map, gc_ref);
+}
+
+/*
+ * Return the current LWP's global thread ID.  Only the current LWP
+ * should ever use this value, unless it is guaranteed that the LWP
+ * is paused (and then it should be accessed directly, rather than
+ * by this accessor).
+ */
+lwpid_t
+lwp_gettid(void)
+{
+	struct lwp * const l = curlwp;
+
+	if (l->l___tid == 0)
+		lwp_threadid_alloc(l);
+
+	return l->l___tid;
+}
+
+/*
+ * Lookup an LWP by global thread ID.  Care must be taken because
+ * callers of this are operating outside the normal locking protocol.
+ * We return the LWP with an additional reference that must be dropped
+ * with lwp_delref().
+ */
+struct lwp *
+lwp_getref_tid(lwpid_t tid)
+{
+	struct lwp *l, *rv;
+
+	rw_enter(&lwp_threadid_lock, RW_READER);
+	l = thmap_get(lwp_threadid_map, &tid, sizeof(&tid));
+	if (__predict_false(l == NULL)) {
+		rw_exit(&lwp_threadid_lock);
+		return NULL;
+	}
+
+	/*
+	 * Acquire a reference on the lwp.  It is safe to do this unlocked
+	 * here because lwp_drainrefs() serializes with us by taking the
+	 * lwp_threadid_lock as a writer.
+	 */
+	lwp_addref2(l);
+	rw_exit(&lwp_threadid_lock);
+
+	/*
+	 * Now verify that our reference is valid.
+	 */
+	mutex_enter(l->l_proc->p_lock);
+	if (__predict_false((l->l_prflag & LPR_DRAINING) != 0 ||
+			    l->l___tid == 0)) {
+		lwp_delref2(l);
+		rv = NULL;
+	} else {
+		rv = l;
+	}
+	mutex_exit(l->l_proc->p_lock);
+
+	return rv;
+}
+
+/*
+ * Perform any thread-related cleanup on LWP exit.
+ * N.B. l->l_proc->p_lock must be HELD on entry but will
+ * be released before returning!
+ */
+void
+lwp_thread_cleanup(struct lwp *l)
+{
+	KASSERT(l == curlwp);
+	const lwpid_t tid = l->l___tid;
+
+	KASSERT(mutex_owned(l->l_proc->p_lock));
+
+	if (__predict_false(tid != 0)) {
+		/*
+		 * Drop our thread ID.  This will also unlock
+		 * our proc.
+		 */
+		lwp_threadid_free(l);
+	} else {
+		/*
+		 * No thread cleanup was required; just unlock
+		 * the proc.
+		 */
+		mutex_exit(l->l_proc->p_lock);
+	}
+}
+
 #if defined(DDB)
 #include <machine/pcb.h>
 

Index: src/sys/kern/sys_lwp.c
diff -u src/sys/kern/sys_lwp.c:1.75 src/sys/kern/sys_lwp.c:1.76
--- src/sys/kern/sys_lwp.c:1.75	Thu Jan 30 12:36:38 2020
+++ src/sys/kern/sys_lwp.c	Sat Apr  4 20:20:12 2020
@@ -1,4 +1,4 @@
-/*	$NetBSD: sys_lwp.c,v 1.75 2020/01/30 12:36:38 ad Exp $	*/
+/*	$NetBSD: sys_lwp.c,v 1.76 2020/04/04 20:20:12 thorpej Exp $	*/
 
 /*-
  * Copyright (c) 2001, 2006, 2007, 2008, 2019, 2020 The NetBSD Foundation, Inc.
@@ -35,7 +35,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: sys_lwp.c,v 1.75 2020/01/30 12:36:38 ad Exp $");
+__KERNEL_RCSID(0, "$NetBSD: sys_lwp.c,v 1.76 2020/04/04 20:20:12 thorpej Exp $");
 
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -175,6 +175,14 @@ sys__lwp_self(struct lwp *l, const void 
 }
 
 int
+sys__lwp_gettid(struct lwp *l, const void *v, register_t *retval)
+{
+
+	*retval = lwp_gettid();
+	return 0;
+}
+
+int
 sys__lwp_getprivate(struct lwp *l, const void *v, register_t *retval)
 {
 

Index: src/sys/kern/syscalls.master
diff -u src/sys/kern/syscalls.master:1.297 src/sys/kern/syscalls.master:1.298
--- src/sys/kern/syscalls.master:1.297	Tue Jan 21 02:37:16 2020
+++ src/sys/kern/syscalls.master	Sat Apr  4 20:20:12 2020
@@ -1,4 +1,4 @@
-	$NetBSD: syscalls.master,v 1.297 2020/01/21 02:37:16 pgoyette Exp $
+	$NetBSD: syscalls.master,v 1.298 2020/04/04 20:20:12 thorpej Exp $
 
 ;	@(#)syscalls.master	8.2 (Berkeley) 1/13/94
 
@@ -661,8 +661,8 @@
 				char *name, size_t len); }
 325	STD 		{ int|sys||_lwp_ctl(int features, \
 				struct lwpctl **address); }
-; Syscalls 326-339 reserved for LWP syscalls.
-326	UNIMPL
+326	STD		{ lwptid_t|sys||_lwp_gettid(void); }
+; Syscalls 327-339 reserved for LWP syscalls.
 327	UNIMPL
 328	UNIMPL
 329	UNIMPL

Index: src/sys/sys/lwp.h
diff -u src/sys/sys/lwp.h:1.204 src/sys/sys/lwp.h:1.205
--- src/sys/sys/lwp.h:1.204	Sat Apr  4 06:51:46 2020
+++ src/sys/sys/lwp.h	Sat Apr  4 20:20:12 2020
@@ -1,4 +1,4 @@
-/*	$NetBSD: lwp.h,v 1.204 2020/04/04 06:51:46 maxv Exp $	*/
+/*	$NetBSD: lwp.h,v 1.205 2020/04/04 20:20:12 thorpej Exp $	*/
 
 /*
  * Copyright (c) 2001, 2006, 2007, 2008, 2009, 2010, 2019, 2020
@@ -135,6 +135,24 @@ struct lwp {
 	u_int		l_slptime;	/* l: time since last blocked */
 	bool		l_vforkwaiting;	/* a: vfork() waiting */
 
+	/* User-space synchronization. */
+	uintptr_t	l___reserved;	/* reserved for future use */
+	/*
+	 * The global thread ID has special locking and access
+	 * considerations.  Because many LWPs may never need one,
+	 * global thread IDs are allocated lazily in lwp_gettid().
+	 * l___tid is not bean to be accessed directly unless
+	 * the accessor has specific knowledge that doing so
+	 * is safe.  l___tid is only assigned by the LWP itself.
+	 * Once assigned, it is stable until the LWP exits.
+	 * An LWP assigns its own thread ID unlocked before it
+	 * reaches visibility to the rest of the system, and
+	 * can access its own thread ID unlocked.  But once
+	 * published, it must hold the proc's lock to change
+	 * the value.
+	 */
+	lwpid_t		l___tid;	/* p: global thread id */
+
 #if PCU_UNIT_COUNT > 0
 	struct cpu_info	* volatile l_pcu_cpu[PCU_UNIT_COUNT];
 	uint32_t	l_pcu_valid;
@@ -288,6 +306,7 @@ extern int		maxlwp __read_mostly;	/* max
  */
 #define	LPR_DETACHED	0x00800000 /* Won't be waited for. */
 #define	LPR_CRMOD	0x00000100 /* Credentials modified */
+#define	LPR_DRAINING	0x80000000 /* Draining references before exiting */
 
 /*
  * Mask indicating that there is "exceptional" work to be done on return to
@@ -345,7 +364,7 @@ int	lwp_trylock(lwp_t *);
 void	lwp_addref(lwp_t *);
 void	lwp_delref(lwp_t *);
 void	lwp_delref2(lwp_t *);
-void	lwp_drainrefs(lwp_t *);
+bool	lwp_drainrefs(lwp_t *);
 bool	lwp_alive(lwp_t *);
 lwp_t	*lwp_find_first(proc_t *);
 
@@ -370,6 +389,10 @@ int	lwp_setprivate(lwp_t *, void *);
 int	do_lwp_create(lwp_t *, void *, u_long, lwp_t **, const sigset_t *,
     const stack_t *);
 
+lwpid_t	lwp_gettid(void);
+lwp_t *	lwp_getref_tid(lwpid_t);
+void	lwp_thread_cleanup(lwp_t *);
+
 void	lwpinit_specificdata(void);
 int	lwp_specific_key_create(specificdata_key_t *, specificdata_dtor_t);
 void	lwp_specific_key_delete(specificdata_key_t);

Added files:

Index: src/tests/lib/libc/sys/t_lwp_tid.c
diff -u /dev/null src/tests/lib/libc/sys/t_lwp_tid.c:1.1
--- /dev/null	Sat Apr  4 20:20:13 2020
+++ src/tests/lib/libc/sys/t_lwp_tid.c	Sat Apr  4 20:20:12 2020
@@ -0,0 +1,144 @@
+/* $NetBSD: t_lwp_tid.c,v 1.1 2020/04/04 20:20:12 thorpej Exp $ */
+
+/*-
+ * Copyright (c) 2019, 2020 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__COPYRIGHT("@(#) Copyright (c) 2019\
+ The NetBSD Foundation, inc. All rights reserved.");
+__RCSID("$NetBSD: t_lwp_tid.c,v 1.1 2020/04/04 20:20:12 thorpej Exp $");
+
+#include <sys/mman.h>
+#include <errno.h>
+#include <lwp.h>
+#include <stdio.h>
+#include <time.h>
+
+#include <atf-c.h>
+
+#define	MAX_LWPS	8
+#define	STACK_SIZE	65536
+#define	LWP_TID_MASK	0x3fffffff	/* placeholder */
+
+struct lwp_data {
+	ucontext_t	context;
+	void		*stack_base;
+	lwpid_t		lwpid;
+	lwpid_t		threadid;
+};
+
+struct lwp_data lwp_data[MAX_LWPS];
+lwpid_t master_tid;
+
+static bool
+tid_is_unique(int index, lwpid_t tid)
+{
+	if (tid == master_tid)
+		return false;
+
+	for (int i = 0; i < MAX_LWPS; i++) {
+		if (i == index)
+			continue;
+		if (lwp_data[i].threadid == tid)
+			return false;
+	}
+
+	return true;
+}
+
+static void
+gettid_test(void *arg)
+{
+	struct lwp_data *d = arg;
+
+	d->threadid = _lwp_gettid();
+	_lwp_exit();
+}
+
+ATF_TC(lwp_gettid);
+ATF_TC_HEAD(lwp_gettid, tc)
+{
+	atf_tc_set_md_var(tc, "descr", "checks _lwp_gettid()");
+}
+
+ATF_TC_BODY(lwp_gettid, tc)
+{
+	int i;
+
+	master_tid = _lwp_gettid();
+	ATF_REQUIRE(master_tid != 0);
+	ATF_REQUIRE((master_tid & ~LWP_TID_MASK) == 0);
+
+	/* Initialize our LWP data. */
+	for (i = 0; i < MAX_LWPS; i++) {
+		lwp_data[i].stack_base = mmap(NULL, STACK_SIZE,
+		    PROT_READ | PROT_WRITE,
+		    MAP_ANON | MAP_STACK | MAP_PRIVATE, -1, 0);
+		ATF_REQUIRE(lwp_data[i].stack_base != MAP_FAILED);
+		_lwp_makecontext(&lwp_data[i].context, gettid_test,
+		    &lwp_data[i], NULL, lwp_data[i].stack_base,
+		    STACK_SIZE);
+		lwp_data[i].threadid = 0;
+	}
+
+	/* Now kick off all the LWPs... */
+	for (i = 0; i < MAX_LWPS; i++) {
+		ATF_REQUIRE(_lwp_create(&lwp_data[i].context, 0,
+		    &lwp_data[i].lwpid) == 0);
+		printf("Started LWP %d\n", lwp_data[i].lwpid);
+	}
+
+	/* ...and wait for them to exit. */
+	for (i = 0; i < MAX_LWPS;) {
+		lwpid_t reaped_lwpid;
+
+		int rv = _lwp_wait(0, &reaped_lwpid);
+		if (rv == 0) {
+			printf("Reaped LWP %d\n", reaped_lwpid);
+			i++;
+			continue;
+		} else {
+			ATF_REQUIRE(errno != EDEADLK);
+			continue;
+		}
+	}
+
+	/* Check the TIDs. */
+	for (i = 0; i < MAX_LWPS; i++) {
+		printf("Checking tid[%d] %d (0x%08x)\n", i,
+		    lwp_data[i].threadid, lwp_data[i].threadid);
+		ATF_REQUIRE(lwp_data[i].threadid != 0);
+		ATF_REQUIRE((lwp_data[i].threadid & ~LWP_TID_MASK) == 0);
+		ATF_REQUIRE(tid_is_unique(i, lwp_data[i].threadid));
+	}
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+	ATF_TP_ADD_TC(tp, lwp_gettid);
+
+	return atf_no_error();
+}

Reply via email to