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  }