github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/pkg/chann/chann_test.go (about)

     1  // Copyright 2022 PingCAP, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  //
    14  // ============================================================
    15  // Forked from https://github.com/golang-design/chann.
    16  // Copyright 2021 The golang.design Initiative Authors.
    17  // All rights reserved. Use of this source code is governed
    18  // by a MIT license that can be found in the LICENSE file.
    19  //
    20  // Written by Changkun Ou <changkun.de>
    21  
    22  package chann
    23  
    24  import (
    25  	"runtime"
    26  	"sync"
    27  	"sync/atomic"
    28  	"testing"
    29  	"time"
    30  
    31  	"github.com/stretchr/testify/require"
    32  )
    33  
    34  func TestChan(t *testing.T) {
    35  	defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(4))
    36  	N := 200
    37  	if testing.Short() {
    38  		N = 20
    39  	}
    40  	for chanCap := 0; chanCap < N; chanCap++ {
    41  		{
    42  			// Ensure that receive from empty chan blocks.
    43  			c := New[int](Cap(chanCap))
    44  			recv1 := false
    45  			go func() {
    46  				<-c.Out()
    47  				recv1 = true
    48  			}()
    49  			recv2 := false
    50  			go func() {
    51  				<-c.Out()
    52  				recv2 = true
    53  			}()
    54  			time.Sleep(time.Millisecond)
    55  			require.Falsef(t, recv1, "chan[%d]: receive from empty chan", chanCap)
    56  			require.Falsef(t, recv2, "chan[%d]: receive from empty chan", chanCap)
    57  			// Ensure that non-blocking receive does not block.
    58  			select {
    59  			case <-c.Out():
    60  				t.Fatalf("chan[%d]: receive from empty chan", chanCap)
    61  			default:
    62  			}
    63  			select {
    64  			case <-c.Out():
    65  				t.Fatalf("chan[%d]: receive from empty chan", chanCap)
    66  			default:
    67  			}
    68  			c.In() <- 0
    69  			c.In() <- 0
    70  		}
    71  
    72  		{
    73  			// Ensure that send to full chan blocks.
    74  			c := New[int](Cap(chanCap))
    75  			for i := 0; i < chanCap; i++ {
    76  				c.In() <- i
    77  			}
    78  			sent := uint32(0)
    79  			go func() {
    80  				c.In() <- 0
    81  				atomic.StoreUint32(&sent, 1)
    82  			}()
    83  			time.Sleep(time.Millisecond)
    84  			require.Equalf(t,
    85  				uint32(0),
    86  				atomic.LoadUint32(&sent),
    87  				"chan[%d]: send to full chan", chanCap,
    88  			)
    89  			// Ensure that non-blocking send does not block.
    90  			select {
    91  			case c.In() <- 0:
    92  				t.Fatalf("chan[%d]: send to full chan", chanCap)
    93  			default:
    94  			}
    95  			<-c.Out()
    96  		}
    97  
    98  		{
    99  			// Ensure that we receive 0 from closed chan.
   100  			c := New[int](Cap(chanCap))
   101  			for i := 0; i < chanCap; i++ {
   102  				c.In() <- i
   103  			}
   104  			c.Close()
   105  			for i := 0; i < chanCap; i++ {
   106  				v := <-c.Out()
   107  				require.Equalf(t, i, v, "chan[%d]", chanCap)
   108  			}
   109  			v := <-c.Out()
   110  			require.Equalf(t, 0, v, "chan[%d]", chanCap)
   111  			v, ok := <-c.Out()
   112  			require.Equalf(t, 0, v, "chan[%d]", chanCap)
   113  			require.Falsef(t, ok, "chan[%d]", chanCap)
   114  		}
   115  
   116  		{
   117  			// Ensure that close unblocks receive.
   118  			c := New[int](Cap(chanCap))
   119  			done := make(chan bool)
   120  			go func() {
   121  				v, ok := <-c.Out()
   122  				done <- v == 0 && ok == false
   123  			}()
   124  			time.Sleep(time.Millisecond)
   125  			c.Close()
   126  			require.Truef(t, <-done, "chan[%d]: received non zero from closed chan", chanCap)
   127  		}
   128  
   129  		{
   130  			// Send 100 integers,
   131  			// ensure that we receive them non-corrupted in FIFO order.
   132  			c := New[int](Cap(chanCap))
   133  			go func() {
   134  				for i := 0; i < 100; i++ {
   135  					c.In() <- i
   136  				}
   137  			}()
   138  			for i := 0; i < 100; i++ {
   139  				v := <-c.Out()
   140  				require.Equalf(t, i, v, "chan[%d]", chanCap)
   141  			}
   142  
   143  			// Same, but using recv2.
   144  			go func() {
   145  				for i := 0; i < 100; i++ {
   146  					c.In() <- i
   147  				}
   148  			}()
   149  			for i := 0; i < 100; i++ {
   150  				v, ok := <-c.Out()
   151  				require.Truef(t, ok, "chan[%d]: receive failed, expected %v", chanCap, i)
   152  				require.Equalf(t, i, v, "chan[%d]", chanCap)
   153  			}
   154  
   155  			// Send 1000 integers in 4 goroutines,
   156  			// ensure that we receive what we send.
   157  			const P = 4
   158  			const L = 1000
   159  			for p := 0; p < P; p++ {
   160  				go func() {
   161  					for i := 0; i < L; i++ {
   162  						c.In() <- i
   163  					}
   164  				}()
   165  			}
   166  			done := New[map[int]int](Cap(0))
   167  			for p := 0; p < P; p++ {
   168  				go func() {
   169  					recv := make(map[int]int)
   170  					for i := 0; i < L; i++ {
   171  						v := <-c.Out()
   172  						recv[v] = recv[v] + 1
   173  					}
   174  					done.In() <- recv
   175  				}()
   176  			}
   177  			recv := make(map[int]int)
   178  			for p := 0; p < P; p++ {
   179  				for k, v := range <-done.Out() {
   180  					recv[k] = recv[k] + v
   181  				}
   182  			}
   183  			require.Lenf(t, recv, L, "chan[%d]", chanCap)
   184  			for _, v := range recv {
   185  				require.Equalf(t, P, v, "chan[%d]", chanCap)
   186  			}
   187  		}
   188  
   189  		{
   190  			// Test len/cap.
   191  			c := New[int](Cap(chanCap))
   192  			require.Equalf(t, 0, c.Len(), "chan[%d]", chanCap)
   193  			require.Equalf(t, chanCap, c.Cap(), "chan[%d]", chanCap)
   194  			for i := 0; i < chanCap; i++ {
   195  				c.In() <- i
   196  			}
   197  			require.Equalf(t, chanCap, c.Len(), "chan[%d]", chanCap)
   198  			require.Equalf(t, chanCap, c.Cap(), "chan[%d]", chanCap)
   199  		}
   200  	}
   201  }
   202  
   203  func TestNonblockRecvRace(t *testing.T) {
   204  	n := 10000
   205  	if testing.Short() {
   206  		n = 100
   207  	}
   208  	for i := 0; i < n; i++ {
   209  		c := New[int](Cap(1))
   210  		c.In() <- 1
   211  		t.Log(i)
   212  		go func() {
   213  			select {
   214  			case <-c.Out():
   215  			default:
   216  				t.Error("chan is not ready")
   217  			}
   218  		}()
   219  		c.Close()
   220  		<-c.Out()
   221  		if t.Failed() {
   222  			return
   223  		}
   224  	}
   225  }
   226  
   227  const internalCacheSize = 16 + 1<<10
   228  
   229  // This test checks that select acts on the state of the channels at one
   230  // moment in the execution, not over a smeared time window.
   231  // In the test, one goroutine does:
   232  //
   233  //	create c1, c2
   234  //	make c1 ready for receiving
   235  //	create second goroutine
   236  //	make c2 ready for receiving
   237  //
   238  // The second goroutine does a non-blocking select receiving from c1 and c2.
   239  // From the time the second goroutine is created, at least one of c1 and c2
   240  // is always ready for receiving, so the select in the second goroutine must
   241  // always receive from one or the other. It must never execute the default case.
   242  func TestNonblockSelectRace(t *testing.T) {
   243  	n := 1000
   244  	done := New[bool](Cap(1))
   245  	for i := 0; i < n; i++ {
   246  		c1 := New[int]()
   247  		c2 := New[int]()
   248  		// The input channel of an unbounded buffer have an internal
   249  		// cache queue. When the input channel and the internal cache
   250  		// queue both gets full, we are certain that once the next send
   251  		// is complete, the out will be available for sure hence the
   252  		// waiting time of a receive is bounded.
   253  		for i := 0; i < internalCacheSize; i++ {
   254  			c1.In() <- 1
   255  		}
   256  		c1.In() <- 1
   257  		go func() {
   258  			runtime.Gosched()
   259  			select {
   260  			case <-c1.Out():
   261  			case <-c2.Out():
   262  			default:
   263  				done.In() <- false
   264  				return
   265  			}
   266  			done.In() <- true
   267  		}()
   268  		// Same for c2
   269  		for i := 0; i < internalCacheSize; i++ {
   270  			c2.In() <- 1
   271  		}
   272  		c2.In() <- 1
   273  		select {
   274  		case <-c1.Out():
   275  		default:
   276  		}
   277  		require.Truef(t, <-done.Out(), "no chan is ready")
   278  		c1.Close()
   279  		// Drop all events.
   280  		for range c1.Out() {
   281  		}
   282  		c2.Close()
   283  		for range c2.Out() {
   284  		}
   285  	}
   286  }
   287  
   288  // Same as TestNonblockSelectRace, but close(c2) replaces c2 <- 1.
   289  func TestNonblockSelectRace2(t *testing.T) {
   290  	n := 1000
   291  	done := make(chan bool, 1)
   292  	for i := 0; i < n; i++ {
   293  		c1 := New[int]()
   294  		c2 := New[int]()
   295  		// See TestNonblockSelectRace.
   296  		for i := 0; i < internalCacheSize; i++ {
   297  			c1.In() <- 1
   298  		}
   299  		c1.In() <- 1
   300  		go func() {
   301  			select {
   302  			case <-c1.Out():
   303  			case <-c2.Out():
   304  			default:
   305  				done <- false
   306  				return
   307  			}
   308  			done <- true
   309  		}()
   310  		c2.Close()
   311  		select {
   312  		case <-c1.Out():
   313  		default:
   314  		}
   315  		require.Truef(t, <-done, "no chan is ready")
   316  		c1.Close()
   317  		// Drop all events.
   318  		for range c1.Out() {
   319  		}
   320  	}
   321  }
   322  
   323  func TestUnboundedChann(t *testing.T) {
   324  	N := 200
   325  	if testing.Short() {
   326  		N = 20
   327  	}
   328  
   329  	wg := sync.WaitGroup{}
   330  	for i := 0; i < N; i++ {
   331  		t.Run("interface{}", func(t *testing.T) {
   332  			t.Run("send", func(t *testing.T) {
   333  				// Ensure send to an unbounded channel does not block.
   334  				c := New[interface{}]()
   335  				blocked := false
   336  				wg.Add(1)
   337  				go func() {
   338  					defer wg.Done()
   339  					select {
   340  					case c.In() <- true:
   341  					default:
   342  						blocked = true
   343  					}
   344  				}()
   345  				wg.Wait()
   346  				require.Falsef(t, blocked, "send op to an unbounded channel blocked")
   347  				c.Close()
   348  			})
   349  
   350  			t.Run("recv", func(t *testing.T) {
   351  				// Ensure that receive op from unbounded chan can happen on
   352  				// the same goroutine of send op.
   353  				c := New[interface{}]()
   354  				wg.Add(1)
   355  				go func() {
   356  					defer wg.Done()
   357  					c.In() <- true
   358  					<-c.Out()
   359  				}()
   360  				wg.Wait()
   361  				c.Close()
   362  			})
   363  			t.Run("order", func(t *testing.T) {
   364  				// Ensure that the unbounded channel processes everything FIFO.
   365  				c := New[interface{}]()
   366  				for i := 0; i < 1<<11; i++ {
   367  					c.In() <- i
   368  				}
   369  				for i := 0; i < 1<<11; i++ {
   370  					val := <-c.Out()
   371  					require.Equalf(
   372  						t,
   373  						i,
   374  						val,
   375  						"unbounded channel passes messages in a non-FIFO order",
   376  					)
   377  				}
   378  				c.Close()
   379  			})
   380  		})
   381  		t.Run("struct{}", func(t *testing.T) {
   382  			t.Run("send", func(t *testing.T) {
   383  				// Ensure send to an unbounded channel does not block.
   384  				c := New[struct{}]()
   385  				blocked := false
   386  				wg.Add(1)
   387  				go func() {
   388  					defer wg.Done()
   389  					select {
   390  					case c.In() <- struct{}{}:
   391  					default:
   392  						blocked = true
   393  					}
   394  				}()
   395  				<-c.Out()
   396  				wg.Wait()
   397  				require.Falsef(t, blocked, "send op to an unbounded channel blocked")
   398  				c.Close()
   399  			})
   400  
   401  			t.Run("recv", func(t *testing.T) {
   402  				// Ensure that receive op from unbounded chan can happen on
   403  				// the same goroutine of send op.
   404  				c := New[struct{}]()
   405  				wg.Add(1)
   406  				go func() {
   407  					defer wg.Done()
   408  					c.In() <- struct{}{}
   409  					<-c.Out()
   410  				}()
   411  				wg.Wait()
   412  				c.Close()
   413  			})
   414  			t.Run("order", func(t *testing.T) {
   415  				// Ensure that the unbounded channel processes everything FIFO.
   416  				c := New[struct{}]()
   417  				for i := 0; i < 1<<11; i++ {
   418  					c.In() <- struct{}{}
   419  				}
   420  				n := 0
   421  				for i := 0; i < 1<<11; i++ {
   422  					if _, ok := <-c.Out(); ok {
   423  						n++
   424  					}
   425  				}
   426  				require.Equalf(t, 1<<11, n, "unbounded channel missed a message")
   427  				c.Close()
   428  			})
   429  		})
   430  	}
   431  }
   432  
   433  func TestUnboundedChannClose(t *testing.T) {
   434  	t.Run("close-status", func(t *testing.T) {
   435  		ch := New[any]()
   436  		for i := 0; i < 100; i++ {
   437  			ch.In() <- 0
   438  		}
   439  		ch.Close()
   440  		go func() {
   441  			for range ch.Out() {
   442  			}
   443  		}()
   444  
   445  		// Theoretically, this is not a dead loop. If the channel
   446  		// is closed, then this loop must terminate at somepoint.
   447  		// If not, we will meet timeout in the test.
   448  		for !ch.isClosed() {
   449  			t.Log("unbounded channel is still not entirely closed")
   450  		}
   451  	})
   452  	t.Run("struct{}", func(t *testing.T) {
   453  		grs := runtime.NumGoroutine()
   454  		N := 10
   455  		n := 0
   456  		done := make(chan struct{})
   457  		ch := New[struct{}]()
   458  		for i := 0; i < N; i++ {
   459  			ch.In() <- struct{}{}
   460  		}
   461  		go func() {
   462  			for range ch.Out() {
   463  				n++
   464  			}
   465  			done <- struct{}{}
   466  		}()
   467  		ch.Close()
   468  		<-done
   469  		runtime.GC()
   470  		require.LessOrEqualf(t, runtime.NumGoroutine(), grs+2, "leaking goroutines: %v", n)
   471  		require.Equalf(t, N, n, "After close, not all elements are received")
   472  	})
   473  
   474  	t.Run("interface{}", func(t *testing.T) {
   475  		grs := runtime.NumGoroutine()
   476  		N := 10
   477  		n := 0
   478  		done := make(chan struct{})
   479  		ch := New[interface{}]()
   480  		for i := 0; i < N; i++ {
   481  			ch.In() <- true
   482  		}
   483  		go func() {
   484  			for range ch.Out() {
   485  				n++
   486  			}
   487  			done <- struct{}{}
   488  		}()
   489  		ch.Close()
   490  		<-done
   491  		runtime.GC()
   492  		require.LessOrEqualf(t, runtime.NumGoroutine(), grs+2, "leaking goroutines: %v", n)
   493  		require.Equalf(t, N, n, "After close, not all elements are received")
   494  	})
   495  }
   496  
   497  func BenchmarkUnboundedChann(b *testing.B) {
   498  	b.Run("interface{}", func(b *testing.B) {
   499  		b.Run("sync", func(b *testing.B) {
   500  			c := New[interface{}]()
   501  			defer c.Close()
   502  			b.ResetTimer()
   503  			b.ReportAllocs()
   504  			for i := 0; i < b.N; i++ {
   505  				c.In() <- struct{}{}
   506  				<-c.Out()
   507  			}
   508  		})
   509  		b.Run("chann", func(b *testing.B) {
   510  			c := New[interface{}]()
   511  			defer c.Close()
   512  			b.ResetTimer()
   513  			b.ReportAllocs()
   514  			for i := 0; i < b.N; i++ {
   515  				go func() { c.In() <- struct{}{} }()
   516  				<-c.Out()
   517  			}
   518  		})
   519  	})
   520  	b.Run("struct{}", func(b *testing.B) {
   521  		b.Run("sync", func(b *testing.B) {
   522  			c := New[struct{}]()
   523  			defer c.Close()
   524  			b.ResetTimer()
   525  			b.ReportAllocs()
   526  			for i := 0; i < b.N; i++ {
   527  				c.In() <- struct{}{}
   528  				<-c.Out()
   529  			}
   530  		})
   531  		b.Run("chann", func(b *testing.B) {
   532  			c := New[struct{}]()
   533  			defer c.Close()
   534  			b.ResetTimer()
   535  			b.ReportAllocs()
   536  			for i := 0; i < b.N; i++ {
   537  				go func() { c.In() <- struct{}{} }()
   538  				<-c.Out()
   539  			}
   540  		})
   541  	})
   542  }