Таймеры в Go 1.23
Тут прям детективная история приключилась. В Go есть таймер (тип Timer
), а в нем — поле с каналом (Timer.C
), в который таймер тикает спустя указанное время.
В коде стдлибы таймер создается так:
func NewTimer(d Duration) *Timer {
c := make(chan Time, 1)
t := &Timer{
C: c,
// ...
}
startTimer(&t.r)
return t
}
Такая реализация привела к проблемам с time.After
и Reset
, от которых многие страдали.
И вот в Go 1.23 решили это исправить, для чего сделали канал в таймере небуферизованным:
// As of Go 1.23, the channel is synchronous (unbuffered, capacity 0),
// eliminating the possibility of those stale values.
func NewTimer(d Duration) *Timer {
c := make(chan Time, 1)
t := (*Timer)(newTimer(when(d), 0, sendTime, c, syncTimer(c)))
t.C = c
return t
}
Вот только если вы посмотрите на фактический код, то канал-то остался буферизованным 😁
c := make(chan Time, 1)
Из комментариев к коммиту выясняется, что канал действительно остался буферизованным, но притворяется, что никакого буфера у него нет:
Specifically, the timer channel has a 1-element buffer like it always has, but len(t.C) and cap(t.C) are special-cased to return 0 anyway, so user code cannot see what's in the buffer except with a receive.
Эту логику вкорячили прямо в реализацию канала (тип chan
).
Что тут скажешь. Ну и дичь.
★ Подписывайтесь на новые заметки.