diff options
| author | Russ Cox <rsc@golang.org> | 2008-08-04 16:43:49 -0700 |
|---|---|---|
| committer | Russ Cox <rsc@golang.org> | 2008-08-04 16:43:49 -0700 |
| commit | d28acc42ec0f8dff9471e4663cfe55aa5da86656 (patch) | |
| tree | 9bbd3f46848f10a65f701fb12cbddadf5e59b114 /src/runtime/rt1_amd64_linux.c | |
| parent | f439299035bbdb4ac7c1c684214b7bf8b4347474 (diff) | |
| download | go-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.c | 235 |
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); +} + |
