From 4fff78903df907354166a8a771af32b6a1945ea7 Mon Sep 17 00:00:00 2001 From: Ben Date: Tue, 11 Feb 2025 15:33:40 +0100 Subject: [PATCH] sets up main loop --- codexcrawler/application.nim | 72 +++++++------------------------- codexcrawler/state.nim | 22 ++++++++-- tests/codexcrawler/mockstate.nim | 1 + tests/codexcrawler/teststate.nim | 32 ++++++++++++++ tests/test.nim | 1 + 5 files changed, 68 insertions(+), 60 deletions(-) create mode 100644 tests/codexcrawler/teststate.nim diff --git a/codexcrawler/application.nim b/codexcrawler/application.nim index e207544..3cd17cf 100644 --- a/codexcrawler/application.nim +++ b/codexcrawler/application.nim @@ -8,60 +8,19 @@ import pkg/metrics import ./config import ./utils/logging -import ./metrics -import ./list -import ./utils/datastoreutils import ./utils/asyncdataevent import ./installer import ./state import ./component import ./types -type - ApplicationStatus* {.pure.} = enum - Stopped - Stopping - Running +type Application* = ref object + state: State - Application* = ref object - status: ApplicationStatus - config*: Config - todoNodes*: List - okNodes*: List - nokNodes*: List - -# proc initializeLists(app: Application): Future[?!void] {.async.} = -# without store =? createTypedDatastore(app.config.dataDir / "lists"), err: -# return failure(err) - -# # We can't extract this into a function because gauges cannot be passed as argument. -# # The use of global state in nim-metrics is not pleasant. -# proc onTodoMetric(value: int64) = -# todoNodesGauge.set(value) - -# proc onOkMetric(value: int64) = -# okNodesGauge.set(value) - -# proc onNokMetric(value: int64) = -# nokNodesGauge.set(value) - -# app.todoNodes = List.new("todo", store, onTodoMetric) -# app.okNodes = List.new("ok", store, onOkMetric) -# app.nokNodes = List.new("nok", store, onNokMetric) - -# if err =? (await app.todoNodes.load()).errorOption: -# return failure(err) -# if err =? (await app.okNodes.load()).errorOption: -# return failure(err) -# if err =? (await app.nokNodes.load()).errorOption: -# return failure(err) - -# return success() - -proc initializeApp(app: Application): Future[?!void] {.async.} = - # todo move this +proc initializeApp(app: Application, config: Config): Future[?!void] {.async.} = let state = State( - config: app.config, + status: ApplicationStatus.Running, + config: config, events: Events( nodesFound: newAsyncDataEvent[seq[Nid]](), newNodesDiscovered: newAsyncDataEvent[seq[Nid]](), @@ -81,30 +40,29 @@ proc initializeApp(app: Application): Future[?!void] {.async.} = return success() proc stop*(app: Application) = - app.status = ApplicationStatus.Stopping - # waitFor app.dht.stop() + app.state.status = ApplicationStatus.Stopping proc run*(app: Application) = - app.config = parseConfig() - info "Loaded configuration", config = app.config + let config = parseConfig() + info "Loaded configuration", config = $config # Configure loglevel - updateLogLevel(app.config.logLevel) + updateLogLevel(config.logLevel) # Ensure datadir path exists: - if not existsDir(app.config.dataDir): - createDir(app.config.dataDir) + if not existsDir(config.dataDir): + createDir(config.dataDir) info "Metrics endpoint initialized" info "Starting application" - app.status = ApplicationStatus.Running - if err =? (waitFor app.initializeApp()).errorOption: - app.status = ApplicationStatus.Stopping + app.state.status = ApplicationStatus.Running + if err =? (waitFor app.initializeApp(config)).errorOption: + app.state.status = ApplicationStatus.Stopping error "Failed to start application", err = err.msg return - while app.status == ApplicationStatus.Running: + while app.state.status == ApplicationStatus.Running: try: chronos.poll() except Exception as exc: diff --git a/codexcrawler/state.nim b/codexcrawler/state.nim index 9d8d4be..e32e856 100644 --- a/codexcrawler/state.nim +++ b/codexcrawler/state.nim @@ -1,10 +1,14 @@ import pkg/chronos +import pkg/chronicles import pkg/questionable/results import ./config import ./utils/asyncdataevent import ./types +logScope: + topics = "state" + type OnStep = proc(): Future[?!void] {.async: (raises: []), gcsafe.} @@ -18,10 +22,22 @@ type dhtNodeCheck*: AsyncDataEvent[DhtNodeCheckEventData] nodesExpired*: AsyncDataEvent[seq[Nid]] + ApplicationStatus* {.pure.} = enum + Stopped + Stopping + Running + State* = ref object of RootObj + status*: ApplicationStatus config*: Config events*: Events -proc whileRunning*(this: State, step: OnStep, delay: Duration) = - discard - #todo: while status == running, step(), asyncsleep duration +proc whileRunning*(s: State, step: OnStep, delay: Duration) {.async.} = + proc worker(): Future[void] {.async.} = + while s.status == ApplicationStatus.Running: + if err =? (await step()).errorOption: + error "Failure-result caught in main loop. Stopping...", err = err.msg + s.status = ApplicationStatus.Stopping + await sleepAsync(delay) + + asyncSpawn worker() diff --git a/tests/codexcrawler/mockstate.nim b/tests/codexcrawler/mockstate.nim index 1a305a9..7721b08 100644 --- a/tests/codexcrawler/mockstate.nim +++ b/tests/codexcrawler/mockstate.nim @@ -8,6 +8,7 @@ type MockState* = ref object of State proc createMockState*(): MockState = MockState( + status: ApplicationStatus.Running, config: Config(), events: Events( nodesFound: newAsyncDataEvent[seq[Nid]](), diff --git a/tests/codexcrawler/teststate.nim b/tests/codexcrawler/teststate.nim new file mode 100644 index 0000000..3a1493e --- /dev/null +++ b/tests/codexcrawler/teststate.nim @@ -0,0 +1,32 @@ +import pkg/chronos +import pkg/questionable/results +import pkg/asynctest/chronos/unittest + +import ../../codexcrawler/state +import ./mockstate + +suite "State": + var state: State + + setup: + # The behavior we're testing is the same for the mock + state = createMockState() + + test "whileRunning": + var counter = 0 + + proc onStep(): Future[?!void] {.async: (raises: []), gcsafe.} = + inc counter + return success() + + await state.whileRunning(onStep, 1.milliseconds) + + while counter < 5: + await sleepAsync(1.milliseconds) + + state.status = ApplicationStatus.Stopped + + await sleepAsync(10.milliseconds) + + check: + counter == 5 diff --git a/tests/test.nim b/tests/test.nim index a310fc6..1df02d1 100644 --- a/tests/test.nim +++ b/tests/test.nim @@ -1,5 +1,6 @@ import ./codexcrawler/testutils import ./codexcrawler/testcomponents import ./codexcrawler/testtypes +import ./codexcrawler/teststate {.warning[UnusedImport]: off.}