From 2e0aa581b4a2544249ad2f8e86e17204ca778ca7 Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Wed, 10 Apr 2019 17:56:18 -0700 Subject: [PATCH] runtime: add new addtimer function When we add a timer, make sure that the network poller is initialized, since we will use it if we have to wait for the timer to be ready. Updates #27707 Change-Id: I0637fe646bade2cc5ce50b745712292aa9c445b1 Reviewed-on: https://go-review.googlesource.com/c/go/+/171830 Run-TryBot: Ian Lance Taylor TryBot-Result: Gobot Gobot Reviewed-by: Michael Knyszek --- src/runtime/netpoll.go | 9 ++++-- src/runtime/netpoll_stub.go | 4 +++ src/runtime/time.go | 63 ++++++++++++++++++++++++++++++++++++- 3 files changed, 73 insertions(+), 3 deletions(-) diff --git a/src/runtime/netpoll.go b/src/runtime/netpoll.go index 536dae3d4f..939b27061e 100644 --- a/src/runtime/netpoll.go +++ b/src/runtime/netpoll.go @@ -100,8 +100,13 @@ var ( //go:linkname poll_runtime_pollServerInit internal/poll.runtime_pollServerInit func poll_runtime_pollServerInit() { - netpollinit() - atomic.Store(&netpollInited, 1) + netpollGenericInit() +} + +func netpollGenericInit() { + if atomic.Cas(&netpollInited, 0, 1) { + netpollinit() + } } func netpollinited() bool { diff --git a/src/runtime/netpoll_stub.go b/src/runtime/netpoll_stub.go index 00c06a440b..ab92b0424e 100644 --- a/src/runtime/netpoll_stub.go +++ b/src/runtime/netpoll_stub.go @@ -8,12 +8,16 @@ package runtime import "runtime/internal/atomic" +var netpollInited uint32 var netpollWaiters uint32 var netpollStubLock mutex var netpollNote note var netpollBroken uint32 +func netpollGenericInit() { +} + func netpollBreak() { if atomic.Cas(&netpollBroken, 0, 1) { notewakeup(&netpollNote) diff --git a/src/runtime/time.go b/src/runtime/time.go index b9105592a4..b4f1c0205e 100644 --- a/src/runtime/time.go +++ b/src/runtime/time.go @@ -116,6 +116,10 @@ type timersBucket struct { // // Active timers live in heaps attached to P, in the timers field. // Inactive timers live there too temporarily, until they are removed. +// +// addtimer: +// timerNoStatus -> timerWaiting +// anything else -> panic: invalid value // Values for the timer status field. const ( @@ -161,6 +165,9 @@ const ( timerMoving ) +// maxWhen is the maximum value for timer's when field. +const maxWhen = 1<<63 - 1 + // Package time APIs. // Godoc uses the comments in package time, not these. @@ -232,12 +239,56 @@ func goroutineReady(arg interface{}, seq uintptr) { goready(arg.(*g), 0) } +// addtimer adds a timer to the current P. +// This should only be called with a newly created timer. +// That avoids the risk of changing the when field of a timer in some P's heap, +// which could cause the heap to become unsorted. func addtimer(t *timer) { if oldTimers { addtimerOld(t) return } - throw("new addtimer not yet implemented") + + // when must never be negative; otherwise runtimer will overflow + // during its delta calculation and never expire other runtime timers. + if t.when < 0 { + t.when = maxWhen + } + if t.status != timerNoStatus { + badTimer() + } + t.status = timerWaiting + + when := t.when + + pp := getg().m.p.ptr() + lock(&pp.timersLock) + ok := cleantimers(pp) && doaddtimer(pp, t) + unlock(&pp.timersLock) + if !ok { + badTimer() + } + + wakeNetPoller(when) +} + +// doaddtimer adds t to the current P's heap. +// It reports whether it saw no problems due to races. +// The caller must have locked the timers for pp. +func doaddtimer(pp *p, t *timer) bool { + // Timers rely on the network poller, so make sure the poller + // has started. + if netpollInited == 0 { + netpollGenericInit() + } + + if t.pp != 0 { + throw("doaddtimer: P already set in timer") + } + t.pp.set(pp) + i := len(pp.timers) + pp.timers = append(pp.timers, t) + return siftupTimer(pp.timers, i) } func addtimerOld(t *timer) { @@ -457,6 +508,16 @@ func timerproc(tb *timersBucket) { } } +// cleantimers cleans up the head of the timer queue. This speeds up +// programs that create and delete timers; leaving them in the heap +// slows down addtimer. Reports whether no timer problems were found. +// The caller must have locked the timers for pp. +func cleantimers(pp *p) bool { + // TODO: write this. + throw("cleantimers") + return true +} + // moveTimers moves a slice of timers to pp. The slice has been taken // from a different P. // This is currently called when the world is stopped, but it could -- 2.50.0