github.com/ydb-platform/ydb-go-sdk/v3@v3.57.0/internal/xsync/event_broadcast_test.go (about)

     1  package xsync
     2  
     3  import (
     4  	"runtime"
     5  	"sync/atomic"
     6  	"testing"
     7  	"time"
     8  
     9  	"github.com/stretchr/testify/require"
    10  
    11  	"github.com/ydb-platform/ydb-go-sdk/v3/internal/empty"
    12  	"github.com/ydb-platform/ydb-go-sdk/v3/internal/xtest"
    13  )
    14  
    15  func TestEventBroadcast(t *testing.T) {
    16  	t.Run("Simple", func(t *testing.T) {
    17  		b := &EventBroadcast{}
    18  		waiter := b.Waiter()
    19  		b.Broadcast()
    20  		xtest.WaitChannelClosed(t, waiter.Done())
    21  	})
    22  
    23  	xtest.TestManyTimesWithName(t, "SubscribeAndEventsInRace", func(t testing.TB) {
    24  		testDuration := time.Second / 100
    25  
    26  		b := &EventBroadcast{}
    27  		var events atomic.Int64
    28  
    29  		var backgroundCounter atomic.Int64
    30  		firstWaiterStarted := atomic.Bool{}
    31  
    32  		stopSubscribe := atomic.Bool{}
    33  
    34  		subscribeStopped := make(empty.Chan)
    35  		broadcastStopped := make(empty.Chan)
    36  
    37  		// Add subscribers
    38  		go func() {
    39  			defer close(subscribeStopped)
    40  			for {
    41  				backgroundCounter.Add(1)
    42  				waiter := b.Waiter()
    43  				firstWaiterStarted.Store(true)
    44  				go func() {
    45  					<-waiter.Done()
    46  					backgroundCounter.Add(-1)
    47  				}()
    48  				if stopSubscribe.Load() {
    49  					return
    50  				}
    51  			}
    52  		}()
    53  
    54  		stopBroadcast := atomic.Bool{}
    55  		go func() {
    56  			defer close(broadcastStopped)
    57  
    58  			// Fire events
    59  			for {
    60  				events.Add(1)
    61  				b.Broadcast()
    62  				runtime.Gosched()
    63  				if stopBroadcast.Load() {
    64  					return
    65  				}
    66  			}
    67  		}()
    68  
    69  		xtest.SpinWaitCondition(t, nil, firstWaiterStarted.Load)
    70  
    71  		<-time.After(testDuration)
    72  
    73  		stopSubscribe.Store(true)
    74  		<-subscribeStopped
    75  
    76  		for {
    77  			oldCounter := backgroundCounter.Load()
    78  			if oldCounter == 0 {
    79  				break
    80  			}
    81  
    82  			t.Log("background counter", oldCounter)
    83  			xtest.SpinWaitCondition(t, nil, func() bool {
    84  				return backgroundCounter.Load() < oldCounter
    85  			})
    86  		}
    87  		stopBroadcast.Store(true)
    88  		<-broadcastStopped
    89  
    90  		require.Greater(t, events.Load(), int64(0))
    91  	})
    92  }