Go 1.23引入的新Bug?其實只是文檔沒看仔細
上個月中旬,Go 1.23版本正式發布!這也是Russ Cox作為Go tech leader的最后一個發布版本,他本人在該版本中做出重要貢獻,那就是解決了一直困擾Go團隊的Timer/Ticker的GC回收問題,進而解決了Timer的Stop和Reset很難正確使用的問題。
不過,就在昨天,一個叫tulir的gopher提出的issue(ttps://github.com/golang/go/issues/69186)差點讓Russ Cox“晚節不保”:)。該issue提到,他寫的一段使用了Timer的代碼在Go 1.22中工作正常,但在Go 1.23中就無法工作了,具體現象是:在linux上,整個程序hang住不動了,而在macOS上,則直接引發panic異常退出:"fatal error: ts set in timer"。
隨即,Go101老貘兄“補上一刀”,給出了一個更為簡潔的示例:
package main
import "time"
func main() {
illegalTimerCopy := *time.NewTimer(time.Second)
illegalTimerCopy.Stop() // block for ever
}
我也實測了該示例,在我的macOS上,用go1.23.0運行,直接panic,即便使用GODEBUG=synctimerchan=1退回到Go 1.23以前的行為也不行。在centos 7.9(kernel 3.10)上跑,也發生了和issue一樣的現象:hang住不動。
到這里,我也不得不認為:這是go 1.23 Timer引入的新bug!但真相果真如此嗎?
幾個小時后,Go大神Ian Lance Taylor現身說法了。他居然表示對Go 1.23之前的版本依然可以正確運行上述代碼表示“驚訝”。
圖片
之后,他表示從Go 1.4版本開始,Go標準庫文檔(#8776)就對Timer類型的使用做出了限制:"A Timer must be created with NewTimer or AfterFunc.",即Timer只能使用NewTimer和AfterFunc創建。
圖片
而像上述代碼中的對Timer實例的Copy的行為則是未定義的。
圖片
目前Ian Lance Taylor將該issue改名為“proposal: cmd/vet: warn about copying a time.Timer value”,即建議在vet中增加warning,但他也不保證該proposal能被accept,因為要看后續是否會受到很多類似的問題報告。
蓋棺定論了,原來是我們的文檔看的還不夠仔細:)。
不過,即便go vet不增加對Timer copy的warning,我也想建議官方修改一下Timer的doc描述,再直白一些,就像對sync.Mutex那樣:A Mutex must not be copied after first use.
圖片
在Timer文檔中,也補充依據:A Timer must not be copied after being created.
你說呢?