parlib: Implement join/detach() for all uthreads
authorBarret Rhoden <brho@cs.berkeley.edu>
Tue, 11 Apr 2017 19:28:50 +0000 (15:28 -0400)
committerBarret Rhoden <brho@cs.berkeley.edu>
Wed, 3 May 2017 16:13:02 +0000 (12:13 -0400)
commit55925c0effc1beff3ef02864c611fd0861ca332e
tree72efd49c6a42f262e38dd8dbb6ff7f7cc7722aed
parentde830e89a07c2c95bf8a5d0fc7c53289cb544363
parlib: Implement join/detach() for all uthreads

Previously, it was up to specific 2LSs, such as pthreads, to implement
join() and detach() if they wanted it.  GCC needs it for the C++ threads,
and it might be helpful for all 2LSs.  Now, join/detach() are handled at
the uthread layer.

These functions only kick in if a thread exits.  That is still controlled
by the 2LS.  Previously, exits were done with a yield callback.  Instead,
2LSs will call uth_2ls_thread_exit(), and the callback work is done in the
2LS->thread_exited() op.  If a thread never exits, such as certain types of
VM threads, then that op will not be called.  And, as one would expect, if
you join() a thread that never exits, you'll block forever.

The implementation of join/detach/exit differs from the original pthreads
implementation.  The pthread implementation had a race on the detach state,
where a bad combination of detacher/joiner/exiter could be corrupted.
CAS-ing on the state solved a lot of those problems.

The other major improvement was adding a parallel join.  Since this join
will work for all 2LSs, I'd like it to work for some of the more exotic
schedulers.  Parallel join is a common operation in some threading
libraries and frameworks (openmp, lithe, etc).  My implementation uses
krefs so that the joiner is only woken up once for all N threads, instead
of up to N times.  The kref also helps with races between concurrent joins
and exits.

Signed-off-by: Barret Rhoden <brho@cs.berkeley.edu>
tests/pthread_test.c
user/parlib/include/parlib/uthread.h
user/parlib/thread0_sched.c
user/parlib/uthread.c
user/pthread/pthread.c
user/pthread/pthread.h
user/vmm/sched.c