process_race_test.go 1.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960
  1. package xray
  2. import (
  3. "errors"
  4. "os/exec"
  5. "sync"
  6. "testing"
  7. "time"
  8. )
  9. // TestProcessLifecycleFieldsRaceSafe drives the lifecycle fields (cmd, done,
  10. // exitErr) the way Start/startCommand and the waitForCommand goroutine do, while
  11. // the status getters read them concurrently. Run with -race: any unsynchronized
  12. // access to those fields is reported as a data race.
  13. func TestProcessLifecycleFieldsRaceSafe(t *testing.T) {
  14. p := &process{logWriter: NewLogWriter()}
  15. var wg sync.WaitGroup
  16. stop := make(chan struct{})
  17. // Writer: churn cmd/done/exitErr like Start + waitForCommand.
  18. wg.Add(1)
  19. go func() {
  20. defer wg.Done()
  21. for {
  22. select {
  23. case <-stop:
  24. return
  25. default:
  26. }
  27. p.mu.Lock()
  28. p.cmd = &exec.Cmd{}
  29. p.done = make(chan struct{})
  30. p.mu.Unlock()
  31. p.setExitErr(errors.New("boom"))
  32. }
  33. }()
  34. // Readers: the concurrent status getters.
  35. for i := 0; i < 4; i++ {
  36. wg.Add(1)
  37. go func() {
  38. defer wg.Done()
  39. for {
  40. select {
  41. case <-stop:
  42. return
  43. default:
  44. }
  45. _ = p.IsRunning()
  46. _ = p.GetErr()
  47. _ = p.GetResult()
  48. }
  49. }()
  50. }
  51. time.Sleep(50 * time.Millisecond)
  52. close(stop)
  53. wg.Wait()
  54. }