parlib: Have exactly one specific 2LS
authorBarret Rhoden <brho@cs.berkeley.edu>
Wed, 3 May 2017 14:08:45 +0000 (10:08 -0400)
committerBarret Rhoden <brho@cs.berkeley.edu>
Wed, 3 May 2017 16:13:02 +0000 (12:13 -0400)
commit0efa97f895a6586a1fb4de8009c352d36e4ea870
treec978eb59e9ac2190e059d39a634be516f19cfe85
parentbd84232924dd42e23ff347621eb414293efa6413
parlib: Have exactly one specific 2LS

Processes in Akaros are given control over their cores, but with this
control comes a bootstrapping problem.  For very early code, such as ld.so
and parts of glibc, the kernel takes care of a few things.  This time is
referred to as "early-SCP context."  Eventually, the process is capable of
handling itself, and it takes over.

Previously, we would make the transition from the early-SCP to the thread0
scheduler, and then if a "real" 2LS (e.g. pthreads or vmm) was linked in,
it's constructor would switch us from thread0 to the real 2LS.

There's a bunch of problems with this.
- What's to stop you from having multiple 2LSs linked in, and then only the
  'last ctor wins'?
- It's very tricky to transition from thread0 to another 2LS.  We've had a
  bunch of bugs and tricky code related to races, blocking calls, and other
things during the transition.  Going from early-SCP to a 2LS is fine.  But
once the process has taken control, switching out schedulers is very
tricky.
- If glibc (or any code, like a ctor) makes a 2LS op after thread0 is
  running, but before the next 2LS takes over, the object we're operating
on will get confused.  For instance, consider a sync object initialized for
thread0, but then locked while pthreads is the 2LS.  Pthreads would see a
sync object that it didn't initialize.

For all these reasons, and especially the latter, it's time to go back to
the older style of 2LS ops: there's a weak version of the sched ops, which
is thread0s.  That will be the default 2LS unless another one is used.  If
you link in another 2LS (e.g. pthreads or vmm), then that will take over.
This will also catch issues of linking in multiple 2LSs.

Now that we know the specific 2LS statically, uthread_lib_init() can call
the 2LS's initialization function.  Previously we were forcing the 2LS
ctors to make sure uthread code ran first, but there was actually nothing
making sure the 2LSs ctor ran first.  Imagine a ctor in your application
that uses the 2LS via the GCC/C++ threading.  ld or whatever can figure out
that it needs parlib and run parlib's ctors first.  But it won't know that
e.g. pthread's ctor needs to run.  Now, that's not a problem.  The
dependency is on GCC/C++ threads -> uthreads, and the implied dependency
from uthreads to the real 2LS is sorted out by calling the 2LS's
sched_op->sched_init().

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