github.com/searKing/golang/go@v1.2.117/sync/thread_test.go (about) 1 // Copyright 2021 The searKing Author. 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. 4 5 package sync_test 6 7 import ( 8 "context" 9 "errors" 10 "expvar" 11 "fmt" 12 "sync" 13 "testing" 14 15 expvar_ "github.com/searKing/golang/go/expvar" 16 sync_ "github.com/searKing/golang/go/sync" 17 ) 18 19 type one int 20 21 func (o *one) Increment() { 22 *o++ 23 } 24 25 func run(thread *sync_.Thread, o *one, c chan bool) { 26 _ = thread.Do(context.Background(), func() { o.Increment() }) 27 c <- true 28 } 29 30 func TestThread(t *testing.T) { 31 o := new(one) 32 thread := new(sync_.Thread) 33 defer thread.Shutdown() 34 c := make(chan bool) 35 const N = 10 36 for i := 0; i < N; i++ { 37 go run(thread, o, c) 38 } 39 for i := 0; i < N; i++ { 40 <-c 41 } 42 if *o != N { 43 t.Errorf("once failed outside run: %d is not %d", *o, N) 44 } 45 } 46 47 func TestThreadPanic(t *testing.T) { 48 var thread sync_.Thread 49 func() { 50 defer func() { 51 if r := recover(); r == nil { 52 t.Fatalf("Thread.Do did not panic") 53 } 54 }() 55 _ = thread.Do(context.Background(), func() { 56 panic("failed") 57 }) 58 }() 59 60 { 61 var do bool 62 _ = thread.Do(context.Background(), func() { 63 do = true 64 }) 65 if !do { 66 t.Fatalf("Thread.Do did not called") 67 } 68 } 69 thread.Shutdown() 70 71 { 72 var do bool 73 _ = thread.Do(context.Background(), func() { 74 do = true 75 }) 76 if do { 77 t.Fatalf("Thread.Do called after Thread.Shutdown") 78 } 79 } 80 } 81 82 func TestThreadCancel(t *testing.T) { 83 var thread sync_.Thread 84 var errThreadClosedByUser = errors.New("sync: Thread closed by user") 85 { 86 var wg sync.WaitGroup 87 wg.Add(1) 88 ctx, cancel := context.WithCancelCause(context.Background()) 89 90 go func() { 91 defer wg.Done() 92 // Block until canceled 93 err := thread.Do(ctx, func() { 94 select {} 95 }) 96 if err == nil { 97 t.Errorf("Thread.Do did not return error") 98 return 99 } 100 101 if !errors.Is(err, errThreadClosedByUser) { 102 t.Errorf("Thread.Do did not return errThreadClosedByUser") 103 return 104 } 105 }() 106 cancel(errThreadClosedByUser) 107 wg.Wait() 108 } 109 110 { 111 go func() { 112 // Block until canceled 113 err := thread.Do(context.Background(), func() { 114 select {} 115 }) 116 if err == nil { 117 t.Errorf("Thread.Do did not return error") 118 return 119 } 120 }() 121 } 122 123 thread.Shutdown() 124 125 { 126 var do bool 127 err := thread.Do(context.Background(), func() { 128 do = true 129 }) 130 if do { 131 t.Fatalf("Thread.Do called after Thread.Shutdown") 132 } 133 if err == nil { 134 t.Errorf("Thread.Do did not return error") 135 return 136 } 137 138 if !errors.Is(err, sync_.ErrThreadClosed) { 139 t.Errorf("Thread.Do did not return ErrThreadClosed") 140 return 141 } 142 } 143 } 144 145 func BenchmarkThread(b *testing.B) { 146 var thread sync_.Thread 147 defer thread.Shutdown() 148 f := func() {} 149 b.RunParallel(func(pb *testing.PB) { 150 for pb.Next() { 151 _ = thread.Do(context.Background(), f) 152 } 153 }) 154 } 155 156 var osThreadLeak = expvar_.NewLeak("os_thread_leak") 157 var goroutineLeak = expvar_.NewLeak("goroutine_leak") 158 var handlerLeak = expvar_.NewLeak("handler_leak") 159 160 func BenchmarkThreadWithLeak(b *testing.B) { 161 var thread sync_.Thread 162 thread.OSThreadLeak = osThreadLeak 163 thread.GoroutineLeak = goroutineLeak 164 thread.HandlerLeak = handlerLeak 165 defer b.Logf("%s %s %s ", osThreadLeak, goroutineLeak, handlerLeak) 166 defer thread.Shutdown() 167 f := func() {} 168 b.RunParallel(func(pb *testing.PB) { 169 for pb.Next() { 170 _ = thread.Do(context.Background(), f) 171 } 172 }) 173 } 174 175 func TestThreadWithLeak(t *testing.T) { 176 o := new(one) 177 thread := new(sync_.Thread) 178 thread.OSThreadLeak = osThreadLeak 179 thread.GoroutineLeak = goroutineLeak 180 thread.HandlerLeak = handlerLeak 181 182 defer thread.Shutdown() 183 c := make(chan bool) 184 const N = 10 185 for i := 0; i < N; i++ { 186 go run(thread, o, c) 187 } 188 for i := 0; i < N; i++ { 189 <-c 190 } 191 if *o != N { 192 t.Errorf("once failed outside run: %d is not %d", *o, N) 193 } 194 if got := thread.OSThreadLeak.String(); got != "[1 1]" { 195 t.Errorf("OSThreadLeak.String() = %q, want %q", got, "[1 1]") 196 } 197 if got := thread.GoroutineLeak.String(); got != "[1 1]" { 198 t.Errorf("GoroutineLeak.String() = %q, want %q", got, "[1 1]") 199 } 200 201 want := fmt.Sprintf("[0 %d]", N) 202 if got := thread.HandlerLeak.String(); got != want { 203 t.Errorf("HandlerLeak.String() = %q, want %q", got, want) 204 } 205 } 206 207 func TestThread_WatchStats(t *testing.T) { 208 o := new(one) 209 thread := new(sync_.Thread) 210 thread.WatchStats() 211 212 defer thread.Shutdown() 213 c := make(chan bool) 214 const N = 10 215 for i := 0; i < N; i++ { 216 go run(thread, o, c) 217 } 218 for i := 0; i < N; i++ { 219 <-c 220 } 221 if *o != N { 222 t.Errorf("once failed outside run: %d is not %d", *o, N) 223 } 224 if got := thread.OSThreadLeak.String(); got != "[1 1]" { 225 t.Errorf("OSThreadLeak.String() = %q, want %q", got, "[1 1]") 226 } 227 if got := thread.GoroutineLeak.String(); got != "[1 1]" { 228 t.Errorf("GoroutineLeak.String() = %q, want %q", got, "[1 1]") 229 } 230 231 want := fmt.Sprintf("[0 %d]", N) 232 if got := thread.HandlerLeak.String(); got != want { 233 t.Errorf("HandlerLeak.String() = %q, want %q", got, want) 234 } 235 t.Logf("sync.Thread: %s", expvar.Get("sync.Thread")) 236 }