github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/dm/master/scheduler/latch_test.go (about)

     1  // Copyright 2021 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  package scheduler
    15  
    16  import (
    17  	"sort"
    18  	"sync"
    19  	"testing"
    20  	"time"
    21  
    22  	"github.com/stretchr/testify/require"
    23  )
    24  
    25  func TestOneAcquireSuccess(t *testing.T) {
    26  	t.Parallel()
    27  
    28  	var (
    29  		l       = newLatches()
    30  		fire    = make(chan struct{})
    31  		success = make(chan string, 2)
    32  		fail    = make(chan struct{}, 8)
    33  		group1  = "group1"
    34  		group2  = "group2"
    35  		wg      sync.WaitGroup
    36  	)
    37  
    38  	for i := 0; i < 10; i++ {
    39  		var group string
    40  		if i < 5 {
    41  			group = group1
    42  		} else {
    43  			group = group2
    44  		}
    45  
    46  		wg.Add(1)
    47  		go func(name string) {
    48  			defer wg.Done()
    49  			<-fire
    50  			_, err := l.tryAcquire(name)
    51  			if err != nil {
    52  				fail <- struct{}{}
    53  			} else {
    54  				success <- name
    55  			}
    56  		}(group)
    57  	}
    58  
    59  	for i := 0; i < 10; i++ {
    60  		fire <- struct{}{}
    61  	}
    62  	wg.Wait()
    63  
    64  	require.Len(t, fail, 8)
    65  
    66  	var succNames []string
    67  	succNames = append(succNames, <-success)
    68  	succNames = append(succNames, <-success)
    69  	sort.Strings(succNames)
    70  	require.Equal(t, []string{group1, group2}, succNames)
    71  }
    72  
    73  func TestAcquireAfterRelease(t *testing.T) {
    74  	t.Parallel()
    75  
    76  	var (
    77  		l       = newLatches()
    78  		fire    = make(chan struct{})
    79  		success int
    80  		group   = "group1"
    81  		wg      sync.WaitGroup
    82  	)
    83  
    84  	for i := 0; i < 5; i++ {
    85  		wg.Add(1)
    86  		go func() {
    87  			defer wg.Done()
    88  			<-fire
    89  
    90  			for {
    91  				release, err := l.tryAcquire(group)
    92  				if err != nil {
    93  					time.Sleep(10 * time.Millisecond)
    94  				} else {
    95  					time.Sleep(20 * time.Millisecond)
    96  					success++
    97  					release()
    98  					return
    99  				}
   100  			}
   101  		}()
   102  	}
   103  
   104  	for i := 0; i < 5; i++ {
   105  		fire <- struct{}{}
   106  	}
   107  
   108  	wg.Wait()
   109  	require.Equal(t, 5, success)
   110  }
   111  
   112  func TestMultiRelease(t *testing.T) {
   113  	t.Parallel()
   114  
   115  	var (
   116  		l     = newLatches()
   117  		names = []string{"name1", "name2", "name3"}
   118  		fire  = make(chan struct{}, len(names))
   119  		wg    sync.WaitGroup
   120  	)
   121  
   122  	for repeat := 0; repeat < 3; repeat++ {
   123  		for i := range names {
   124  			wg.Add(1)
   125  			go func(name string) {
   126  				defer wg.Done()
   127  				<-fire
   128  				release, err := l.tryAcquire(name)
   129  				require.NoError(t, err)
   130  				release()
   131  				// will not panic or cause other error
   132  				release()
   133  			}(names[i])
   134  		}
   135  
   136  		for range names {
   137  			fire <- struct{}{}
   138  		}
   139  		wg.Wait()
   140  	}
   141  }
   142  
   143  func TestWontReleaseOther(t *testing.T) {
   144  	t.Parallel()
   145  
   146  	var (
   147  		l     = newLatches()
   148  		group = "group1"
   149  	)
   150  
   151  	release1, err := l.tryAcquire(group)
   152  	require.NoError(t, err)
   153  	release1()
   154  
   155  	// because release1 is called, another tryAcquire should succeed
   156  	release2, err := l.tryAcquire(group)
   157  	require.NoError(t, err)
   158  
   159  	// release1 should not release the latch of release2, we test this by tryAcquire
   160  	release1()
   161  	_, err = l.tryAcquire(group)
   162  	require.Error(t, err)
   163  
   164  	release2()
   165  	_, err = l.tryAcquire(group)
   166  	require.NoError(t, err)
   167  }