forked from thrasher-corp/gocryptotrader
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
(Engine) Bugfix: Unlocking an unlocked mutex PANIC + Increase dispatc…
…her job capacity via commandline (thrasher-corp#371) * Removes lock unlock timer and instead sets unlocks between getting a nonce and sending a payload. Increases dispatch channel buffer to deal with len(enabledCurrencies) > ~100 * Adds additional comments to help explain the situation * Fixes bug that could unlock mutex too early * Fixes LIES where Gemini gets a nonce and then proceeds to declare it doesn't get a nonce causing an unrecoverable lock * Fun new concept! The creation of a tested timed mutex. Unlocking an unlocked mutex cannot occur and response can be checked to verify whether the mutex was unlocked from timeout or command. * Adds new cmd parameter "dispatchjobbuffer" * Expands comments and renames benchmark. Makes `Timer` property private * Happy little linters * Renames jobBuffer and all related instances to jobs limit * Tiny error message update * Grammatical fix and setting dispatch.Start to use defaults
- Loading branch information
1 parent
1805c40
commit 242b02c
Showing
16 changed files
with
301 additions
and
139 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
package timedmutex | ||
|
||
import ( | ||
"sync" | ||
"time" | ||
) | ||
|
||
// NewTimedMutex creates a new timed mutex with a | ||
// specified duration | ||
func NewTimedMutex(length time.Duration) *TimedMutex { | ||
return &TimedMutex{ | ||
duration: length, | ||
} | ||
} | ||
|
||
// LockForDuration will start a timer, lock the mutex, | ||
// then allow the caller to continue | ||
// After the duration, the mutex will be unlocked | ||
func (t *TimedMutex) LockForDuration() { | ||
var wg sync.WaitGroup | ||
wg.Add(1) | ||
go t.lockAndSetTimer(&wg) | ||
wg.Wait() | ||
} | ||
|
||
func (t *TimedMutex) lockAndSetTimer(wg *sync.WaitGroup) { | ||
t.mtx.Lock() | ||
t.setTimer() | ||
wg.Done() | ||
} | ||
|
||
// UnlockIfLocked will unlock the mutex if its currently locked | ||
// Will return true if successfully unlocked | ||
func (t *TimedMutex) UnlockIfLocked() bool { | ||
if t.isTimerNil() { | ||
return false | ||
} | ||
|
||
if !t.stopTimer() { | ||
return false | ||
} | ||
t.mtx.Unlock() | ||
return true | ||
} | ||
|
||
// stopTimer will return true if timer has been stopped by this command | ||
// If the timer has expired, clear the channel | ||
func (t *TimedMutex) stopTimer() bool { | ||
t.timerLock.Lock() | ||
defer t.timerLock.Unlock() | ||
if !t.timer.Stop() { | ||
select { | ||
case <-t.timer.C: | ||
default: | ||
} | ||
return false | ||
} | ||
return true | ||
} | ||
|
||
// isTimerNil safely read locks to detect nil | ||
func (t *TimedMutex) isTimerNil() bool { | ||
t.timerLock.RLock() | ||
defer t.timerLock.RUnlock() | ||
return t.timer == nil | ||
} | ||
|
||
// setTimer safely locks and sets a timer | ||
// which will automatically execute a mutex unlock | ||
// once timer expires | ||
func (t *TimedMutex) setTimer() { | ||
t.timerLock.Lock() | ||
t.timer = time.AfterFunc(t.duration, func() { | ||
t.mtx.Unlock() | ||
}) | ||
t.timerLock.Unlock() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
package timedmutex | ||
|
||
import ( | ||
"testing" | ||
"time" | ||
) | ||
|
||
func BenchmarkTimedMutexTime(b *testing.B) { | ||
tm := NewTimedMutex(20 * time.Millisecond) | ||
for i := 0; i < b.N; i++ { | ||
tm.LockForDuration() | ||
} | ||
} | ||
|
||
func TestConsistencyOfPanicFreeUnlock(t *testing.T) { | ||
t.Parallel() | ||
duration := 20 * time.Millisecond | ||
tm := NewTimedMutex(duration) | ||
for i := 1; i <= 50; i++ { | ||
testUnlockTime := time.Duration(i) * time.Millisecond | ||
tm.LockForDuration() | ||
time.Sleep(testUnlockTime) | ||
tm.UnlockIfLocked() | ||
} | ||
} | ||
|
||
func TestUnlockAfterTimeout(t *testing.T) { | ||
t.Parallel() | ||
tm := NewTimedMutex(time.Second) | ||
tm.LockForDuration() | ||
time.Sleep(2 * time.Second) | ||
wasUnlocked := tm.UnlockIfLocked() | ||
if wasUnlocked { | ||
t.Error("Mutex should have been unlocked by timeout, not command") | ||
} | ||
} | ||
|
||
func TestUnlockBeforeTimeout(t *testing.T) { | ||
t.Parallel() | ||
tm := NewTimedMutex(2 * time.Second) | ||
tm.LockForDuration() | ||
time.Sleep(time.Second) | ||
wasUnlocked := tm.UnlockIfLocked() | ||
if !wasUnlocked { | ||
t.Error("Mutex should have been unlocked by command, not timeout") | ||
} | ||
} | ||
|
||
// TestUnlockAtSameTimeAsTimeout this test ensures | ||
// that even if the timeout and the command occur at | ||
// the same time, no panics occur. The result of the | ||
// 'who' unlocking this doesn't matter, so long as | ||
// the unlock occurs without this test panicking | ||
func TestUnlockAtSameTimeAsTimeout(t *testing.T) { | ||
t.Parallel() | ||
duration := time.Second | ||
tm := NewTimedMutex(duration) | ||
tm.LockForDuration() | ||
time.Sleep(duration) | ||
tm.UnlockIfLocked() | ||
} | ||
|
||
func TestMultipleUnlocks(t *testing.T) { | ||
t.Parallel() | ||
tm := NewTimedMutex(10 * time.Second) | ||
tm.LockForDuration() | ||
wasUnlocked := tm.UnlockIfLocked() | ||
if !wasUnlocked { | ||
t.Error("Mutex should have been unlocked by command, not timeout") | ||
} | ||
wasUnlocked = tm.UnlockIfLocked() | ||
if wasUnlocked { | ||
t.Error("Mutex should have been already unlocked by command") | ||
} | ||
wasUnlocked = tm.UnlockIfLocked() | ||
if wasUnlocked { | ||
t.Error("Mutex should have been already unlocked by command") | ||
} | ||
} | ||
|
||
func TestJustWaitItOut(t *testing.T) { | ||
t.Parallel() | ||
tm := NewTimedMutex(1 * time.Second) | ||
tm.LockForDuration() | ||
time.Sleep(2 * time.Second) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
package timedmutex | ||
|
||
import ( | ||
"sync" | ||
"time" | ||
) | ||
|
||
// TimedMutex is a blocking mutex which will unlock | ||
// after a specified time | ||
type TimedMutex struct { | ||
mtx sync.Mutex | ||
timerLock sync.RWMutex | ||
timer *time.Timer | ||
duration time.Duration | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.