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