Skip to content

errors: race condition is detected when using errors.As() #67928

@yunbj

Description

@yunbj

Go version

go version go1.22.4 darwin/arm64

Output of go env in your module/workspace:

GO111MODULE=''
GOARCH='arm64'
GOBIN=''
GOCACHE='/Users/yunbj/Library/Caches/go-build'
GOENV='/Users/yunbj/Library/Application Support/go/env'
GOEXE=''
GOEXPERIMENT=''
GOFLAGS=''
GOHOSTARCH='arm64'
GOHOSTOS='darwin'
GOINSECURE=''
GOMODCACHE='/Users/yunbj/Workspace/go/pkg/mod'
GONOPROXY='github.daumkakao.com'
GONOSUMDB='github.daumkakao.com'
GOOS='darwin'
GOPATH='/Users/yunbj/Workspace/go'
GOPRIVATE='github.daumkakao.com'
GOPROXY='https://proxy.golang.org,direct'
GOROOT='/Users/yunbj/Applications/go'
GOSUMDB='sum.golang.org'
GOTMPDIR=''
GOTOOLCHAIN='auto'
GOTOOLDIR='/Users/yunbj/Applications/go/pkg/tool/darwin_arm64'
GOVCS=''
GOVERSION='go1.22.4'
GCCGO='gccgo'
AR='ar'
CC='clang'
CXX='clang++'
CGO_ENABLED='1'
GOMOD='/Users/yunbj/Workspace/go/src/toy/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/sl/59_3w_5d2fnc85xsx_jr41zm0000gp/T/go-build3034457358=/tmp/go-build -gno-record-gcc-switches -fno-common'

What did you do?

When I call errors.As in multiple goroutine, race detector reports race condition.

What did you see happen?

sample code is below:

package main

import (
    "context"
    "errors"
    "fmt"
    "sync"
    "time"
)

var raceError = errors.New("race condition")

func isRaceError(err error) bool {
    return errors.As(err, &raceError)
}

func somthingWrong() error {
    return raceError
}

func checkRaceError() {
    err := somthingWrong()
    if isRaceError(err) {
        fmt.Println("this is race error")
    } else {
        fmt.Println("this is not race error")
    }
}

func worker(confirmStart, complete *sync.WaitGroup, wake <-chan struct{}, ctx context.Context) {
    confirmStart.Done()

    for {
        select {
        case <-ctx.Done():
            complete.Done()
            return
        case <-wake:
            checkRaceError()
        }
    }
}

func main() {
    wg := sync.WaitGroup{}
    confirmStart := sync.WaitGroup{}
    ctx, cancel := context.WithCancel(context.Background())

    const workers = 2
    const repeat = 1

    var wakeChannel = make([]chan struct{}, workers)

    wg.Add(workers)
    confirmStart.Add(workers)

    for i := 0; i < workers; i++ {
        wakeChannel[i] = make(chan struct{}, 1)
        go worker(&confirmStart, &wg, wakeChannel[i], ctx)
    }

    confirmStart.Wait()

    for i := 0; i < repeat; i++ {

        fmt.Println("send signal")

        for i := 0; i < workers; i++ {
            wakeChannel[i] <- struct{}{}
        }

        time.Sleep(1 * time.Second)
    }

    cancel()
    wg.Wait()
}

When I run with -race, "race condition" report is shown.

$ go run -race main.go
send signal
this is race error
==================
WARNING: DATA RACE
Read at 0x000100e9c280 by goroutine 7:
  main.checkRaceError()
      /Users/yunbj/Workspace/go/src/toy/errors.as/main.go:22 +0x28
  main.worker()
      /Users/yunbj/Workspace/go/src/toy/errors.as/main.go:39 +0x40
  main.main.gowrap1()
      /Users/yunbj/Workspace/go/src/toy/errors.as/main.go:59 +0x64

Previous write at 0x000100e9c280 by goroutine 6:
  reflect.maplen()
      /Users/yunbj/Applications/go/src/runtime/map.go:1406 +0x7c
  internal/reflectlite.typedmemmove()
      /Users/yunbj/Applications/go/src/runtime/mbarrier.go:221 +0x18
  errors.as()
      /Users/yunbj/Applications/go/src/errors/wrap.go:119 +0x1ec
  errors.As()
      /Users/yunbj/Applications/go/src/errors/wrap.go:113 +0x1d8
  main.isRaceError()
      /Users/yunbj/Workspace/go/src/toy/errors.as/main.go:14 +0x4c
  main.checkRaceError()
      /Users/yunbj/Workspace/go/src/toy/errors.as/main.go:23 +0x50
  main.worker()
      /Users/yunbj/Workspace/go/src/toy/errors.as/main.go:39 +0x40
  main.main.gowrap1()
      /Users/yunbj/Workspace/go/src/toy/errors.as/main.go:59 +0x64

Goroutine 7 (running) created at:
  main.main()
      /Users/yunbj/Workspace/go/src/toy/errors.as/main.go:59 +0xb4

Goroutine 6 (running) created at:
  main.main()
      /Users/yunbj/Workspace/go/src/toy/errors.as/main.go:59 +0xb4
==================
this is race error
Found 1 data race(s)
exit status 66

What did you expect to see?

When I use "errors.Is()", no race condition is reported.
Even though error is wrapped, I expect that race condition is not occurred.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions