github.com/haraldrudell/parl@v0.4.176/counter/access-manager_test.go (about)

     1  /*
     2  © 2023–present Harald Rudell <harald.rudell@gmail.com> (https://haraldrudell.github.io/haraldrudell/)
     3  ISC License
     4  */
     5  
     6  package counter
     7  
     8  import (
     9  	"sync"
    10  	"sync/atomic"
    11  	"testing"
    12  	"time"
    13  )
    14  
    15  const shortTime = time.Millisecond
    16  
    17  func TestAccessManagerAtomic(t *testing.T) {
    18  	var exp1 = ticketDelta
    19  	var exp2 = uint64(0)
    20  
    21  	var manager accessManager
    22  	var actual uint64
    23  
    24  	// start atomic operation
    25  	var isLockAccess = manager.RequestAccess()
    26  	if isLockAccess {
    27  		t.Error("isLockAccess true")
    28  	}
    29  	actual = atomic.LoadUint64(&manager.before)
    30  	if actual != exp1 {
    31  		t.Errorf("before1: %d exp %d", actual, exp1)
    32  	}
    33  
    34  	// end atomic operation
    35  	manager.RelinquishAccess(isLockAccess)
    36  	actual = atomic.LoadUint64(&manager.before)
    37  	if actual != exp2 {
    38  		t.Errorf("before2: %d exp %d", actual, exp2)
    39  	}
    40  }
    41  
    42  type UseLockTester struct {
    43  	lockIsReady, waitToReleaseLock, lockDone sync.WaitGroup
    44  	hasLock                                  chan struct{}
    45  	requestIsReady, requestDone              sync.WaitGroup
    46  	hasTicket                                chan struct{}
    47  }
    48  
    49  func NewUseLockTester() (lockTester *UseLockTester) { return &UseLockTester{} }
    50  func (u *UseLockTester) Lock(manager *accessManager) {
    51  	u.lockIsReady.Add(1)
    52  	u.waitToReleaseLock.Add(1)
    53  	u.lockDone.Add(1)
    54  	u.hasLock = make(chan struct{})
    55  	go u.lock(manager)
    56  }
    57  func (u *UseLockTester) lock(manager *accessManager) {
    58  	defer u.lockDone.Done()
    59  
    60  	u.lockIsReady.Done()
    61  	manager.Lock()
    62  	close(u.hasLock)
    63  	u.waitToReleaseLock.Wait()
    64  	manager.Unlock()
    65  }
    66  func (u *UseLockTester) Request(manager *accessManager) {
    67  	u.requestIsReady.Add(1)
    68  	u.requestDone.Add(1)
    69  	u.hasTicket = make(chan struct{})
    70  	go u.request(manager)
    71  }
    72  func (u *UseLockTester) request(manager *accessManager) {
    73  	defer u.requestDone.Done()
    74  
    75  	u.requestIsReady.Done()
    76  	var ticket = manager.RequestAccess()
    77  	close(u.hasTicket)
    78  	manager.RelinquishAccess(ticket)
    79  }
    80  
    81  func TestAccessManagerLock(t *testing.T) {
    82  
    83  	var manager accessManager
    84  	var lockTester = NewUseLockTester()
    85  	var timer *time.Timer
    86  
    87  	// have ongoing atomic access: blocks Lock
    88  	var ticket = manager.RequestAccess()
    89  
    90  	// have thread wait for lock access
    91  	lockTester.Lock(&manager)
    92  	lockTester.lockIsReady.Wait()
    93  	select {
    94  	case <-lockTester.hasLock:
    95  		t.Error("hasLock prematurely")
    96  	default:
    97  	}
    98  
    99  	// end atomic access: enter lock access
   100  	manager.RelinquishAccess(ticket)
   101  	timer = time.NewTimer(shortTime)
   102  	select {
   103  	case <-timer.C:
   104  		t.Error("hasLock not enabled")
   105  	case <-lockTester.hasLock:
   106  		timer.Stop()
   107  	}
   108  
   109  	// attempt lock access: should be blocked
   110  	lockTester.Request(&manager)
   111  	lockTester.requestIsReady.Wait()
   112  	select {
   113  	case <-lockTester.hasTicket:
   114  		t.Error("hasTicket prematurely")
   115  	default:
   116  	}
   117  
   118  	// make lock available: lock access should happen
   119  	lockTester.waitToReleaseLock.Done()
   120  	timer = time.NewTimer(shortTime)
   121  	select {
   122  	case <-timer.C:
   123  		t.Error("hasTicket not enabled")
   124  	case <-lockTester.hasTicket:
   125  		timer.Stop()
   126  	}
   127  
   128  	lockTester.lockDone.Wait()
   129  	lockTester.requestDone.Wait()
   130  }