do icon indicating copy to clipboard operation
do copied to clipboard

Data race when invoking services after shutdown

Open ivanduka opened this issue 3 years ago • 0 comments

I am not sure if this is a problem or a feature, but invoking dependencies after shutdown creates a race condition (runnable code at https://goplay.tools/snippet/vIQ3Pwu4lw1):

package main

import (
	"log"
	"sync"
	"time"

	"github.com/samber/do"
)

type Service struct{}

func main() {
	var wg sync.WaitGroup

	i := do.New()

	do.Provide(i, func(injector *do.Injector) (*Service, error) {
		return &Service{}, nil
	})

	// uncommenting the line below gives an error: `Invoking after shutdown error: DI: could not find service `*main.Service`, available services:`
	//_ = do.MustInvoke[*Service](i)

	wg.Add(1)

	go func() {
		defer wg.Done()

		time.Sleep(time.Second * 1)

		_, err := do.Invoke[*Service](i)
		if err != nil {
			log.Printf("Invoking after shutdown error: %v\n", err)
			return
		}
	}()

	err := i.Shutdown()
	if err != nil {
		log.Printf("Shutdown error: %v\n", err)
	}

	wg.Wait()

	log.Println("Finished")
}

When running with go run -race . it detects a race condition:

==================
WARNING: DATA RACE
Write at 0x00c0000b60c8 by goroutine 6:
  github.com/samber/do.(*Injector).onServiceInvoke()
      /Users/ivand/go/pkg/mod/github.com/samber/[email protected]/injector.go:278 +0x120
  github.com/samber/do.InvokeNamed[...]()
      /Users/ivand/go/pkg/mod/github.com/samber/[email protected]/di.go:128 +0xc8
  github.com/samber/do.Invoke[...]()
      /Users/ivand/go/pkg/mod/github.com/samber/[email protected]/di.go:101 +0x6c
  main.main.func2()
      /Users/ivand/IdeaProjects/untitled1/main.go:32 +0x7c

Previous read at 0x00c0000b60c8 by main goroutine:
  github.com/samber/do.(*Injector).Shutdown()
      /Users/ivand/go/pkg/mod/github.com/samber/[email protected]/injector.go:124 +0x1b0
  main.main()
      /Users/ivand/IdeaProjects/untitled1/main.go:39 +0x130

Goroutine 6 (running) created at:
  main.main()
      /Users/ivand/IdeaProjects/untitled1/main.go:27 +0x128
==================
2023/04/28 12:55:27 Finished
Found 1 data race(s)
exit status 66

Is this the intended way it supposed to work? Is the shutdown procedure expected to be a final call with no invoking of services after it?

I haven't dug deeply in the source code, but apparently the shutdown procedure does not operate under the write lock. Is there a particular reason for that?

ivanduka avatar Apr 28 '23 20:04 ivanduka