aboutsummaryrefslogtreecommitdiff
path: root/src/runtime/rt1_amd64_linux.c
diff options
context:
space:
mode:
authorRuss Cox <rsc@golang.org>2008-08-04 16:43:49 -0700
committerRuss Cox <rsc@golang.org>2008-08-04 16:43:49 -0700
commitd28acc42ec0f8dff9471e4663cfe55aa5da86656 (patch)
tree9bbd3f46848f10a65f701fb12cbddadf5e59b114 /src/runtime/rt1_amd64_linux.c
parentf439299035bbdb4ac7c1c684214b7bf8b4347474 (diff)
downloadgo-d28acc42ec0f8dff9471e4663cfe55aa5da86656.tar.xz
first cut at multithreading. works on Linux.
* kick off new os procs (machs) as needed * add sys·sleep for testing * add Lock, Rendez * properly lock mal, sys·newproc, scheduler * linux syscall arg #4 is in R10, not CX * chans are not multithread-safe yet * multithreading disabled by default; set $gomaxprocs=2 (or 1000) to turn it on This should build on OS X but may not. Rob and I will fix soon after submitting. TBR=r OCL=13784 CL=13842
Diffstat (limited to 'src/runtime/rt1_amd64_linux.c')
-rw-r--r--src/runtime/rt1_amd64_linux.c235
1 files changed, 234 insertions, 1 deletions
diff --git a/src/runtime/rt1_amd64_linux.c b/src/runtime/rt1_amd64_linux.c
index 99700fdf89..df0274f76a 100644
--- a/src/runtime/rt1_amd64_linux.c
+++ b/src/runtime/rt1_amd64_linux.c
@@ -3,6 +3,7 @@
// license that can be found in the LICENSE file.
#include "runtime.h"
+#include "amd64_linux.h"
#include "signals.h"
/* From /usr/include/asm-x86_64/sigcontext.h */
@@ -161,7 +162,7 @@ sighandler(int32 sig, siginfo* info, void** context)
}
-sigaction a;
+static sigaction a;
void
initsig(void)
@@ -177,3 +178,235 @@ initsig(void)
sys·rt_sigaction(i, &a, (void*)0, 8);
}
}
+
+// Linux futex. The simple cases really are simple:
+//
+// futex(addr, FUTEX_WAIT, val, duration, _, _)
+// Inside the kernel, atomically check that *addr == val
+// and go to sleep for at most duration.
+//
+// futex(addr, FUTEX_WAKE, val, _, _, _)
+// Wake up at least val procs sleeping on addr.
+//
+// (Of course, they have added more complicated things since then.)
+
+enum
+{
+ FUTEX_WAIT = 0,
+ FUTEX_WAKE = 1,
+
+ EINTR = 4,
+ EAGAIN = 11,
+};
+
+// TODO(rsc) I tried using 1<<40 here but it woke up (-ETIMEDOUT).
+// I wonder if the timespec that gets to the kernel
+// actually has two 32-bit numbers in it, so that
+// a 64-bit 1<<40 ends up being 0 seconds,
+// 1<<8 nanoseconds.
+static struct timespec longtime =
+{
+ 1<<30, // 34 years
+ 0
+};
+
+static void
+efutex(uint32 *addr, int32 op, int32 val, struct timespec *ts)
+{
+ int64 ret;
+
+again:
+ ret = futex(addr, op, val, ts, nil, 0);
+
+ // These happen when you use a debugger, among other times.
+ if(ret == -EAGAIN || ret == -EINTR){
+ // If we were sleeping, it's okay to wake up early.
+ if(op == FUTEX_WAIT)
+ return;
+
+ // If we were waking someone up, we don't know
+ // whether that succeeded, so wake someone else up too.
+ if(op == FUTEX_WAKE){
+prints("futexwake ");
+sys·printint(ret);
+prints("\n");
+ goto again;
+ }
+ }
+
+ if(ret < 0){
+ prints("futex error addr=");
+ sys·printpointer(addr);
+ prints(" op=");
+ sys·printint(op);
+ prints(" val=");
+ sys·printint(val);
+ prints(" ts=");
+ sys·printpointer(ts);
+ prints(" returned ");
+ sys·printint(-ret);
+ prints("\n");
+ *(int32*)101 = 202;
+ }
+}
+
+// Lock and unlock.
+// A zeroed Lock is unlocked (no need to initialize each lock).
+// The l->key is either 0 (unlocked), 1 (locked), or >=2 (contended).
+
+void
+lock(Lock *l)
+{
+ uint32 v;
+
+ if(l->key != 0) *(int32*)0x1001 = 0x1001;
+ l->key = 1;
+ return;
+
+ for(;;){
+ // Try for lock. If we incremented it from 0 to 1, we win.
+ if((v=xadd(&l->key, 1)) == 1)
+ return;
+
+ // We lose. It was already >=1 and is now >=2.
+ // Use futex to atomically check that the value is still
+ // what we think it is and go to sleep.
+ efutex(&l->key, FUTEX_WAIT, v, &longtime);
+ }
+}
+
+void
+unlock(Lock *l)
+{
+ uint32 v;
+
+ if(l->key != 1) *(int32*)0x1002 = 0x1002;
+ l->key = 0;
+ return;
+
+ // Unlock the lock. If we decremented from 1 to 0, wasn't contended.
+ if((v=xadd(&l->key, -1)) == 0)
+ return;
+
+ // The lock was contended. Mark it as unlocked and wake a waiter.
+ l->key = 0;
+ efutex(&l->key, FUTEX_WAKE, 1, nil);
+}
+
+// Sleep and wakeup (see description in runtime.h)
+
+void
+rsleep(Rendez *r)
+{
+ // Record that we're about to go to sleep and drop the lock.
+ r->sleeping = 1;
+ unlock(r->l);
+
+ // Go to sleep if r->sleeping is still 1.
+ efutex(&r->sleeping, FUTEX_WAIT, 1, &longtime);
+
+ // Reacquire the lock.
+ lock(r->l);
+}
+
+void
+rwakeup(Rendez *r)
+{
+ if(!r->sleeping)
+ return;
+
+ // Clear the sleeping flag in case sleeper
+ // is between unlock and futex.
+ r->sleeping = 0;
+
+ // Wake up if actually made it to sleep.
+ efutex(&r->sleeping, FUTEX_WAKE, 1, nil);
+}
+
+// Like rwakeup(r), unlock(r->l), but drops the lock before
+// waking the other proc. This reduces bouncing back and forth
+// in the scheduler: the first thing the other proc wants to do
+// is acquire r->l, so it helps to unlock it before we wake him.
+void
+rwakeupandunlock(Rendez *r)
+{
+ int32 wassleeping;
+
+ if(!r->sleeping){
+ unlock(r->l);
+ return;
+ }
+
+ r->sleeping = 0;
+ unlock(r->l);
+ efutex(&r->sleeping, FUTEX_WAKE, 1, nil);
+}
+
+enum
+{
+ CLONE_VM = 0x100,
+ CLONE_FS = 0x200,
+ CLONE_FILES = 0x400,
+ CLONE_SIGHAND = 0x800,
+ CLONE_PTRACE = 0x2000,
+ CLONE_VFORK = 0x4000,
+ CLONE_PARENT = 0x8000,
+ CLONE_THREAD = 0x10000,
+ CLONE_NEWNS = 0x20000,
+ CLONE_SYSVSEM = 0x40000,
+ CLONE_SETTLS = 0x80000,
+ CLONE_PARENT_SETTID = 0x100000,
+ CLONE_CHILD_CLEARTID = 0x200000,
+ CLONE_UNTRACED = 0x800000,
+ CLONE_CHILD_SETTID = 0x1000000,
+ CLONE_STOPPED = 0x2000000,
+ CLONE_NEWUTS = 0x4000000,
+ CLONE_NEWIPC = 0x8000000,
+};
+
+void
+newosproc(M *mm, G *gg, void *stk, void (*fn)(void*), void *arg)
+{
+ int64 ret;
+ int32 flags;
+
+ flags = CLONE_PARENT /* getppid doesn't change in child */
+ | CLONE_VM /* share memory */
+ | CLONE_FS /* share cwd, etc */
+ | CLONE_FILES /* share fd table */
+ | CLONE_SIGHAND /* share sig handler table */
+ | CLONE_PTRACE /* revisit - okay for now */
+ | CLONE_THREAD /* revisit - okay for now */
+ ;
+
+ if(0){
+ prints("newosproc stk=");
+ sys·printpointer(stk);
+ prints(" mm=");
+ sys·printpointer(mm);
+ prints(" gg=");
+ sys·printpointer(gg);
+ prints(" fn=");
+ sys·printpointer(fn);
+ prints(" arg=");
+ sys·printpointer(arg);
+ prints(" clone=");
+ sys·printpointer(clone);
+ prints("\n");
+ }
+
+ ret = clone(flags, stk, mm, gg, fn, arg);
+ if(ret < 0)
+ *(int32*)123 = 123;
+}
+
+void
+sys·sleep(int64 ms)
+{
+ struct timeval tv;
+
+ tv.tv_sec = ms/1000;
+ tv.tv_usec = ms%1000 * 1000;
+ select(0, nil, nil, nil, &tv);
+}
+