parlib: Add synchronization objects
authorBarret Rhoden <brho@cs.berkeley.edu>
Wed, 5 Apr 2017 15:44:53 +0000 (11:44 -0400)
committerBarret Rhoden <brho@cs.berkeley.edu>
Wed, 3 May 2017 16:13:02 +0000 (12:13 -0400)
commit344c0601f9e65611f69f045163dd2011b88b4b29
treec735318a351bc8f0f5ad56f9e450a412d0a935ca
parente7519e158de8d8aa5fa6612e0ff3476ad6cda4de
parlib: Add synchronization objects

The way synchronization primitives worked before is that a 2LS could
override the default functions (e.g. mutex_lock()) if they don't want the
default scheduling policy (FIFO).

After thinking about it a bit, the 2LS-specific part is the scheduling
decision: given an object (e.g. mutex), which thread should run next?  The
rest of it: uthread_yield(), callbacks, etc, is the same for all 2LSs.  We
can see this to some extent already with how recursive mutexes aren't part
of the 2LS interface.  It will become more clear as we add timeouts to
mutexes and CVs; that work just need to be done at the uthread level.  The
2LS just picks the next uthread.

This work is part of the GCC generic threading interface.  One of the
things required from that is a static initializer for mutexes.  To deal
with that, we'll need some fields in a mutex that uthread.c can access (for
a once_ctl).  That's part of the reason why mutex allocation will be a
uthread job, and the 2LS just does the sync_obj.  That sync obj can be used
for all sorts of synchronization primitives - at least mutexes and CVs for
now, maybe more in the future.

I considered allowing static allocation (but not initialization) of 2LS
sync_objs.  If we want to do that in the future, we can do so if we set an
upper bound on the size of a 2LS sync obj, and use a uint8_t
sync_store[SOME_AMT] that all 2LSs cast to their object.  It's probably not
worth it at this point.

There's also a minor issue with whether or not get_next() and friends
returns a uthread or another object.  If we wanted to use this in event.c
(which we don't, for other reasons at the moment), we might want to get the
controller back, not the uthread.  It's a layer of indirection.  That would
require storing a pointer with the per-uthread part of the sync structure
(e.g. TAILQ_ENTRY).  Right now, the implied pointer is the uthread, which
we can easily access since the per-uthread part is embedded in the uthread
(or 2LS thread).  We could just store another pointer next to it, but so
far it's not worth it / necessary.

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