mirror of
https://github.com/status-im/nim-raft.git
synced 2025-02-04 17:24:49 +00:00
Timers + tests
This commit is contained in:
parent
833cc236a3
commit
93fa2d632c
@ -19,6 +19,6 @@ skipDirs = @["tests"]
|
||||
requires "nim >= 1.6.0"
|
||||
requires "stew >= 0.1.0"
|
||||
requires "unittest2 >= 0.0.4"
|
||||
requires "uuid4 >= 0.9.3"
|
||||
requires "uuids >= 0.1.11"
|
||||
|
||||
# Helper functions
|
@ -73,9 +73,9 @@ proc RaftNodeSmApply[SmCommandType, SmStateType](stateMachine: RaftNodeStateMach
|
||||
RaftSmApply(stateMachine, command)
|
||||
|
||||
# Private Abstract Timer manipulation Ops
|
||||
template RaftTimerCreate(timerInterval: int, repeat: bool, timerCallback: RaftTimerCallback): RaftTimer =
|
||||
template RaftTimerCreate(timerInterval: int, oneshot: bool, timerCallback: RaftTimerCallback): RaftTimer =
|
||||
mixin RaftTimerCreateCustomImpl
|
||||
RaftTimerCreateCustomImpl(timerInterval, repeat, timerCallback)
|
||||
RaftTimerCreateCustomImpl(timerInterval, oneshot, timerCallback)
|
||||
|
||||
template RaftTimerCancel(timer: RaftTimer) =
|
||||
mixin RaftTimerCancelCustomImpl
|
||||
|
@ -12,6 +12,7 @@
|
||||
import std/locks
|
||||
import options
|
||||
import stew/results
|
||||
# import uuids/
|
||||
|
||||
export results, options
|
||||
|
||||
@ -23,9 +24,10 @@ type
|
||||
rnsCandidate = 2
|
||||
rnsLeader = 3
|
||||
|
||||
RaftNodeId* = uint64 # uuid4 uniquely identifying every Raft Node
|
||||
RaftNodeTerm* = uint64 # Raft Node Term Type
|
||||
RaftLogIndex* = uint64 # Raft Node Log Index Type
|
||||
UUID = object
|
||||
RaftNodeId* = UUID # uuid4 uniquely identifying every Raft Node
|
||||
RaftNodeTerm* = uint64 # Raft Node Term Type
|
||||
RaftLogIndex* = uint64 # Raft Node Log Index Type
|
||||
|
||||
RaftNodePeer* = object # Raft Node Peer object
|
||||
id*: RaftNodeId
|
||||
@ -63,7 +65,7 @@ type
|
||||
raftNodeAccessCallback: RaftNodeAccessCallback[SmCommandType, SmStateType]
|
||||
|
||||
# Callback for sending messages out of this Raft Node
|
||||
RaftMessageId* = uint64 # UUID assigned to every Raft Node Message,
|
||||
RaftMessageId* = UUID # UUID assigned to every Raft Node Message,
|
||||
# so it can be matched with it's corresponding response etc.
|
||||
|
||||
RaftMessageBase* = ref object of RootObj # Base Type for Raft Protocol Messages
|
||||
@ -97,14 +99,14 @@ type
|
||||
logData*: seq[RaftNodeLogEntry[SmCommandType]] # Raft Node Log Data
|
||||
|
||||
# Timer types
|
||||
RaftTimer* = object
|
||||
RaftTimer* = ref object
|
||||
mtx*: Lock
|
||||
canceled*: bool
|
||||
expired*: bool
|
||||
timeout*: int
|
||||
repeat*: bool
|
||||
oneshot*: bool
|
||||
|
||||
RaftTimerCallback* = proc (timer: var RaftTimer) {.nimcall, gcsafe.} # Pass any function wrapped in a closure
|
||||
RaftTimerCallback* = proc (timer: RaftTimer) {.nimcall, gcsafe.} # Pass any function wrapped in a closure
|
||||
|
||||
# Raft Node Object type
|
||||
RaftNode*[SmCommandType, SmStateType] = object
|
||||
|
@ -18,52 +18,55 @@ var
|
||||
runningMtx: Lock
|
||||
running: bool
|
||||
|
||||
proc RaftTimerCreateCustomImpl*(timerInterval: int, repeat: bool, timerCallback: RaftTimerCallback): RaftTimer =
|
||||
proc RaftTimerCreateCustomImpl*(timerInterval: int, oneshot: bool, timerCallback: RaftTimerCallback): RaftTimer {.nimcall, gcsafe.} =
|
||||
var
|
||||
timer = RaftTimer(canceled: false, expired: false, timeout: timerInterval, repeat: repeat)
|
||||
timer = RaftTimer(mtx: Lock(), canceled: false, expired: false, timeout: timerInterval, oneshot: oneshot)
|
||||
|
||||
initLock(timer.mtx)
|
||||
|
||||
proc CallbackClosureProc(): Callback =
|
||||
result = proc (fd: AsyncFD): bool {.closure, gcsafe.} =
|
||||
withLock(timer.mtx):
|
||||
if not timer.canceled:
|
||||
timerCallback(timer)
|
||||
if not timer.repeat:
|
||||
timer.expired = true
|
||||
addTimer(timer.timeout, timer.oneshot, proc (fd: AsyncFD): bool {.closure, gcsafe.} =
|
||||
withLock(timer.mtx):
|
||||
if not timer.canceled:
|
||||
timerCallback(timer)
|
||||
if timer.oneshot:
|
||||
timer.expired = true
|
||||
return true
|
||||
else:
|
||||
return false
|
||||
|
||||
debugEcho repr(CallbackClosureProc())
|
||||
addTimer(timer.timeout, timer.repeat, CallbackClosureProc())
|
||||
else:
|
||||
return true
|
||||
)
|
||||
timer
|
||||
|
||||
proc RaftTimerCancelCustomImpl*(timer: var RaftTimer): bool {.discardable.} =
|
||||
withLock(timer.mtx):
|
||||
if not timer.expired and not timer.canceled:
|
||||
timer.canceled = true
|
||||
return true
|
||||
else:
|
||||
return false
|
||||
proc RaftTimerCancelCustomImpl*(timer: RaftTimer): bool {.nimcall, gcsafe, discardable.} =
|
||||
withLock(timer.mtx):
|
||||
if not timer.expired and not timer.canceled:
|
||||
timer.canceled = true
|
||||
else:
|
||||
return false
|
||||
|
||||
proc RaftTimerPollThread() {.thread, nimcall, gcsafe.} =
|
||||
while running:
|
||||
try:
|
||||
poll()
|
||||
except ValueError as e:
|
||||
debugEcho e.msg
|
||||
# debugEcho e.msg
|
||||
# Add a 'dummy' timer if no other handles are present to prevent more
|
||||
# ValueError exceptions this is a workaround for a asyncdyspatch bug
|
||||
# see - https://github.com/nim-lang/Nim/issues/14564
|
||||
addTimer(1, false, proc (fd: AsyncFD): bool {.closure, gcsafe.} = false)
|
||||
|
||||
proc RaftTimerJoinPollThread*() =
|
||||
proc RaftTimerJoinPollThread*() {.nimcall, gcsafe.} =
|
||||
joinThread(pollThr)
|
||||
|
||||
proc RaftTimerStartCustomImpl*(joinThread: bool = true) =
|
||||
proc RaftTimerStartCustomImpl*(joinThread: bool = true) {.nimcall, gcsafe.} =
|
||||
withLock(runningMtx):
|
||||
running = true
|
||||
createThread(pollThr, RaftTimerPollThread)
|
||||
if joinThread:
|
||||
RaftTimerJoinPollThread()
|
||||
|
||||
proc RaftTimerStopCustomImpl*(joinThread: bool = true) =
|
||||
proc RaftTimerStopCustomImpl*(joinThread: bool = true) {.nimcall, gcsafe.} =
|
||||
withLock(runningMtx):
|
||||
running = false
|
||||
if joinThread:
|
||||
|
@ -11,46 +11,51 @@ import unittest2
|
||||
import ../raft/types
|
||||
import std/locks
|
||||
import basic_timers
|
||||
import random
|
||||
|
||||
var
|
||||
cancelCond: Cond
|
||||
cancelLock: Lock
|
||||
|
||||
initLock(cancelLock)
|
||||
initCond(cancelCond)
|
||||
const
|
||||
MAX_TIMERS = 50
|
||||
SLOW_TIMERS_MIN = 300
|
||||
SLOW_TIMERS_MAX = 350
|
||||
FAST_TIMERS_MIN = 20
|
||||
FAST_TIMERS_MAX = 150
|
||||
WAIT_FOR_SLOW_TIMERS = 225
|
||||
FINAL_WAIT = 125
|
||||
|
||||
proc timersRunner() =
|
||||
const
|
||||
MAX_TIMERS = 50
|
||||
var
|
||||
slowTimers: array[0..MAX_TIMERS, RaftTimer]
|
||||
fastTimers: array[0..MAX_TIMERS, RaftTimer]
|
||||
cancelTimer: RaftTimer
|
||||
|
||||
proc CancelTimerCallbackClosure(
|
||||
slowTimers: var array[0..MAX_TIMERS, RaftTimer],
|
||||
fastTimers: var array[0..MAX_TIMERS, RaftTimer]
|
||||
): RaftTimerCallback =
|
||||
result = proc (timer: var RaftTimer) {.nimcall, gcsafe.} =
|
||||
debugEcho "Aahjsbdghajsdhjgshgjd"
|
||||
signal(cancelCond)
|
||||
var
|
||||
RaftDummyTimerCallback = proc (timer: RaftTimer) {.nimcall, gcsafe.} =
|
||||
discard
|
||||
|
||||
suite "Create and test basic timers":
|
||||
test "Create 50 slow timers (100-150 ms)":
|
||||
check true
|
||||
test "Create 50 fast timers (20-50 ms)":
|
||||
check true
|
||||
test "Create cancel timer":
|
||||
check true
|
||||
test "Start timers":
|
||||
cancelTimer = RaftTimerCreateCustomImpl(250, false, CancelTimerCallbackClosure(slowTimers, fastTimers))
|
||||
RaftTimerStartCustomImpl(joinThread=false)
|
||||
debugEcho repr(cancelTimer)
|
||||
RaftTimerStartCustomImpl(false)
|
||||
check true
|
||||
test "Wait cancel timer 250 ms and stop timers":
|
||||
wait(cancelCond, cancelLock)
|
||||
test "Create 'slow' timers":
|
||||
for i in 0..MAX_TIMERS:
|
||||
slowTimers[i] = RaftTimerCreateCustomImpl(max(SLOW_TIMERS_MIN, rand(SLOW_TIMERS_MAX)), true, RaftDummyTimerCallback)
|
||||
check true
|
||||
test "Create 'fast' timers":
|
||||
for i in 0..MAX_TIMERS:
|
||||
fastTimers[i] = RaftTimerCreateCustomImpl(max(FAST_TIMERS_MIN, rand(FAST_TIMERS_MAX)), true, RaftDummyTimerCallback)
|
||||
check true
|
||||
test "Wait for and cancel 'slow' timers":
|
||||
waitFor sleepAsync(WAIT_FOR_SLOW_TIMERS)
|
||||
for i in 0..MAX_TIMERS:
|
||||
RaftTimerCancelCustomImpl(slowTimers[i])
|
||||
check true
|
||||
test "Wait and stop timers":
|
||||
waitFor sleepAsync(FINAL_WAIT)
|
||||
RaftTimerStopCustomImpl(true)
|
||||
check true
|
||||
test "Check timers consistency":
|
||||
for i in 0..MAX_TIMERS:
|
||||
if not fastTimers[i].expired or not slowTimers[i].canceled:
|
||||
check false
|
||||
check true
|
||||
|
||||
if isMainModule:
|
||||
|
Loading…
x
Reference in New Issue
Block a user