runtime: implement timers for Go 1.23

There were a number of changes in time.Timer/time.Ticker that need a
separate implementation for Go 1.22 and Go 1.23.
This commit is contained in:
Ayke van Laethem
2024-06-24 16:19:07 +02:00
committed by Ron Evans
parent db2a06a9bb
commit e865db232b
4 changed files with 105 additions and 28 deletions
+2 -1
View File
@@ -181,12 +181,13 @@ func scheduler() {
// Check for expired timers to trigger.
if timerQueue != nil && now >= timerQueue.whenTicks() {
scheduleLog("--- timer awoke")
delay := ticksToNanoseconds(now - timerQueue.whenTicks())
// Pop timer from queue.
tn := timerQueue
timerQueue = tn.next
tn.next = nil
// Run the callback stored in this timer node.
tn.callback(tn)
tn.callback(tn, delay)
}
t := runqueue.Pop()
+3 -27
View File
@@ -4,7 +4,7 @@ package runtime
type timerNode struct {
next *timerNode
timer *timer
callback func(*timerNode)
callback func(node *timerNode, delta int64)
}
// whenTicks returns the (absolute) time when this timer should trigger next.
@@ -12,17 +12,6 @@ func (t *timerNode) whenTicks() timeUnit {
return nanosecondsToTicks(t.timer.when)
}
// Defined in the time package, implemented here in the runtime.
//
//go:linkname startTimer time.startTimer
func startTimer(tim *timer) {
addTimer(&timerNode{
timer: tim,
callback: timerCallback,
})
scheduleLog("adding timer")
}
// timerCallback is called when a timer expires. It makes sure to call the
// callback in the time package and to re-add the timer to the queue if this is
// a ticker (repeating timer).
@@ -32,11 +21,11 @@ func startTimer(tim *timer) {
// dependency causes timerQueue not to get optimized away.
// If timerQueue doesn't get optimized away, small programs (that don't call
// time.NewTimer etc) would still pay the cost of these timers.
func timerCallback(tn *timerNode) {
func timerCallback(tn *timerNode, delta int64) {
// Run timer function (implemented in the time package).
// The seq parameter to the f function is not used in the time
// package so is left zero.
tn.timer.f(tn.timer.arg, 0)
tn.timer.callCallback(delta)
// If this is a periodic timer (a ticker), re-add it to the queue.
if tn.timer.period != 0 {
@@ -44,16 +33,3 @@ func timerCallback(tn *timerNode) {
addTimer(tn)
}
}
//go:linkname stopTimer time.stopTimer
func stopTimer(tim *timer) bool {
return removeTimer(tim)
}
//go:linkname resetTimer time.resetTimer
func resetTimer(tim *timer, when int64) bool {
tim.when = when
removed := removeTimer(tim)
startTimer(tim)
return removed
}
@@ -1,9 +1,13 @@
//go:build !go1.23
// Portions copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package runtime
// Time functions for Go 1.22 and below.
type puintptr uintptr
// Package time knows the layout of this structure.
@@ -31,3 +35,31 @@ type timer struct {
// The status field holds one of the values below.
status uint32
}
func (tim *timer) callCallback(delta int64) {
tim.f(tim.arg, 0)
}
// Defined in the time package, implemented here in the runtime.
//
//go:linkname startTimer time.startTimer
func startTimer(tim *timer) {
addTimer(&timerNode{
timer: tim,
callback: timerCallback,
})
scheduleLog("adding timer")
}
//go:linkname stopTimer time.stopTimer
func stopTimer(tim *timer) bool {
return removeTimer(tim)
}
//go:linkname resetTimer time.resetTimer
func resetTimer(tim *timer, when int64) bool {
tim.when = when
removed := removeTimer(tim)
startTimer(tim)
return removed
}
+68
View File
@@ -0,0 +1,68 @@
//go:build go1.23
package runtime
import "unsafe"
// Time functions for Go 1.23 and above.
// This is the timer that's used internally inside the runtime.
type timer struct {
// When to call the timer, and the interval for the ticker.
when int64
period int64
// Callback from the time package.
f func(arg any, seq uintptr, delta int64)
arg any
}
func (tim *timer) callCallback(delta int64) {
tim.f(tim.arg, 0, delta)
}
// This is the struct used internally in the runtime. The first two fields are
// the same as time.Timer and time.Ticker so it can be used as-is in the time
// package.
type timeTimer struct {
c unsafe.Pointer // <-chan time.Time
init bool
timer
}
//go:linkname newTimer time.newTimer
func newTimer(when, period int64, f func(arg any, seq uintptr, delta int64), arg any, c unsafe.Pointer) *timeTimer {
tim := &timeTimer{
c: c,
init: true,
timer: timer{
when: when,
period: period,
f: f,
arg: arg,
},
}
scheduleLog("new timer")
addTimer(&timerNode{
timer: &tim.timer,
callback: timerCallback,
})
return tim
}
//go:linkname stopTimer time.stopTimer
func stopTimer(tim *timeTimer) bool {
return removeTimer(&tim.timer)
}
//go:linkname resetTimer time.resetTimer
func resetTimer(t *timeTimer, when, period int64) bool {
t.timer.when = when
t.timer.period = period
removed := removeTimer(&t.timer)
addTimer(&timerNode{
timer: &t.timer,
callback: timerCallback,
})
return removed
}