From ce00a07d88f3bc529ceca44d203b809c075e8c15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Zdyba=C5=82?= Date: Wed, 5 Jul 2023 00:05:28 +0200 Subject: [PATCH] refactor(sync): get rid of RWMutex and TryLock --- sync/sync.go | 2 +- sync/sync_getter.go | 24 +++++++++++++++++------- sync/sync_getter_test.go | 1 + 3 files changed, 19 insertions(+), 8 deletions(-) diff --git a/sync/sync.go b/sync/sync.go index ce66b6a9..733baac9 100644 --- a/sync/sync.go +++ b/sync/sync.go @@ -72,7 +72,7 @@ func NewSyncer[H header.Header]( return &Syncer[H]{ sub: sub, store: syncStore[H]{Store: store}, - getter: syncGetter[H]{Getter: getter}, + getter: *newSyncGetter(getter), triggerSync: make(chan struct{}, 1), // should be buffered Params: ¶ms, }, nil diff --git a/sync/sync_getter.go b/sync/sync_getter.go index 7b81ee01..88f6df96 100644 --- a/sync/sync_getter.go +++ b/sync/sync_getter.go @@ -10,32 +10,36 @@ import ( // syncGetter is a Getter wrapper that ensure only one Head call happens at the time type syncGetter[H header.Header] struct { - getterLk sync.RWMutex + getterLk sync.Mutex + getterCond *sync.Cond isGetterLk atomic.Bool header.Getter[H] } +func newSyncGetter[H header.Header](g header.Getter[H]) *syncGetter[H] { + getter := syncGetter[H]{Getter: g} + getter.getterCond = sync.NewCond(&getter.getterLk) + return &getter +} + // Lock locks the getter for single user. // Reports 'true' if the lock was held by the current routine. // Does not require unlocking on 'false'. func (se *syncGetter[H]) Lock() bool { // the lock construction here ensures only one routine is freed at a time // while others wait via Rlock - locked := se.getterLk.TryLock() + locked := se.isGetterLk.CompareAndSwap(false, true) if !locked { - se.getterLk.RLock() - defer se.getterLk.RUnlock() - return false + se.await() } - se.isGetterLk.Store(locked) return locked } // Unlock unlocks the getter. func (se *syncGetter[H]) Unlock() { se.checkLock("Unlock without preceding Lock on syncGetter") - se.getterLk.Unlock() se.isGetterLk.Store(false) + se.getterCond.Broadcast() } // Head must be called with held Lock. @@ -50,3 +54,9 @@ func (se *syncGetter[H]) checkLock(msg string) { panic(msg) } } + +func (se *syncGetter[H]) await() { + se.getterLk.Lock() + se.getterCond.Wait() + se.getterLk.Unlock() +} diff --git a/sync/sync_getter_test.go b/sync/sync_getter_test.go index 06fcd7ad..5989f812 100644 --- a/sync/sync_getter_test.go +++ b/sync/sync_getter_test.go @@ -20,6 +20,7 @@ func TestSyncGetterHead(t *testing.T) { fex := &fakeGetter[*headertest.DummyHeader]{} sex := &syncGetter[*headertest.DummyHeader]{Getter: fex} + sex.getterCond = sync.NewCond(&sex.getterLk) var wg sync.WaitGroup for i := 0; i < 100; i++ {