1 // Copyright 2013 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
16 func TestDo(t *testing.T) {
18 v, err, _ := g.Do("key", func() (interface{}, error) {
21 if got, want := fmt.Sprintf("%v (%T)", v, v), "bar (string)"; got != want {
22 t.Errorf("Do = %v; want %v", got, want)
25 t.Errorf("Do error = %v", err)
29 func TestDoErr(t *testing.T) {
31 someErr := errors.New("Some error")
32 v, err, _ := g.Do("key", func() (interface{}, error) {
36 t.Errorf("Do error = %v; want someErr %v", err, someErr)
39 t.Errorf("unexpected non-nil value %#v", v)
43 func TestDoDupSuppress(t *testing.T) {
45 var wg1, wg2 sync.WaitGroup
46 c := make(chan string, 1)
48 fn := func() (interface{}, error) {
49 if atomic.AddInt32(&calls, 1) == 1 {
54 c <- v // pump; make available for any future calls
56 time.Sleep(10 * time.Millisecond) // let more goroutines enter Do
63 for i := 0; i < n; i++ {
69 v, err, _ := g.Do("key", fn)
71 t.Errorf("Do error: %v", err)
74 if s, _ := v.(string); s != "bar" {
75 t.Errorf("Do = %T %v; want %q", v, v, "bar")
80 // At least one goroutine is in fn now and all of them have at
81 // least reached the line before the Do.
84 if got := atomic.LoadInt32(&calls); got <= 0 || got >= n {
85 t.Errorf("number of calls = %d; want over 0 and less than %d", got, n)
89 // Test that singleflight behaves correctly after Forget called.
90 // See https://github.com/golang/go/issues/31420
91 func TestForget(t *testing.T) {
94 var firstStarted, firstFinished sync.WaitGroup
99 firstCh := make(chan struct{})
101 g.Do("key", func() (i interface{}, e error) {
110 g.Forget("key") // from this point no two function using same key should be executed concurrently
112 var secondStarted int32
113 var secondFinished int32
114 var thirdStarted int32
116 secondCh := make(chan struct{})
117 secondRunning := make(chan struct{})
119 g.Do("key", func() (i interface{}, e error) {
122 atomic.AddInt32(&secondStarted, 1)
123 // Notify that we started
124 secondCh <- struct{}{}
125 // Wait other get above signal
128 atomic.AddInt32(&secondFinished, 1)
134 firstFinished.Wait() // wait for first execution (which should not affect execution after Forget)
137 // Notify second that we got the signal that it started
138 secondRunning <- struct{}{}
139 if atomic.LoadInt32(&secondStarted) != 1 {
140 t.Fatal("Second execution should be executed due to usage of forget")
143 if atomic.LoadInt32(&secondFinished) == 1 {
144 t.Fatal("Second execution should be still active")
148 result, _, _ := g.Do("key", func() (i interface{}, e error) {
149 atomic.AddInt32(&thirdStarted, 1)
153 if atomic.LoadInt32(&thirdStarted) != 0 {
154 t.Error("Third call should not be started because was started during second execution")
157 t.Errorf("We should receive result produced by second call, expected: 2, got %d", result)