Go version
go version go1.23.1 darwin/arm64
Output of go env
in your module/workspace:
GO111MODULE=''
GOARCH='arm64'
GOBIN=''
GOCACHE='/Users/admin/Library/Caches/go-build'
GOENV='/Users/admin/Library/Application Support/go/env'
GOEXE=''
GOEXPERIMENT=''
GOFLAGS=''
GOHOSTARCH='arm64'
GOHOSTOS='darwin'
GOINSECURE=''
GOMODCACHE='/Users/admin/go/pkg/mod'
GONOPROXY=''
GONOSUMDB=''
GOOS='darwin'
GOPATH='/Users/admin/go'
GOPRIVATE=''
GOPROXY='https://proxy.golang.org,direct'
GOROOT='/opt/homebrew/Cellar/go/1.23.1/libexec'
GOSUMDB='sum.golang.org'
GOTMPDIR=''
GOTOOLCHAIN='local'
GOTOOLDIR='/opt/homebrew/Cellar/go/1.23.1/libexec/pkg/tool/darwin_arm64'
GOVCS=''
GOVERSION='go1.23.1'
GODEBUG=''
GOTELEMETRY='local'
GOTELEMETRYDIR='/Users/admin/Library/Application Support/go/telemetry'
GCCGO='gccgo'
GOARM64='v8.0'
AR='ar'
CC='cc'
CXX='c++'
CGO_ENABLED='1'
GOMOD='/Users/admin/hard/test-squad-rcon/go.mod'
GOWORK=''
CGO_CFLAGS='-O2 -g'
CGO_CPPFLAGS=''
CGO_CXXFLAGS='-O2 -g'
CGO_FFLAGS='-O2 -g'
CGO_LDFLAGS='-O2 -g'
PKG_CONFIG='pkg-config'
GOGCCFLAGS='-fPIC -arch arm64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -ffile-prefix-map=/var/folders/8q/9530g_8d3zbgb84jqwx9k39w0000gn/T/go-build3830069734=/tmp/go-build -gno-record-gcc-switches -fno-common'
What did you do?
package main
import (
"fmt"
"time"
)
type Callbacks struct {
onData func(string)
}
func (r *Callbacks) print() {
ticker := time.NewTicker(1 * time.Second)
defer ticker.Stop()
for {
select {
case <-ticker.C:
{
fmt.Printf("OnData is nil? // %v\n", r.onData == nil)
if r.onData != nil {
r.onData("data")
}
}
}
}
}
func (r *Callbacks) OnData(callback func(string)) {
r.onData = callback
}
func main() {
r := &Callbacks{}
go r.print()
r.OnData(func(data string) {
fmt.Println(data)
})
for {}
}
What did you see happen?
The function r.OnData(...) will never be executed, I looked at the assembler code of the executable file, and there the call to this function was removed.
There are several solutions to the problem. 1) Use select instead of for. 2) Where we assign the callback in the structure, print some log data to the console (for example). This way the compiler will not remove the function from the executable file.
What did you expect to see?
I have synchronous code that must be executed without fail, but it does not. In any case, looking at the assembler code and the absence of a function call, you can verify this
Comment From: gabyhelp
Related Issues and Documentation
(Emoji vote if this was helpful or unhelpful; more detailed feedback welcome in this discussion.)
Comment From: randall77
Your program has a data race (run with -race
), you should fix that as Go does not guarantee any semantics in the presence of races.
Probably the immediate problem is that there is no synchronization after your r.OnData
call, so no goroutine is guaranteed to observe the write inside that call. So that write is deadcoded away.
If you want the write of one goroutine to be seen by another, some synchronization is required.
Closing, as not a bug in Go.
Comment From: iamalone98
Your program has a data race (run with
-race
), you should fix that as Go does not guarantee any semantics in the presence of races.Probably the immediate problem is that there is no synchronization after your
r.OnData
call, so no goroutine is guaranteed to observe the write inside that call. So that write is deadcoded away.If you want the write of one goroutine to be seen by another, some synchronization is required.
Closing, as not a bug in Go.
Why can such code fix the situation?
At the same time, the -race
shows the same problem.
func (r *Callbacks) OnData(callback func(string)) {
fmt.Println("it worked!")
r.onData = callback
}
Comment From: zigo101
Without synchronizations,, there is not any guarantee made here. It may behave as you expect, or not.