diff options
Diffstat (limited to 'src/runtime')
-rw-r--r-- | src/runtime/asm_386.s | 14 | ||||
-rw-r--r-- | src/runtime/asm_amd64.s | 14 | ||||
-rw-r--r-- | src/runtime/asm_arm.s | 15 | ||||
-rw-r--r-- | src/runtime/crash_cgo_test.go | 52 | ||||
-rw-r--r-- | src/runtime/crash_test.go | 17 | ||||
-rw-r--r-- | src/runtime/debug/garbage.go | 30 | ||||
-rw-r--r-- | src/runtime/debug/garbage_test.go | 13 | ||||
-rw-r--r-- | src/runtime/extern.go | 6 | ||||
-rw-r--r-- | src/runtime/funcdata.h | 3 | ||||
-rw-r--r-- | src/runtime/malloc.h | 3 | ||||
-rw-r--r-- | src/runtime/mem.go | 3 | ||||
-rw-r--r-- | src/runtime/mgc0.c | 27 | ||||
-rw-r--r-- | src/runtime/print1.go | 27 | ||||
-rw-r--r-- | src/runtime/proc.c | 20 | ||||
-rw-r--r-- | src/runtime/rt0_nacl_amd64p32.s | 2 | ||||
-rw-r--r-- | src/runtime/runtime.c | 4 | ||||
-rw-r--r-- | src/runtime/runtime.h | 2 | ||||
-rw-r--r-- | src/runtime/stack.c | 4 | ||||
-rw-r--r-- | src/runtime/sys_nacl_amd64p32.s | 6 | ||||
-rw-r--r-- | src/runtime/time.go | 29 |
20 files changed, 237 insertions, 54 deletions
diff --git a/src/runtime/asm_386.s b/src/runtime/asm_386.s index d0b3969bd..2d102b273 100644 --- a/src/runtime/asm_386.s +++ b/src/runtime/asm_386.s @@ -732,6 +732,20 @@ needm: MOVL g(CX), BP MOVL g_m(BP), BP + // Set m->sched.sp = SP, so that if a panic happens + // during the function we are about to execute, it will + // have a valid SP to run on the g0 stack. + // The next few lines (after the havem label) + // will save this SP onto the stack and then write + // the same SP back to m->sched.sp. That seems redundant, + // but if an unrecovered panic happens, unwindm will + // restore the g->sched.sp from the stack location + // and then onM will try to use it. If we don't set it here, + // that restored SP will be uninitialized (typically 0) and + // will not be usable. + MOVL m_g0(BP), SI + MOVL SP, (g_sched+gobuf_sp)(SI) + havem: // Now there's a valid m, and we're running on its m->g0. // Save current m->g0->sched.sp on stack and then set it to SP. diff --git a/src/runtime/asm_amd64.s b/src/runtime/asm_amd64.s index 7a0fdfa73..ac9c58cf3 100644 --- a/src/runtime/asm_amd64.s +++ b/src/runtime/asm_amd64.s @@ -717,6 +717,20 @@ needm: get_tls(CX) MOVQ g(CX), BP MOVQ g_m(BP), BP + + // Set m->sched.sp = SP, so that if a panic happens + // during the function we are about to execute, it will + // have a valid SP to run on the g0 stack. + // The next few lines (after the havem label) + // will save this SP onto the stack and then write + // the same SP back to m->sched.sp. That seems redundant, + // but if an unrecovered panic happens, unwindm will + // restore the g->sched.sp from the stack location + // and then onM will try to use it. If we don't set it here, + // that restored SP will be uninitialized (typically 0) and + // will not be usable. + MOVQ m_g0(BP), SI + MOVQ SP, (g_sched+gobuf_sp)(SI) havem: // Now there's a valid m, and we're running on its m->g0. diff --git a/src/runtime/asm_arm.s b/src/runtime/asm_arm.s index 8942b11ac..9a58fdc51 100644 --- a/src/runtime/asm_arm.s +++ b/src/runtime/asm_arm.s @@ -556,6 +556,21 @@ TEXT ·cgocallback_gofunc(SB),NOSPLIT,$8-12 MOVW $runtime·needm(SB), R0 BL (R0) + // Set m->sched.sp = SP, so that if a panic happens + // during the function we are about to execute, it will + // have a valid SP to run on the g0 stack. + // The next few lines (after the havem label) + // will save this SP onto the stack and then write + // the same SP back to m->sched.sp. That seems redundant, + // but if an unrecovered panic happens, unwindm will + // restore the g->sched.sp from the stack location + // and then onM will try to use it. If we don't set it here, + // that restored SP will be uninitialized (typically 0) and + // will not be usable. + MOVW g_m(g), R8 + MOVW m_g0(R8), R3 + MOVW R13, (g_sched+gobuf_sp)(R3) + havem: MOVW g_m(g), R8 MOVW R8, savedm-4(SP) diff --git a/src/runtime/crash_cgo_test.go b/src/runtime/crash_cgo_test.go index 4ff0084c2..5958ad891 100644 --- a/src/runtime/crash_cgo_test.go +++ b/src/runtime/crash_cgo_test.go @@ -8,6 +8,7 @@ package runtime_test import ( "runtime" + "strings" "testing" ) @@ -34,6 +35,17 @@ func TestCgoTraceback(t *testing.T) { } } +func TestCgoExternalThreadPanic(t *testing.T) { + if runtime.GOOS == "windows" || runtime.GOOS == "plan9" { + t.Skipf("no pthreads on %s", runtime.GOOS) + } + got := executeTest(t, cgoExternalThreadPanicSource, nil, "main.c", cgoExternalThreadPanicC) + want := "panic: BOOM" + if !strings.Contains(got, want) { + t.Fatalf("want failure containing %q. output:\n%s\n", want, got) + } +} + const cgoSignalDeadlockSource = ` package main @@ -117,3 +129,43 @@ func main() { fmt.Printf("OK\n") } ` + +const cgoExternalThreadPanicSource = ` +package main + +// void start(void); +import "C" + +func main() { + C.start() + select {} +} + +//export gopanic +func gopanic() { + panic("BOOM") +} +` + +const cgoExternalThreadPanicC = ` +#include <stdlib.h> +#include <stdio.h> +#include <pthread.h> + +void gopanic(void); + +static void* +die(void* x) +{ + gopanic(); + return 0; +} + +void +start(void) +{ + pthread_t t; + if(pthread_create(&t, 0, die, 0) != 0) + printf("pthread_create failed\n"); +} +` diff --git a/src/runtime/crash_test.go b/src/runtime/crash_test.go index 783b4c48f..211a0476f 100644 --- a/src/runtime/crash_test.go +++ b/src/runtime/crash_test.go @@ -31,7 +31,7 @@ func testEnv(cmd *exec.Cmd) *exec.Cmd { return cmd } -func executeTest(t *testing.T, templ string, data interface{}) string { +func executeTest(t *testing.T, templ string, data interface{}, extra ...string) string { switch runtime.GOOS { case "android", "nacl": t.Skipf("skipping on %s", runtime.GOOS) @@ -61,7 +61,20 @@ func executeTest(t *testing.T, templ string, data interface{}) string { t.Fatalf("failed to close file: %v", err) } - got, _ := testEnv(exec.Command("go", "run", src)).CombinedOutput() + for i := 0; i < len(extra); i += 2 { + if err := ioutil.WriteFile(filepath.Join(dir, extra[i]), []byte(extra[i+1]), 0666); err != nil { + t.Fatal(err) + } + } + + cmd := exec.Command("go", "build", "-o", "a.exe") + cmd.Dir = dir + out, err := testEnv(cmd).CombinedOutput() + if err != nil { + t.Fatalf("building source: %v\n%s", err, out) + } + + got, _ := testEnv(exec.Command(filepath.Join(dir, "a.exe"))).CombinedOutput() return string(got) } diff --git a/src/runtime/debug/garbage.go b/src/runtime/debug/garbage.go index 30994f219..4a77dcfcd 100644 --- a/src/runtime/debug/garbage.go +++ b/src/runtime/debug/garbage.go @@ -16,6 +16,7 @@ type GCStats struct { NumGC int64 // number of garbage collections PauseTotal time.Duration // total pause for all collections Pause []time.Duration // pause history, most recent first + PauseEnd []time.Time // pause end times history, most recent first PauseQuantiles []time.Duration } @@ -30,25 +31,36 @@ type GCStats struct { func ReadGCStats(stats *GCStats) { // Create a buffer with space for at least two copies of the // pause history tracked by the runtime. One will be returned - // to the caller and the other will be used as a temporary buffer - // for computing quantiles. + // to the caller and the other will be used as transfer buffer + // for end times history and as a temporary buffer for + // computing quantiles. const maxPause = len(((*runtime.MemStats)(nil)).PauseNs) - if cap(stats.Pause) < 2*maxPause { - stats.Pause = make([]time.Duration, 2*maxPause) + if cap(stats.Pause) < 2*maxPause+3 { + stats.Pause = make([]time.Duration, 2*maxPause+3) } - // readGCStats fills in the pause history (up to maxPause entries) - // and then three more: Unix ns time of last GC, number of GC, - // and total pause time in nanoseconds. Here we depend on the - // fact that time.Duration's native unit is nanoseconds, so the - // pauses and the total pause time do not need any conversion. + // readGCStats fills in the pause and end times histories (up to + // maxPause entries) and then three more: Unix ns time of last GC, + // number of GC, and total pause time in nanoseconds. Here we + // depend on the fact that time.Duration's native unit is + // nanoseconds, so the pauses and the total pause time do not need + // any conversion. readGCStats(&stats.Pause) n := len(stats.Pause) - 3 stats.LastGC = time.Unix(0, int64(stats.Pause[n])) stats.NumGC = int64(stats.Pause[n+1]) stats.PauseTotal = stats.Pause[n+2] + n /= 2 // buffer holds pauses and end times stats.Pause = stats.Pause[:n] + if cap(stats.PauseEnd) < maxPause { + stats.PauseEnd = make([]time.Time, 0, maxPause) + } + stats.PauseEnd = stats.PauseEnd[:0] + for _, ns := range stats.Pause[n : n+n] { + stats.PauseEnd = append(stats.PauseEnd, time.Unix(0, int64(ns))) + } + if len(stats.PauseQuantiles) > 0 { if n == 0 { for i := range stats.PauseQuantiles { diff --git a/src/runtime/debug/garbage_test.go b/src/runtime/debug/garbage_test.go index 149bafc6f..54c33bd4f 100644 --- a/src/runtime/debug/garbage_test.go +++ b/src/runtime/debug/garbage_test.go @@ -70,6 +70,19 @@ func TestReadGCStats(t *testing.T) { t.Errorf("stats.PauseQuantiles[%d]=%d > stats.PauseQuantiles[%d]=%d", i, q[i], i+1, q[i+1]) } } + + // compare memory stats with gc stats: + if len(stats.PauseEnd) != n { + t.Fatalf("len(stats.PauseEnd) = %d, want %d", len(stats.PauseEnd), n) + } + off := (int(mstats.NumGC) + len(mstats.PauseEnd) - 1) % len(mstats.PauseEnd) + for i := 0; i < n; i++ { + dt := stats.PauseEnd[i] + if dt.UnixNano() != int64(mstats.PauseEnd[off]) { + t.Errorf("stats.PauseEnd[%d] = %d, want %d", i, dt, mstats.PauseEnd[off]) + } + off = (off + len(mstats.PauseEnd) - 1) % len(mstats.PauseEnd) + } } var big = make([]byte, 1<<20) diff --git a/src/runtime/extern.go b/src/runtime/extern.go index b8db5d0c4..1b8052bb5 100644 --- a/src/runtime/extern.go +++ b/src/runtime/extern.go @@ -39,6 +39,12 @@ a comma-separated list of name=val pairs. Supported names are: gcdead: setting gcdead=1 causes the garbage collector to clobber all stack slots that it thinks are dead. + invalidptr: defaults to invalidptr=1, causing the garbage collector and stack + copier to crash the program if an invalid pointer value (for example, 1) + is found in a pointer-typed location. Setting invalidptr=0 disables this check. + This should only be used as a temporary workaround to diagnose buggy code. + The real fix is to not store integers in pointer-typed locations. + scheddetail: setting schedtrace=X and scheddetail=1 causes the scheduler to emit detailed multiline info every X milliseconds, describing state of the scheduler, processors, threads and goroutines. diff --git a/src/runtime/funcdata.h b/src/runtime/funcdata.h index a2667a4c0..d6c14fcb4 100644 --- a/src/runtime/funcdata.h +++ b/src/runtime/funcdata.h @@ -28,6 +28,9 @@ // defines the pointer map for the function's arguments. // GO_ARGS should be the first instruction in a function that uses it. // It can be omitted if there are no arguments at all. +// GO_ARGS is inserted implicitly by the linker for any function +// that also has a Go prototype and therefore is usually not necessary +// to write explicitly. #define GO_ARGS FUNCDATA $FUNCDATA_ArgsPointerMaps, go_args_stackmap(SB) // GO_RESULTS_INITIALIZED indicates that the assembly function diff --git a/src/runtime/malloc.h b/src/runtime/malloc.h index d1930756a..adb8d3d67 100644 --- a/src/runtime/malloc.h +++ b/src/runtime/malloc.h @@ -267,7 +267,8 @@ struct MStats uint64 next_gc; // next GC (in heap_alloc time) uint64 last_gc; // last GC (in absolute time) uint64 pause_total_ns; - uint64 pause_ns[256]; + uint64 pause_ns[256]; // circular buffer of recent GC pause lengths + uint64 pause_end[256]; // circular buffer of recent GC end times (nanoseconds since 1970) uint32 numgc; bool enablegc; bool debuggc; diff --git a/src/runtime/mem.go b/src/runtime/mem.go index 438f22ec0..e6f1eb0e6 100644 --- a/src/runtime/mem.go +++ b/src/runtime/mem.go @@ -44,7 +44,8 @@ type MemStats struct { NextGC uint64 // next collection will happen when HeapAlloc ≥ this amount LastGC uint64 // end time of last collection (nanoseconds since 1970) PauseTotalNs uint64 - PauseNs [256]uint64 // circular buffer of recent GC pause times, most recent at [(NumGC+255)%256] + PauseNs [256]uint64 // circular buffer of recent GC pause durations, most recent at [(NumGC+255)%256] + PauseEnd [256]uint64 // circular buffer of recent GC pause end times NumGC uint32 EnableGC bool DebugGC bool diff --git a/src/runtime/mgc0.c b/src/runtime/mgc0.c index 02f7eba12..e5b6870c6 100644 --- a/src/runtime/mgc0.c +++ b/src/runtime/mgc0.c @@ -330,7 +330,7 @@ scanblock(byte *b, uintptr n, byte *ptrmask) if(obj == nil) continue; if(obj < arena_start || obj >= arena_used) { - if((uintptr)obj < PhysPageSize) { + if((uintptr)obj < PhysPageSize && runtime·invalidptr) { s = nil; goto badobj; } @@ -375,7 +375,7 @@ scanblock(byte *b, uintptr n, byte *ptrmask) else runtime·printf(" span=%p-%p-%p state=%d\n", (uintptr)s->start<<PageShift, s->limit, (uintptr)(s->start+s->npages)<<PageShift, s->state); if(ptrmask != nil) - runtime·throw("bad pointer"); + runtime·throw("invalid heap pointer"); // Add to badblock list, which will cause the garbage collection // to keep repeating until it has traced the chain of pointers // leading to obj all the way back to a root. @@ -1459,6 +1459,7 @@ gc(struct gc_args *args) t4 = runtime·nanotime(); runtime·atomicstore64(&mstats.last_gc, runtime·unixnanotime()); // must be Unix time to make sense to user mstats.pause_ns[mstats.numgc%nelem(mstats.pause_ns)] = t4 - t0; + mstats.pause_end[mstats.numgc%nelem(mstats.pause_end)] = t4; mstats.pause_total_ns += t4 - t0; mstats.numgc++; if(mstats.debuggc) @@ -1572,7 +1573,7 @@ readgcstats_m(void) { Slice *pauses; uint64 *p; - uint32 i, n; + uint32 i, j, n; pauses = g->m->ptrarg[0]; g->m->ptrarg[0] = nil; @@ -1581,25 +1582,29 @@ readgcstats_m(void) if(pauses->cap < nelem(mstats.pause_ns)+3) runtime·throw("runtime: short slice passed to readGCStats"); - // Pass back: pauses, last gc (absolute time), number of gc, total pause ns. + // Pass back: pauses, pause ends, last gc (absolute time), number of gc, total pause ns. p = (uint64*)pauses->array; runtime·lock(&runtime·mheap.lock); + n = mstats.numgc; if(n > nelem(mstats.pause_ns)) n = nelem(mstats.pause_ns); - + // The pause buffer is circular. The most recent pause is at // pause_ns[(numgc-1)%nelem(pause_ns)], and then backward // from there to go back farther in time. We deliver the times // most recent first (in p[0]). - for(i=0; i<n; i++) - p[i] = mstats.pause_ns[(mstats.numgc-1-i)%nelem(mstats.pause_ns)]; + for(i=0; i<n; i++) { + j = (mstats.numgc-1-i)%nelem(mstats.pause_ns); + p[i] = mstats.pause_ns[j]; + p[n+i] = mstats.pause_end[j]; + } - p[n] = mstats.last_gc; - p[n+1] = mstats.numgc; - p[n+2] = mstats.pause_total_ns; + p[n+n] = mstats.last_gc; + p[n+n+1] = mstats.numgc; + p[n+n+2] = mstats.pause_total_ns; runtime·unlock(&runtime·mheap.lock); - pauses->len = n+3; + pauses->len = n+n+3; } void diff --git a/src/runtime/print1.go b/src/runtime/print1.go index 0fa1fb63c..8f8268873 100644 --- a/src/runtime/print1.go +++ b/src/runtime/print1.go @@ -19,32 +19,17 @@ func bytes(s string) (ret []byte) { return } -// goprintf is the function call that is actually deferred when you write -// defer print(...) -// It is otherwise unused. In particular it is not used for ordinary prints. -// Right now a dynamically allocated string that is being passed as an -// argument is invisible to the garbage collector and might be collected -// if that argument list is the only reference. For now we ignore that possibility. -// To fix, we should change to defer a call to vprintf with a pointer to -// an argument list on the stack, stored in an appropriately typed -// struct. golang.org/issue/8614. -//go:nosplit -func goprintf(s string) { - vprintf(s, add(unsafe.Pointer(&s), unsafe.Sizeof(s))) -} - -// printf is only called from C code. It has the same problem as goprintf -// with strings possibly being collected from underneath. -// However, the runtime never prints dynamically allocated -// Go strings using printf. The strings it prints come from the symbol -// and type tables. +// printf is only called from C code. It has no type information for the args, +// but C stacks are ignored by the garbage collector anyway, so having +// type information would not add anything. //go:nosplit func printf(s *byte) { vprintf(gostringnocopy(s), add(unsafe.Pointer(&s), unsafe.Sizeof(s))) } -// sprintf is only called from C code. -// It has the same problem as goprintf. +// sprintf is only called from C code. It has no type information for the args, +// but C stacks are ignored by the garbage collector anyway, so having +// type information would not add anything. //go:nosplit func snprintf(dst *byte, n int32, s *byte) { buf := (*[1 << 30]byte)(unsafe.Pointer(dst))[0:n:n] diff --git a/src/runtime/proc.c b/src/runtime/proc.c index 11af6a1ea..31c62d43f 100644 --- a/src/runtime/proc.c +++ b/src/runtime/proc.c @@ -2758,6 +2758,8 @@ static void checkdead(void) { G *gp; + P *p; + M *mp; int32 run, grunning, s; uintptr i; @@ -2799,6 +2801,24 @@ checkdead(void) runtime·unlock(&runtime·allglock); if(grunning == 0) // possible if main goroutine calls runtime·Goexit() runtime·throw("no goroutines (main called runtime.Goexit) - deadlock!"); + + // Maybe jump time forward for playground. + if((gp = runtime·timejump()) != nil) { + runtime·casgstatus(gp, Gwaiting, Grunnable); + globrunqput(gp); + p = pidleget(); + if(p == nil) + runtime·throw("checkdead: no p for timer"); + mp = mget(); + if(mp == nil) + newm(nil, p); + else { + mp->nextp = p; + runtime·notewakeup(&mp->park); + } + return; + } + g->m->throwing = -1; // do not dump full stacks runtime·throw("all goroutines are asleep - deadlock!"); } diff --git a/src/runtime/rt0_nacl_amd64p32.s b/src/runtime/rt0_nacl_amd64p32.s index d8703dc0f..54e4b1de8 100644 --- a/src/runtime/rt0_nacl_amd64p32.s +++ b/src/runtime/rt0_nacl_amd64p32.s @@ -26,5 +26,5 @@ TEXT _rt0_amd64p32_nacl(SB),NOSPLIT,$16 TEXT main(SB),NOSPLIT,$0 // Uncomment for fake time like on Go Playground. //MOVQ $1257894000000000000, AX - //MOVQ AX, runtime·timens(SB) + //MOVQ AX, runtime·faketime(SB) JMP runtime·rt0_go(SB) diff --git a/src/runtime/runtime.c b/src/runtime/runtime.c index d984983ce..f19f8e4be 100644 --- a/src/runtime/runtime.c +++ b/src/runtime/runtime.c @@ -282,9 +282,13 @@ struct DbgVar int32* value; }; +// Do we report invalid pointers found during stack or heap scans? +int32 runtime·invalidptr = 1; + #pragma dataflag NOPTR /* dbgvar has no heap pointers */ static DbgVar dbgvar[] = { {"allocfreetrace", &runtime·debug.allocfreetrace}, + {"invalidptr", &runtime·invalidptr}, {"efence", &runtime·debug.efence}, {"gctrace", &runtime·debug.gctrace}, {"gcdead", &runtime·debug.gcdead}, diff --git a/src/runtime/runtime.h b/src/runtime/runtime.h index a84a32525..2a6074006 100644 --- a/src/runtime/runtime.h +++ b/src/runtime/runtime.h @@ -657,6 +657,8 @@ enum { byte* runtime·startup_random_data; uint32 runtime·startup_random_data_len; +int32 runtime·invalidptr; + enum { // hashinit wants this many random bytes HashRandomBytes = 32 diff --git a/src/runtime/stack.c b/src/runtime/stack.c index e402691f4..ed8f4f872 100644 --- a/src/runtime/stack.c +++ b/src/runtime/stack.c @@ -401,12 +401,12 @@ adjustpointers(byte **scanp, BitVector *bv, AdjustInfo *adjinfo, Func *f) break; case BitsPointer: p = scanp[i]; - if(f != nil && (byte*)0 < p && (p < (byte*)PageSize || (uintptr)p == PoisonGC || (uintptr)p == PoisonStack)) { + if(f != nil && (byte*)0 < p && (p < (byte*)PageSize && runtime·invalidptr || (uintptr)p == PoisonGC || (uintptr)p == PoisonStack)) { // Looks like a junk value in a pointer slot. // Live analysis wrong? g->m->traceback = 2; runtime·printf("runtime: bad pointer in frame %s at %p: %p\n", runtime·funcname(f), &scanp[i], p); - runtime·throw("bad pointer!"); + runtime·throw("invalid stack pointer"); } if(minp <= p && p < maxp) { if(StackDebug >= 3) diff --git a/src/runtime/sys_nacl_amd64p32.s b/src/runtime/sys_nacl_amd64p32.s index 06a0dc5dd..9cfbef6ef 100644 --- a/src/runtime/sys_nacl_amd64p32.s +++ b/src/runtime/sys_nacl_amd64p32.s @@ -60,7 +60,7 @@ TEXT syscall·naclWrite(SB), NOSPLIT, $24-20 TEXT runtime·write(SB),NOSPLIT,$16-20 // If using fake time and writing to stdout or stderr, // emit playback header before actual data. - MOVQ runtime·timens(SB), AX + MOVQ runtime·faketime(SB), AX CMPQ AX, $0 JEQ write MOVL fd+0(FP), DI @@ -242,7 +242,7 @@ TEXT runtime·mmap(SB),NOSPLIT,$8 RET TEXT time·now(SB),NOSPLIT,$16 - MOVQ runtime·timens(SB), AX + MOVQ runtime·faketime(SB), AX CMPQ AX, $0 JEQ realtime MOVQ $0, DX @@ -277,7 +277,7 @@ TEXT runtime·nacl_clock_gettime(SB),NOSPLIT,$0 RET TEXT runtime·nanotime(SB),NOSPLIT,$16 - MOVQ runtime·timens(SB), AX + MOVQ runtime·faketime(SB), AX CMPQ AX, $0 JEQ 3(PC) MOVQ AX, ret+0(FP) diff --git a/src/runtime/time.go b/src/runtime/time.go index 8cf9eecf8..11862c7e2 100644 --- a/src/runtime/time.go +++ b/src/runtime/time.go @@ -35,8 +35,8 @@ var timers struct { t []*timer } -// nacl fake time support. -var timens int64 +// nacl fake time support - time in nanoseconds since 1970 +var faketime int64 // Package time APIs. // Godoc uses the comments in package time, not these. @@ -194,7 +194,7 @@ func timerproc() { f(arg, seq) lock(&timers.lock) } - if delta < 0 { + if delta < 0 || faketime > 0 { // No timers left - put goroutine to sleep. timers.rescheduling = true goparkunlock(&timers.lock, "timer goroutine (idle)") @@ -208,6 +208,29 @@ func timerproc() { } } +func timejump() *g { + if faketime == 0 { + return nil + } + + lock(&timers.lock) + if !timers.created || len(timers.t) == 0 { + unlock(&timers.lock) + return nil + } + + var gp *g + if faketime < timers.t[0].when { + faketime = timers.t[0].when + if timers.rescheduling { + timers.rescheduling = false + gp = timers.gp + } + } + unlock(&timers.lock) + return gp +} + // Heap maintenance algorithms. func siftupTimer(i int) { |