From a1835cc6788b83f7830e14a311a392fc34c06605 Mon Sep 17 00:00:00 2001 From: Gabriel mermelstein Date: Tue, 8 Apr 2025 14:46:27 +0300 Subject: [PATCH 01/29] start refactor --- Makefile | 4 + bindings/generated/libbindings.dylib | Bin 665080 -> 0 bytes bindings/bindings.h => library/libsds.h | 0 bindings/bindings.nim => library/libsds.nim | 143 +++++++++++++------- reliability.nimble | 7 +- sds_wrapper.go | 9 +- 6 files changed, 105 insertions(+), 58 deletions(-) create mode 100644 Makefile delete mode 100755 bindings/generated/libbindings.dylib rename bindings/bindings.h => library/libsds.h (100%) rename bindings/bindings.nim => library/libsds.nim (73%) diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..443bddc --- /dev/null +++ b/Makefile @@ -0,0 +1,4 @@ +.PHONY: libsds + +libsds: + nim c --app:lib --mm:refc --outdir:build library/libsds.nim \ No newline at end of file diff --git a/bindings/generated/libbindings.dylib b/bindings/generated/libbindings.dylib deleted file mode 100755 index 8fb1999cb1c963e69f3e694414b41fd63368adc2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 665080 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 z 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%0Dm#>uP2oz$c7WOJvO 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 0: copyMem(bufferPtr, cast[pointer](s[0].unsafeAddr), len.Natural) return (bufferPtr, len.csize_t) -proc allocSeqCString*(s: seq[string]): (ptr cstring, csize_t) {.inline, gcsafe, cdecl.} = - if s.len == 0: return (nil, 0) +proc allocSeqCString*( + s: seq[string] +): (ptr cstring, csize_t) {.inline, gcsafe, cdecl.} = + if s.len == 0: + return (nil, 0) let count = s.len # Allocate memory for 'count' cstring pointers, cast to ptr UncheckedArray let arrPtr = cast[ptr UncheckedArray[cstring]](allocShared(count * sizeof(cstring))) - for i in 0.. 0: - return CWrapResult(base_result: toCResultErrStr("Message pointer is NULL but length > 0")) + return CWrapResult( + base_result: toCResultErrStr("Message pointer is NULL but length > 0") + ) if messageIdCStr == nil: - return CWrapResult(base_result: toCResultErrStr("Message ID pointer is NULL")) + return CWrapResult(base_result: toCResultErrStr("Message ID pointer is NULL")) let messageId = $messageIdCStr var messageNim: seq[byte] @@ -228,20 +266,23 @@ proc WrapOutgoingMessage*(handle: CReliabilityManagerHandle, messageC: pointer, if wrapResult.isOk: let (wrappedDataPtr, wrappedDataLen) = allocSeqByte(wrapResult.get()) return CWrapResult( - base_result: toCResultOk(), - message: wrappedDataPtr, - message_len: wrappedDataLen + base_result: toCResultOk(), message: wrappedDataPtr, message_len: wrappedDataLen ) else: return CWrapResult(base_result: toCResultErr(wrapResult.error)) -proc UnwrapReceivedMessage*(handle: CReliabilityManagerHandle, messageC: pointer, messageLen: csize_t): CUnwrapResult {.exportc, dynlib, cdecl.} = # Keep non-gcsafe +proc UnwrapReceivedMessage*( + handle: CReliabilityManagerHandle, messageC: pointer, messageLen: csize_t +): CUnwrapResult {.exportc, dynlib, cdecl.} = # Keep non-gcsafe if handle == nil: - return CUnwrapResult(base_result: toCResultErrStr("ReliabilityManager handle is NULL")) + return + CUnwrapResult(base_result: toCResultErrStr("ReliabilityManager handle is NULL")) let rm = cast[ReliabilityManager](handle) if messageC == nil and messageLen > 0: - return CUnwrapResult(base_result: toCResultErrStr("Message pointer is NULL but length > 0")) + return CUnwrapResult( + base_result: toCResultErrStr("Message pointer is NULL but length > 0") + ) var messageNim: seq[byte] if messageLen > 0: @@ -260,12 +301,14 @@ proc UnwrapReceivedMessage*(handle: CReliabilityManagerHandle, messageC: pointer message: contentPtr, message_len: contentLen, missing_deps: depsPtr, - missing_deps_count: depsCount + missing_deps_count: depsCount, ) else: return CUnwrapResult(base_result: toCResultErr(unwrapResult.error)) -proc MarkDependenciesMet*(handle: CReliabilityManagerHandle, messageIDsC: ptr cstring, count: csize_t): CResult {.exportc, dynlib, cdecl.} = # Keep non-gcsafe +proc MarkDependenciesMet*( + handle: CReliabilityManagerHandle, messageIDsC: ptr cstring, count: csize_t +): CResult {.exportc, dynlib, cdecl.} = # Keep non-gcsafe if handle == nil: return toCResultErrStr("ReliabilityManager handle is NULL") let rm = cast[ReliabilityManager](handle) @@ -276,7 +319,7 @@ proc MarkDependenciesMet*(handle: CReliabilityManagerHandle, messageIDsC: ptr cs var messageIDsNim = newSeq[string](count) # Cast to ptr UncheckedArray for indexing let messageIDsCArray = cast[ptr UncheckedArray[cstring]](messageIDsC) - for i in 0.. // For C.free -#include "bindings/bindings.h" // Update include path +#include "library/libsds.h" // Update include path // Forward declaration for the single Go callback relay function extern void globalCallbackRelay(void* handle, CEventType eventType, void* data1, void* data2, size_t data3); @@ -229,6 +229,7 @@ func StartPeriodicTasks(handle ReliabilityManagerHandle) error { // globalCallbackRelay is called by Nim for all events. // It uses the handle to find the correct Go Callbacks struct and dispatch the call. +// //export globalCallbackRelay func globalCallbackRelay(handle unsafe.Pointer, eventType C.CEventType, data1 unsafe.Pointer, data2 unsafe.Pointer, data3 C.size_t) { goHandle := ReliabilityManagerHandle(handle) From 07329d70daba6b31e68a2cfd4dd15d8b8b5ed8f1 Mon Sep 17 00:00:00 2001 From: Gabriel mermelstein Date: Tue, 8 Apr 2025 15:31:57 +0300 Subject: [PATCH 02/29] restructuring --- .gitignore | 2 + Makefile | 19 ++++++- library/libsds.h | 133 ++++++++------------------------------------- reliability.nimble | 29 ---------- sds.nimble | 39 +++++++++++++ sds_wrapper.go | 2 +- 6 files changed, 81 insertions(+), 143 deletions(-) delete mode 100644 reliability.nimble create mode 100644 sds.nimble diff --git a/.gitignore b/.gitignore index b8be30d..6f5bb9f 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,5 @@ nph docs for_reference do_not_commit +build/* +sds.nims diff --git a/Makefile b/Makefile index 443bddc..3ab8b78 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,19 @@ .PHONY: libsds -libsds: - nim c --app:lib --mm:refc --outdir:build library/libsds.nim \ No newline at end of file +sds.nims: + ln -s sds.nimble $@ + +deps: | sds.nims + + +STATIC ?= 0 + +libsds: deps + rm -f build/libwaku* +ifeq ($(STATIC), 1) + echo -e $(BUILD_MSG) "build/$@.a" && \ + $(ENV_SCRIPT) nim libsdsStatic $(NIM_PARAMS) sds.nims +else + echo -e $(BUILD_MSG) "build/$@.so" && \ + $(ENV_SCRIPT) nim libsdsDynamic $(NIM_PARAMS) sds.nims +endif \ No newline at end of file diff --git a/library/libsds.h b/library/libsds.h index 67fd179..f34daf4 100644 --- a/library/libsds.h +++ b/library/libsds.h @@ -1,56 +1,24 @@ -#ifndef BINDINGS_H -#define BINDINGS_H -#include // For size_t -#include // For standard integer types -#include // For bool type +// Generated manually and inspired by the one generated by the Nim Compiler. +// In order to see the header file generated by Nim just run `make libsds` +// from the root repo folder and the header should be created in +// nimcache/release/libsds/libsds.h +#ifndef __libsds__ +#define __libsds__ + +#include +#include + +// The possible returned values for the functions that return int +#define RET_OK 0 +#define RET_ERR 1 +#define RET_MISSING_CALLBACK 2 #ifdef __cplusplus extern "C" { #endif - -// Opaque struct declaration (handle replaces direct pointer usage) -typedef struct ReliabilityManager ReliabilityManager; // Keep forward declaration - -// Define MessageID as a C string -typedef const char* MessageID; // Keep const for the typedef itself - -// --- Result Types --- - -typedef struct { - bool is_ok; - char* error_message; -} CResult; - -typedef struct { - CResult base_result; - unsigned char* message; - size_t message_len; - MessageID* missing_deps; - size_t missing_deps_count; -} CUnwrapResult; - -typedef struct { - CResult base_result; - unsigned char* message; - size_t message_len; -} CWrapResult; - - -// --- Callback Function Pointer Types --- - -// Define event types (enum or constants) -typedef enum { - EVENT_MESSAGE_READY = 1, - EVENT_MESSAGE_SENT = 2, - EVENT_MISSING_DEPENDENCIES = 3, - EVENT_PERIODIC_SYNC = 4 -} CEventType; - -// Single callback type for all events -// Nim will call this, passing the handle and event-specific data -typedef void (*CEventCallback)(void* handle, CEventType eventType, void* data1, void* data2, size_t data3); +typedef void (*SdsCallBack) (int callerRet, const char* msg, size_t len, void* userData); // --- Core API Functions --- @@ -60,73 +28,16 @@ typedef void (*CEventCallback)(void* handle, CEventType eventType, void* data1, * @param channelId A unique identifier for the communication channel. * @return An opaque handle (void*) representing the instance, or NULL on failure. */ -void* NewReliabilityManager(char* channelId); +void* sds_reliability_manager_new(char* channelId); -/** - * @brief Cleans up resources associated with a ReliabilityManager instance. - * @param handle The opaque handle (void*) of the instance to clean up. - */ -void CleanupReliabilityManager(void* handle); - -/** - * @brief Resets the ReliabilityManager instance. - * @param handle The opaque handle (void*) of the instance. - * @return CResult indicating success or failure. - */ -CResult ResetReliabilityManager(void* handle); -/** - * @brief Wraps an outgoing message. - * @param handle The opaque handle (void*) of the instance. - * @param message Pointer to the raw message content. - * @param messageLen Length of the raw message content. - * @param messageId A unique identifier for this message. - * @return CWrapResult containing the wrapped message or an error. - */ -CWrapResult WrapOutgoingMessage(void* handle, void* message, size_t messageLen, char* messageId); -/** - * @brief Unwraps a received message. - * @param handle The opaque handle (void*) of the instance. - * @param message Pointer to the received message data. - * @param messageLen Length of the received message data. - * @return CUnwrapResult containing the unwrapped content, missing dependencies, or an error. - */ -CUnwrapResult UnwrapReceivedMessage(void* handle, void* message, size_t messageLen); - -/** - * @brief Marks specified message dependencies as met. - * @param handle The opaque handle (void*) of the instance. - * @param messageIDs An array of message IDs to mark as met. - * @param count The number of message IDs in the array. - * @return CResult indicating success or failure. - */ -CResult MarkDependenciesMet(void* handle, char** messageIDs, size_t count); // Reverted to char** - -/** - * @brief Registers callback functions. - * @param handle The opaque handle (void*) of the instance. - * @param messageReady Callback for when a message is ready. - * @param messageSent Callback for when an outgoing message is acknowledged. - * @param eventCallback The single callback function to handle all events. - * @param user_data A pointer to user-defined data (optional, could be managed in Go). - */ -void RegisterCallback(void* handle, CEventCallback eventCallback, void* user_data); // Renamed and simplified - -/** - * @brief Starts the background periodic tasks. - * @param handle The opaque handle (void*) of the instance. - */ -void StartPeriodicTasks(void* handle); - - -// --- Memory Freeing Functions --- - -void FreeCResultError(CResult result); -void FreeCWrapResult(CWrapResult result); -void FreeCUnwrapResult(CUnwrapResult result); +/* void* waku_new( + const char* configJson, + WakuCallBack callback, + void* userData); */ #ifdef __cplusplus -} // extern "C" +} #endif -#endif // BINDINGS_H +#endif /* __libsds__ */ \ No newline at end of file diff --git a/reliability.nimble b/reliability.nimble deleted file mode 100644 index da7f7e6..0000000 --- a/reliability.nimble +++ /dev/null @@ -1,29 +0,0 @@ -# Package -version = "0.1.0" -author = "Waku Team" -description = "E2E Reliability Protocol API" -license = "MIT" -srcDir = "src" - -# Dependencies -requires "nim >= 2.0.8" -requires "chronicles" -requires "libp2p" - -# Tasks -task test, "Run the test suite": - exec "nim c -r tests/test_bloom.nim" - exec "nim c -r tests/test_reliability.nim" - -task bindings, "Generate bindings": - proc compile(libName: string, flags = "") = - exec "nim c -f " & flags & " -d:release --app:lib --mm:arc --tlsEmulation:off --out:" & libName & " --outdir:build library/libsds.nim" - - when defined(windows): - compile "reliability.dll" - elif defined(macosx): - compile "libsds.dylib.arm", "--cpu:arm64 -l:'-target arm64-apple-macos11' -t:'-target arm64-apple-macos11'" - compile "libsds.dylib.x64", "--cpu:amd64 -l:'-target x86_64-apple-macos10.12' -t:'-target x86_64-apple-macos10.12'" - exec "lipo build/libsds.dylib.arm build/libsds.dylib.x64 -output build/libsds.dylib -create" - else: - compile "libsds.so" \ No newline at end of file diff --git a/sds.nimble b/sds.nimble new file mode 100644 index 0000000..2362c28 --- /dev/null +++ b/sds.nimble @@ -0,0 +1,39 @@ +# Package +version = "0.1.0" +author = "Waku Team" +description = "E2E Reliability Protocol API" +license = "MIT" +srcDir = "src" + +# Dependencies +requires "nim >= 2.0.8" +requires "chronicles" +requires "libp2p" + +proc buildLibrary(name: string, srcDir = "./", params = "", `type` = "static") = + if not dirExists "build": + mkDir "build" + # allow something like "nim nimbus --verbosity:0 --hints:off nimbus.nims" + var extra_params = params + for i in 2 ..< paramCount(): + extra_params &= " " & paramStr(i) + if `type` == "static": + exec "nim c" & " --out:build/" & name & + ".a --threads:on --app:staticlib --opt:size --noMain --mm:refc --header --undef:metrics --nimMainPrefix:libsds --skipParentCfg:on " & + extra_params & " " & srcDir & name & ".nim" + else: + exec "nim c" & " --out:build/" & name & + ".so --threads:on --app:lib --opt:size --noMain --mm:refc --header --undef:metrics --nimMainPrefix:libsds --skipParentCfg:on " & + extra_params & " " & srcDir & name & ".nim" + +# Tasks +task test, "Run the test suite": + exec "nim c -r tests/test_bloom.nim" + exec "nim c -r tests/test_reliability.nim" + +task libsdsDynamic, "Generate bindings": + let name = "libsds" + buildLibrary name, + "library/", + "", + "dynamic" \ No newline at end of file diff --git a/sds_wrapper.go b/sds_wrapper.go index 389f763..1150a46 100644 --- a/sds_wrapper.go +++ b/sds_wrapper.go @@ -2,7 +2,7 @@ package main /* #cgo CFLAGS: -I${SRCDIR}/library -#cgo LDFLAGS: -L${SRCDIR}/build -lbindings +#cgo LDFLAGS: -L${SRCDIR}/build -llibsds #cgo LDFLAGS: -Wl,-rpath,${SRCDIR}/build #include // For C.free From 88063913719ec74a038659344a72c5da7a4ed209 Mon Sep 17 00:00:00 2001 From: Gabriel mermelstein Date: Tue, 8 Apr 2025 19:28:26 +0300 Subject: [PATCH 03/29] adding setup code similar tu libwaku's --- library/libsds.nim | 416 +++++++++------------------------------------ 1 file changed, 76 insertions(+), 340 deletions(-) diff --git a/library/libsds.nim b/library/libsds.nim index 4d7c9e5..9a10847 100644 --- a/library/libsds.nim +++ b/library/libsds.nim @@ -1,363 +1,99 @@ +{.pragma: exported, exportc, cdecl, raises: [].} +{.pragma: callback, cdecl, raises: [], gcsafe.} +{.passc: "-fPIC".} + +when defined(linux): + {.passl: "-Wl,-soname,libsds.so".} + import std/[locks, typetraits, tables] # Added tables import chronos import results import ../src/[reliability, reliability_utils, message] -type CReliabilityManagerHandle* = pointer +################################################################################ +### Wrapper around the reliability manager +################################################################################ -type - # Callback Types (Imported from C Header) - CEventType* {.importc: "CEventType", header: "libsds.h", pure.} = enum - EVENT_MESSAGE_READY = 1 - EVENT_MESSAGE_SENT = 2 - EVENT_MISSING_DEPENDENCIES = 3 - EVENT_PERIODIC_SYNC = 4 +################################################################################ +### Not-exported components - CEventCallback* = proc( - handle: pointer, - eventType: CEventType, - data1: pointer, - data2: pointer, - data3: csize_t, - ) {.cdecl.} # Use csize_t - - CResult* {.importc: "CResult", header: "libsds.h", bycopy.} = object - is_ok*: bool - error_message*: cstring - - CWrapResult* {.importc: "CWrapResult", header: "libsds.h", bycopy.} = object - base_result*: CResult - message*: pointer - message_len*: csize_t - - CUnwrapResult* {.importc: "CUnwrapResult", header: "libsds.h", bycopy.} = object - base_result*: CResult - message*: pointer - message_len*: csize_t - missing_deps*: ptr cstring - missing_deps_count*: csize_t - -# --- Callback Registry --- -type CallbackRegistry = Table[CReliabilityManagerHandle, CEventCallback] - -var - callbackRegistry: CallbackRegistry - registryLock: Lock - -initLock(registryLock) - -# --- Memory Management Helpers --- - -proc allocCString*(s: string): cstring {.inline, gcsafe.} = - if s.len == 0: - return nil - result = cast[cstring](allocShared(s.len + 1)) - copyMem(result, s.cstring, s.len + 1) - -proc allocSeqByte*(s: seq[byte]): (pointer, csize_t) {.inline, gcsafe.} = - if s.len == 0: - return (nil, 0) - let len = s.len - let bufferPtr = allocShared(len) - if len > 0: - copyMem(bufferPtr, cast[pointer](s[0].unsafeAddr), len.Natural) - return (bufferPtr, len.csize_t) - -proc allocSeqCString*( - s: seq[string] -): (ptr cstring, csize_t) {.inline, gcsafe, cdecl.} = - if s.len == 0: - return (nil, 0) - let count = s.len - # Allocate memory for 'count' cstring pointers, cast to ptr UncheckedArray - let arrPtr = cast[ptr UncheckedArray[cstring]](allocShared(count * sizeof(cstring))) - for i in 0 ..< count: - # Allocate each string and store its pointer in the array using unchecked array indexing - arrPtr[i] = allocCString(s[i]) - # Return pointer to the first element, cast back to ptr cstring - return (cast[ptr cstring](arrPtr), count.csize_t) - -proc freeCString*(cs: cstring) {.inline, gcsafe.} = - if cs != nil: - deallocShared(cs) - -proc freeSeqByte*(bufferPtr: pointer) {.inline, gcsafe, cdecl.} = - if bufferPtr != nil: - deallocShared(bufferPtr) - -# Corrected to accept ptr cstring -proc freeSeqCString*(arrPtr: ptr cstring, count: csize_t) {.inline, gcsafe, cdecl.} = - if arrPtr != nil: - # Cast to ptr UncheckedArray for proper iteration/indexing before freeing - let arr = cast[ptr UncheckedArray[cstring]](arrPtr) - for i in 0 ..< count: - freeCString(arr[i]) # Free each individual cstring - deallocShared(arrPtr) # Free the array pointer itself - -# --- Result Conversion Helpers --- - -proc toCResultOk*(): CResult = - CResult(is_ok: true, error_message: nil) - -proc toCResultErr*(err: ReliabilityError): CResult = - CResult(is_ok: false, error_message: allocCString($err)) - -proc toCResultErrStr*(errMsg: string): CResult = - CResult(is_ok: false, error_message: allocCString(errMsg)) - -# --- Callback Wrappers (Nim -> C) --- -# These wrappers call the single global Go callback relay. - -proc nimMessageReadyCallback(rm: ReliabilityManager, messageId: MessageID) = - echo "[Nim Binding] nimMessageReadyCallback called for: ", messageId - let handle = cast[CReliabilityManagerHandle](rm) - var cb: CEventCallback - withLock registryLock: - if not callbackRegistry.hasKey(handle): - echo "[Nim Binding] No callback registered for handle: ", cast[int](handle) - return - cb = callbackRegistry[handle] - - # Pass handle, event type, and messageId (as data1) - cb(handle, EVENT_MESSAGE_READY, cast[pointer](messageId.cstring), nil, 0) - -proc nimMessageSentCallback(rm: ReliabilityManager, messageId: MessageID) = - echo "[Nim Binding] nimMessageSentCallback called for: ", messageId - let handle = cast[CReliabilityManagerHandle](rm) - var cb: CEventCallback - withLock registryLock: - if not callbackRegistry.hasKey(handle): - echo "[Nim Binding] No callback registered for handle: ", cast[int](handle) - return - cb = callbackRegistry[handle] - - cb(handle, EVENT_MESSAGE_SENT, cast[pointer](messageId.cstring), nil, 0) - -proc nimMissingDependenciesCallback( - rm: ReliabilityManager, messageId: MessageID, missingDeps: seq[MessageID] +template checkLibsdsParams*( + ctx: ptr SdsContext, callback: SdsCallBack, userData: pointer ) = - echo "[Nim Binding] nimMissingDependenciesCallback called for: ", - messageId, " with deps: ", $missingDeps - let handle = cast[CReliabilityManagerHandle](rm) - var cb: CEventCallback - withLock registryLock: - if not callbackRegistry.hasKey(handle): - echo "[Nim Binding] No callback registered for handle: ", cast[int](handle) - return - cb = callbackRegistry[handle] + ctx[].userData = userData - # Prepare data for the callback - var cDepsPtr: ptr cstring = nil - var cDepsCount: csize_t = 0 - var cDepsNim: seq[cstring] = @[] # Keep Nim seq alive during call - if missingDeps.len > 0: - cDepsNim = newSeq[cstring](missingDeps.len) - for i, dep in missingDeps: - cDepsNim[i] = dep.cstring # Nim GC manages these cstrings via the seq - cDepsPtr = cast[ptr cstring](cDepsNim[0].addr) - cDepsCount = missingDeps.len.csize_t + if isNil(callback): + return RET_MISSING_CALLBACK - cb( - handle, - EVENT_MISSING_DEPENDENCIES, - cast[pointer](messageId.cstring), - cast[pointer](cDepsPtr), - cDepsCount, - ) - -proc nimPeriodicSyncCallback(rm: ReliabilityManager) = - echo "[Nim Binding] nimPeriodicSyncCallback called" - let handle = cast[CReliabilityManagerHandle](rm) - var cb: CEventCallback - withLock registryLock: - if not callbackRegistry.hasKey(handle): - echo "[Nim Binding] No callback registered for handle: ", cast[int](handle) - return - cb = callbackRegistry[handle] - - cb(handle, EVENT_PERIODIC_SYNC, nil, nil, 0) - -# --- Exported C Functions - Using Opaque Pointer --- - -proc NewReliabilityManager*( - channelIdCStr: cstring -): CReliabilityManagerHandle {.exportc, dynlib, cdecl, gcsafe.} = - let channelId = $channelIdCStr - if channelId.len == 0: - echo "Error creating ReliabilityManager: Channel ID cannot be empty" - return nil # Return nil pointer - let rmResult = newReliabilityManager(channelId) - if rmResult.isOk: - let rm = rmResult.get() - # Assign anonymous procs that capture 'rm' and call the wrappers - # Ensure signatures match the non-gcsafe fields in ReliabilityManager - rm.onMessageReady = proc(msgId: MessageID) = - nimMessageReadyCallback(rm, msgId) - rm.onMessageSent = proc(msgId: MessageID) = - nimMessageSentCallback(rm, msgId) - rm.onMissingDependencies = proc(msgId: MessageID, deps: seq[MessageID]) = - nimMissingDependenciesCallback(rm, msgId, deps) - rm.onPeriodicSync = proc() = - nimPeriodicSyncCallback(rm) - - # Return the Nim ref object cast to the opaque pointer type - let handle = cast[CReliabilityManagerHandle](rm) - GC_ref(rm) # Prevent GC from moving the object while Go holds the handle - return handle - else: - echo "Error creating ReliabilityManager: ", rmResult.error - return nil # Return nil pointer - -proc CleanupReliabilityManager*( - handle: CReliabilityManagerHandle -) {.exportc, dynlib, cdecl.} = - let handlePtr = handle - if handlePtr != nil: - # Go side should handle removing the handle from its registry. - # We just need to unref the Nim object. - # No need to interact with gEventCallback here. - - # Cast opaque pointer back to Nim ref type - let rm = cast[ReliabilityManager](handlePtr) - cleanup(rm) # Call Nim cleanup - GC_unref(rm) # Allow GC to collect the object now that Go is done - else: - echo "Warning: CleanupReliabilityManager called with NULL handle" - -proc ResetReliabilityManager*( - handle: CReliabilityManagerHandle -): CResult {.exportc, dynlib, cdecl, gcsafe.} = - if handle == nil: - return toCResultErrStr("ReliabilityManager handle is NULL") - let rm = cast[ReliabilityManager](handle) - let result = resetReliabilityManager(rm) - if result.isOk: - return toCResultOk() - else: - return toCResultErr(result.error) - -proc WrapOutgoingMessage*( - handle: CReliabilityManagerHandle, - messageC: pointer, - messageLen: csize_t, - messageIdCStr: cstring, -): CWrapResult {.exportc, dynlib, cdecl.} = # Keep non-gcsafe - if handle == nil: +template callEventCallback(ctx: ptr SdsContext, eventName: string, body: untyped) = + if isNil(ctx[].eventCallback): + error eventName & " - eventCallback is nil" return - CWrapResult(base_result: toCResultErrStr("ReliabilityManager handle is NULL")) - let rm = cast[ReliabilityManager](handle) - if messageC == nil and messageLen > 0: - return CWrapResult( - base_result: toCResultErrStr("Message pointer is NULL but length > 0") - ) - if messageIdCStr == nil: - return CWrapResult(base_result: toCResultErrStr("Message ID pointer is NULL")) - - let messageId = $messageIdCStr - var messageNim: seq[byte] - if messageLen > 0: - messageNim = newSeq[byte](messageLen) - copyMem(messageNim[0].addr, messageC, messageLen.Natural) - else: - messageNim = @[] - - let wrapResult = wrapOutgoingMessage(rm, messageNim, messageId) - if wrapResult.isOk: - let (wrappedDataPtr, wrappedDataLen) = allocSeqByte(wrapResult.get()) - return CWrapResult( - base_result: toCResultOk(), message: wrappedDataPtr, message_len: wrappedDataLen - ) - else: - return CWrapResult(base_result: toCResultErr(wrapResult.error)) - -proc UnwrapReceivedMessage*( - handle: CReliabilityManagerHandle, messageC: pointer, messageLen: csize_t -): CUnwrapResult {.exportc, dynlib, cdecl.} = # Keep non-gcsafe - if handle == nil: + if isNil(ctx[].eventUserData): + error eventName & " - eventUserData is nil" return - CUnwrapResult(base_result: toCResultErrStr("ReliabilityManager handle is NULL")) - let rm = cast[ReliabilityManager](handle) - if messageC == nil and messageLen > 0: - return CUnwrapResult( - base_result: toCResultErrStr("Message pointer is NULL but length > 0") - ) + foreignThreadGc: + try: + let event = body + cast[SdsCallBack](ctx[].eventCallback)( + RET_OK, unsafeAddr event[0], cast[csize_t](len(event)), ctx[].eventUserData + ) + except Exception, CatchableError: + let msg = + "Exception " & eventName & " when calling 'eventCallBack': " & + getCurrentExceptionMsg() + cast[SdsCallBack](ctx[].eventCallback)( + RET_ERR, unsafeAddr msg[0], cast[csize_t](len(msg)), ctx[].eventUserData + ) - var messageNim: seq[byte] - if messageLen > 0: - messageNim = newSeq[byte](messageLen) - copyMem(messageNim[0].addr, messageC, messageLen.Natural) - else: - messageNim = @[] +proc handleRequest( + ctx: ptr SdsContext, + requestType: RequestType, + content: pointer, + callback: SdsCallBack, + userData: pointer, +): cint = + sds_thread.sendRequestToSdsThread(ctx, requestType, content, callback, userData).isOkOr: + let msg = "libsds error: " & $error + callback(RET_ERR, unsafeAddr msg[0], cast[csize_t](len(msg)), userData) + return RET_ERR - let unwrapResult = unwrapReceivedMessage(rm, messageNim) - if unwrapResult.isOk: - let (unwrappedContent, missingDepsNim) = unwrapResult.get() - let (contentPtr, contentLen) = allocSeqByte(unwrappedContent) - let (depsPtr, depsCount) = allocSeqCString(missingDepsNim) - return CUnwrapResult( - base_result: toCResultOk(), - message: contentPtr, - message_len: contentLen, - missing_deps: depsPtr, - missing_deps_count: depsCount, - ) - else: - return CUnwrapResult(base_result: toCResultErr(unwrapResult.error)) + return RET_OK -proc MarkDependenciesMet*( - handle: CReliabilityManagerHandle, messageIDsC: ptr cstring, count: csize_t -): CResult {.exportc, dynlib, cdecl.} = # Keep non-gcsafe - if handle == nil: - return toCResultErrStr("ReliabilityManager handle is NULL") - let rm = cast[ReliabilityManager](handle) +### End of not-exported components +################################################################################ - if messageIDsC == nil and count > 0: - return toCResultErrStr("MessageIDs pointer is NULL but count > 0") +################################################################################ +### Library setup - var messageIDsNim = newSeq[string](count) - # Cast to ptr UncheckedArray for indexing - let messageIDsCArray = cast[ptr UncheckedArray[cstring]](messageIDsC) - for i in 0 ..< count: - let currentCStr = messageIDsCArray[i] # Use unchecked array indexing - if currentCStr != nil: - messageIDsNim[i] = $currentCStr - else: - return toCResultErrStr("NULL message ID found in array") +# Every Nim library must have this function called - the name is derived from +# the `--nimMainPrefix` command line option +proc libsdsNimMain() {.importc.} - let result = markDependenciesMet(rm, messageIDsNim) - if result.isOk: - return toCResultOk() - else: - return toCResultErr(result.error) +# To control when the library has been initialized +var initialized: Atomic[bool] -proc RegisterCallback*( - handle: CReliabilityManagerHandle, - cEventCallback: CEventCallback, - cUserDataPtr: pointer, -) {.exportc, dynlib, cdecl.} = - withLock registryLock: - callbackRegistry[handle] = cEventCallback - echo "[Nim Binding] Registered callback for handle: ", cast[int](handle) +if defined(android): + # Redirect chronicles to Android System logs + when compiles(defaultChroniclesStream.outputs[0].writer): + defaultChroniclesStream.outputs[0].writer = proc( + logLevel: LogLevel, msg: LogOutputStr + ) {.raises: [].} = + echo logLevel, msg -proc StartPeriodicTasks*(handle: CReliabilityManagerHandle) {.exportc, dynlib, cdecl.} = - if handle == nil: - echo "Error: Cannot start periodic tasks: NULL ReliabilityManager handle" - return - let rm = cast[ReliabilityManager](handle) - startPeriodicTasks(rm) +proc initializeLibrary() {.exported.} = + if not initialized.exchange(true): + ## Every Nim library needs to call `NimMain` once exactly, to initialize the Nim runtime. + ## Being `` the value given in the optional compilation flag --nimMainPrefix:yourprefix + libsdsNimMain() + when declared(setupForeignThreadGc): + setupForeignThreadGc() + when declared(nimGC_setStackBottom): + var locals {.volatile, noinit.}: pointer + locals = addr(locals) + nimGC_setStackBottom(locals) -# --- Memory Freeing Functions --- - -proc FreeCResultError*(result: CResult) {.exportc, dynlib, gcsafe, cdecl.} = - freeCString(result.error_message) - -proc FreeCWrapResult*(result: CWrapResult) {.exportc, dynlib, gcsafe, cdecl.} = - freeCString(result.base_result.error_message) - freeSeqByte(result.message) - -proc FreeCUnwrapResult*(result: CUnwrapResult) {.exportc, dynlib, gcsafe, cdecl.} = - freeCString(result.base_result.error_message) - freeSeqByte(result.message) - freeSeqCString(result.missing_deps, result.missing_deps_count) +### End of library setup +################################################################################ From dbf271e0f9fab51f397b9dbec80122f61d34b430 Mon Sep 17 00:00:00 2001 From: Gabriel mermelstein Date: Wed, 9 Apr 2025 13:05:42 +0300 Subject: [PATCH 04/29] adding missing files --- Makefile | 2 +- library/alloc.nim | 42 ++++++ library/ffi_types.nim | 30 ++++ .../requests/sds_lifecycle_request.nim | 43 ++++++ .../sds_thread_request.nim | 70 ++++++++++ library/sds_thread/sds_thread.nim | 129 ++++++++++++++++++ 6 files changed, 315 insertions(+), 1 deletion(-) create mode 100644 library/alloc.nim create mode 100644 library/ffi_types.nim create mode 100644 library/sds_thread/inter_thread_communication/requests/sds_lifecycle_request.nim create mode 100644 library/sds_thread/inter_thread_communication/sds_thread_request.nim create mode 100644 library/sds_thread/sds_thread.nim diff --git a/Makefile b/Makefile index 3ab8b78..1c478cd 100644 --- a/Makefile +++ b/Makefile @@ -9,7 +9,7 @@ deps: | sds.nims STATIC ?= 0 libsds: deps - rm -f build/libwaku* + rm -f build/libsds* ifeq ($(STATIC), 1) echo -e $(BUILD_MSG) "build/$@.a" && \ $(ENV_SCRIPT) nim libsdsStatic $(NIM_PARAMS) sds.nims diff --git a/library/alloc.nim b/library/alloc.nim new file mode 100644 index 0000000..1a6f118 --- /dev/null +++ b/library/alloc.nim @@ -0,0 +1,42 @@ +## Can be shared safely between threads +type SharedSeq*[T] = tuple[data: ptr UncheckedArray[T], len: int] + +proc alloc*(str: cstring): cstring = + # Byte allocation from the given address. + # There should be the corresponding manual deallocation with deallocShared ! + if str.isNil(): + var ret = cast[cstring](allocShared(1)) # Allocate memory for the null terminator + ret[0] = '\0' # Set the null terminator + return ret + + let ret = cast[cstring](allocShared(len(str) + 1)) + copyMem(ret, str, len(str) + 1) + return ret + +proc alloc*(str: string): cstring = + ## Byte allocation from the given address. + ## There should be the corresponding manual deallocation with deallocShared ! + var ret = cast[cstring](allocShared(str.len + 1)) + let s = cast[seq[char]](str) + for i in 0 ..< str.len: + ret[i] = s[i] + ret[str.len] = '\0' + return ret + +proc allocSharedSeq*[T](s: seq[T]): SharedSeq[T] = + let data = allocShared(sizeof(T) * s.len) + if s.len != 0: + copyMem(data, unsafeAddr s[0], s.len) + return (cast[ptr UncheckedArray[T]](data), s.len) + +proc deallocSharedSeq*[T](s: var SharedSeq[T]) = + deallocShared(s.data) + s.len = 0 + +proc toSeq*[T](s: SharedSeq[T]): seq[T] = + ## Creates a seq[T] from a SharedSeq[T]. No explicit dealloc is required + ## as req[T] is a GC managed type. + var ret = newSeq[T]() + for i in 0 ..< s.len: + ret.add(s.data[i]) + return ret diff --git a/library/ffi_types.nim b/library/ffi_types.nim new file mode 100644 index 0000000..e2445a2 --- /dev/null +++ b/library/ffi_types.nim @@ -0,0 +1,30 @@ +################################################################################ +### Exported types + +type SdsCallBack* = proc( + callerRet: cint, msg: ptr cchar, len: csize_t, userData: pointer +) {.cdecl, gcsafe, raises: [].} + +const RET_OK*: cint = 0 +const RET_ERR*: cint = 1 +const RET_MISSING_CALLBACK*: cint = 2 + +### End of exported types +################################################################################ + +################################################################################ +### FFI utils + +template foreignThreadGc*(body: untyped) = + when declared(setupForeignThreadGc): + setupForeignThreadGc() + + body + + when declared(tearDownForeignThreadGc): + tearDownForeignThreadGc() + +type onDone* = proc() + +### End of FFI utils +################################################################################ diff --git a/library/sds_thread/inter_thread_communication/requests/sds_lifecycle_request.nim b/library/sds_thread/inter_thread_communication/requests/sds_lifecycle_request.nim new file mode 100644 index 0000000..16ab116 --- /dev/null +++ b/library/sds_thread/inter_thread_communication/requests/sds_lifecycle_request.nim @@ -0,0 +1,43 @@ +import std/[options, json, strutils, net] +import chronos, chronicles, results, confutils, confutils/std/net + +import ../../../alloc + +type SdsLifecycleMsgType* = enum + CREATE_SDS + START_SDS + STOP_SDS + +type SdsLifecycleRequest* = object + operation: SdsLifecycleMsgType + configJson: cstring ## Only used in 'CREATE_NODE' operation + appCallbacks: AppCallbacks + +proc createShared*( + T: type SdsLifecycleRequest, + op: SdsLifecycleMsgType, + configJson: cstring = "", + appCallbacks: AppCallbacks = nil, +): ptr type T = + var ret = createShared(T) + ret[].operation = op + ret[].appCallbacks = appCallbacks + ret[].configJson = configJson.alloc() + return ret + +proc destroyShared(self: ptr SdsLifecycleRequest) = + deallocShared(self[].configJson) + deallocShared(self) + +proc process*( + self: ptr SdsLifecycleRequest, waku: ptr Waku +): Future[Result[string, string]] {.async.} = + defer: + destroyShared(self) + + case self.operation + of CREATE_SDS: discard + of START_SDS: discard + of STOP_SDS: discard + + return ok("") diff --git a/library/sds_thread/inter_thread_communication/sds_thread_request.nim b/library/sds_thread/inter_thread_communication/sds_thread_request.nim new file mode 100644 index 0000000..be38fd6 --- /dev/null +++ b/library/sds_thread/inter_thread_communication/sds_thread_request.nim @@ -0,0 +1,70 @@ +## This file contains the base message request type that will be handled. +## The requests are created by the main thread and processed by +## the SDS Thread. + +import std/json, results +import chronos, chronos/threadsync +import ../../ffi_types, ./requests/sds_lifecycle_request + +type RequestType* {.pure.} = enum + LIFECYCLE + +type SdsThreadRequest* = object + reqType: RequestType + reqContent: pointer + callback: SdsCallBack + userData: pointer + +proc createShared*( + T: type SdsThreadRequest, + reqType: RequestType, + reqContent: pointer, + callback: SdsCallBack, + userData: pointer, +): ptr type T = + var ret = createShared(T) + ret[].reqType = reqType + ret[].reqContent = reqContent + ret[].callback = callback + ret[].userData = userData + return ret + +proc handleRes[T: string | void]( + res: Result[T, string], request: ptr SdsThreadRequest +) = + ## Handles the Result responses, which can either be Result[string, string] or + ## Result[void, string]. + + defer: + deallocShared(request) + + if res.isErr(): + foreignThreadGc: + let msg = "libsds error: handleRes fireSyncRes error: " & $res.error + request[].callback( + RET_ERR, unsafeAddr msg[0], cast[csize_t](len(msg)), request[].userData + ) + return + + foreignThreadGc: + var msg: cstring = "" + when T is string: + msg = res.get().cstring() + request[].callback( + RET_OK, unsafeAddr msg[0], cast[csize_t](len(msg)), request[].userData + ) + return + +# TODO: change waku for reliability manager or something like that +proc process*( + T: type SdsThreadRequest, request: ptr SdsThreadRequest, waku: ptr Waku +) {.async.} = + let retFut = + case request[].reqType + of LIFECYCLE: + cast[ptr SdsLifecycleRequest](request[].reqContent).process(waku) + + handleRes(await retFut, request) + +proc `$`*(self: SdsThreadRequest): string = + return $self.reqType diff --git a/library/sds_thread/sds_thread.nim b/library/sds_thread/sds_thread.nim new file mode 100644 index 0000000..a8b141d --- /dev/null +++ b/library/sds_thread/sds_thread.nim @@ -0,0 +1,129 @@ +{.pragma: exported, exportc, cdecl, raises: [].} +{.pragma: callback, cdecl, raises: [], gcsafe.} +{.passc: "-fPIC".} + +import std/[options, atomics, os, net, locks] +import chronicles, chronos, chronos/threadsync, taskpools/channels_spsc_single, results +import ../ffi_types + +type SdsContext* = object + thread: Thread[(ptr SdsContext)] + lock: Lock + reqChannel: ChannelSPSCSingle[ptr SdsThreadRequest] + reqSignal: ThreadSignalPtr + # to inform The SDS Thread (a.k.a TST) that a new request is sent + reqReceivedSignal: ThreadSignalPtr + # to inform the main thread that the request is rx by TST + userData*: pointer + eventCallback*: pointer + eventUserdata*: pointer + running: Atomic[bool] # To control when the thread is running + +proc runSds(ctx: ptr SdsContext) {.async.} = + ## This is the worker body. This runs the SDS instance + ## and attends library user requests (stop, connect_to, etc.) + + var waku: Waku # TODO + + while true: + await ctx.reqSignal.wait() + + if ctx.running.load == false: + break + + ## Trying to get a request from the libsds requestor thread + var request: ptr SdsThreadRequest + let recvOk = ctx.reqChannel.tryRecv(request) + if not recvOk: + error "sds thread could not receive a request" + continue + + let fireRes = ctx.reqReceivedSignal.fireSync() + if fireRes.isErr(): + error "could not fireSync back to requester thread", error = fireRes.error + + ## Handle the request + asyncSpawn SdsThreadRequest.process(request, addr waku) # TODO + +proc run(ctx: ptr SdsContext) {.thread.} = + ## Launch sds worker + waitFor runSds(ctx) + +proc createSdsThread*(): Result[ptr SdsContext, string] = + ## This proc is called from the main thread and it creates + ## the SDS working thread. + var ctx = createShared(SdsContext, 1) + ctx.reqSignal = ThreadSignalPtr.new().valueOr: + return err("couldn't create reqSignal ThreadSignalPtr") + ctx.reqReceivedSignal = ThreadSignalPtr.new().valueOr: + return err("couldn't create reqReceivedSignal ThreadSignalPtr") + ctx.lock.initLock() + + ctx.running.store(true) + + try: + createThread(ctx.thread, run, ctx) + except ValueError, ResourceExhaustedError: + # and freeShared for typed allocations! + freeShared(ctx) + + return err("failed to create the SDS thread: " & getCurrentExceptionMsg()) + + return ok(ctx) + +proc destroySdsThread*(ctx: ptr SdsContext): Result[void, string] = + ctx.running.store(false) + + let signaledOnTime = ctx.reqSignal.fireSync().valueOr: + return err("error in destroySdsThread: " & $error) + if not signaledOnTime: + return err("failed to signal reqSignal on time in destroySdsThread") + + joinThread(ctx.thread) + ctx.lock.deinitLock() + ?ctx.reqSignal.close() + ?ctx.reqReceivedSignal.close() + freeShared(ctx) + + return ok() + +proc sendRequestToSdsThread*( + ctx: ptr SdsContext, + reqType: RequestType, + reqContent: pointer, + callback: SdsCallBack, + userData: pointer, +): Result[void, string] = + let req = SdsThreadRequest.createShared(reqType, reqContent, callback, userData) + + # This lock is only necessary while we use a SP Channel and while the signalling + # between threads assumes that there aren't concurrent requests. + # Rearchitecting the signaling + migrating to a MP Channel will allow us to receive + # requests concurrently and spare us the need of locks + ctx.lock.acquire() + defer: + ctx.lock.release() + ## Sending the request + let sentOk = ctx.reqChannel.trySend(req) + if not sentOk: + deallocShared(req) + return err("Couldn't send a request to the sds thread: " & $req[]) + + let fireSyncRes = ctx.reqSignal.fireSync() + if fireSyncRes.isErr(): + deallocShared(req) + return err("failed fireSync: " & $fireSyncRes.error) + + if fireSyncRes.get() == false: + deallocShared(req) + return err("Couldn't fireSync in time") + + ## wait until the SDS Thread properly received the request + let res = ctx.reqReceivedSignal.waitSync() + if res.isErr(): + deallocShared(req) + return err("Couldn't receive reqReceivedSignal signal") + + ## Notice that in case of "ok", the deallocShared(req) is performed by the SDS Thread in the + ## process proc. + ok() From 12f5cc2ebbd9663995747fb209a03dfca7dd5ab3 Mon Sep 17 00:00:00 2001 From: Gabriel mermelstein Date: Wed, 9 Apr 2025 13:35:06 +0300 Subject: [PATCH 05/29] setting up create reliability manager request --- .../requests/sds_lifecycle_request.nim | 47 ++++++++++++------- .../sds_thread_request.nim | 5 +- library/sds_thread/sds_thread.nim | 4 +- 3 files changed, 35 insertions(+), 21 deletions(-) diff --git a/library/sds_thread/inter_thread_communication/requests/sds_lifecycle_request.nim b/library/sds_thread/inter_thread_communication/requests/sds_lifecycle_request.nim index 16ab116..58ca559 100644 --- a/library/sds_thread/inter_thread_communication/requests/sds_lifecycle_request.nim +++ b/library/sds_thread/inter_thread_communication/requests/sds_lifecycle_request.nim @@ -4,40 +4,55 @@ import chronos, chronicles, results, confutils, confutils/std/net import ../../../alloc type SdsLifecycleMsgType* = enum - CREATE_SDS - START_SDS - STOP_SDS + CREATE_RELIABILITY_MANAGER type SdsLifecycleRequest* = object operation: SdsLifecycleMsgType - configJson: cstring ## Only used in 'CREATE_NODE' operation - appCallbacks: AppCallbacks + channelId: cstring proc createShared*( - T: type SdsLifecycleRequest, - op: SdsLifecycleMsgType, - configJson: cstring = "", - appCallbacks: AppCallbacks = nil, + T: type SdsLifecycleRequest, op: SdsLifecycleMsgType, configJson: cstring = "" ): ptr type T = var ret = createShared(T) ret[].operation = op - ret[].appCallbacks = appCallbacks - ret[].configJson = configJson.alloc() + ret[].channelId = channelId.alloc() return ret proc destroyShared(self: ptr SdsLifecycleRequest) = - deallocShared(self[].configJson) + deallocShared(self[].channelId) deallocShared(self) +proc createReliabilityManager(channelId: cstring): Result[ReliabilityManager, string] = + let channelId = $channelIdCStr + if channelId.len == 0: + error "Failed creating ReliabilityManager: Channel ID cannot be empty" + return err("Failed creating ReliabilityManager: Channel ID cannot be empty") + + let rm = newReliabilityManager(channelId).valueOr: + error "Failed creating reliability manager", error = error + return err("Failed creating reliability manager: " & $error) + + rm.onMessageReady = proc(msgId: MessageID) = + nimMessageReadyCallback(rm, msgId) + rm.onMessageSent = proc(msgId: MessageID) = + nimMessageSentCallback(rm, msgId) + rm.onMissingDependencies = proc(msgId: MessageID, deps: seq[MessageID]) = + nimMissingDependenciesCallback(rm, msgId, deps) + rm.onPeriodicSync = proc() = + nimPeriodicSyncCallback(rm) + + return ok(rm) + proc process*( - self: ptr SdsLifecycleRequest, waku: ptr Waku + self: ptr SdsLifecycleRequest, rm: ptr ReliabilityManager ): Future[Result[string, string]] {.async.} = defer: destroyShared(self) case self.operation - of CREATE_SDS: discard - of START_SDS: discard - of STOP_SDS: discard + of CREATE_RELIABILITY_MANAGER: + rm[] = (await createReliabilityManager(self.channelId)).valueOr: + error "CREATE_RELIABILITY_MANAGER failed", error = error + return err("error processing CREATE_RELIABILITY_MANAGER request: " & $error) return ok("") diff --git a/library/sds_thread/inter_thread_communication/sds_thread_request.nim b/library/sds_thread/inter_thread_communication/sds_thread_request.nim index be38fd6..64c908f 100644 --- a/library/sds_thread/inter_thread_communication/sds_thread_request.nim +++ b/library/sds_thread/inter_thread_communication/sds_thread_request.nim @@ -55,14 +55,13 @@ proc handleRes[T: string | void]( ) return -# TODO: change waku for reliability manager or something like that proc process*( - T: type SdsThreadRequest, request: ptr SdsThreadRequest, waku: ptr Waku + T: type SdsThreadRequest, request: ptr SdsThreadRequest, rm: ptr ReliabilityManager ) {.async.} = let retFut = case request[].reqType of LIFECYCLE: - cast[ptr SdsLifecycleRequest](request[].reqContent).process(waku) + cast[ptr SdsLifecycleRequest](request[].reqContent).process(rm) handleRes(await retFut, request) diff --git a/library/sds_thread/sds_thread.nim b/library/sds_thread/sds_thread.nim index a8b141d..3cde969 100644 --- a/library/sds_thread/sds_thread.nim +++ b/library/sds_thread/sds_thread.nim @@ -23,7 +23,7 @@ proc runSds(ctx: ptr SdsContext) {.async.} = ## This is the worker body. This runs the SDS instance ## and attends library user requests (stop, connect_to, etc.) - var waku: Waku # TODO + var rm: ReliabilityManager while true: await ctx.reqSignal.wait() @@ -43,7 +43,7 @@ proc runSds(ctx: ptr SdsContext) {.async.} = error "could not fireSync back to requester thread", error = fireRes.error ## Handle the request - asyncSpawn SdsThreadRequest.process(request, addr waku) # TODO + asyncSpawn SdsThreadRequest.process(request, addr rm) proc run(ctx: ptr SdsContext) {.thread.} = ## Launch sds worker From 71a6d6f00bf5e1bd0899ba590e991f4ec037568a Mon Sep 17 00:00:00 2001 From: Gabriel mermelstein Date: Wed, 9 Apr 2025 13:39:12 +0300 Subject: [PATCH 06/29] missing import --- library/libsds.nim | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/library/libsds.nim b/library/libsds.nim index 9a10847..8a35e3b 100644 --- a/library/libsds.nim +++ b/library/libsds.nim @@ -8,7 +8,11 @@ when defined(linux): import std/[locks, typetraits, tables] # Added tables import chronos import results -import ../src/[reliability, reliability_utils, message] +import + ./sds_thread/sds_thread, + ./alloc, + ./ffi_types, + ../src/[reliability, reliability_utils, message] ################################################################################ ### Wrapper around the reliability manager From cff40624102d463071e595d766c6cb33e82507e7 Mon Sep 17 00:00:00 2001 From: Gabriel mermelstein Date: Wed, 9 Apr 2025 17:49:20 +0300 Subject: [PATCH 07/29] fixing compilation errors --- library/libsds.nim | 3 ++- .../requests/sds_lifecycle_request.nim | 12 ++++++++---- .../sds_thread_request.nim | 3 ++- library/sds_thread/sds_thread.nim | 5 ++++- src/reliability_utils.nim | 6 ++++-- 5 files changed, 20 insertions(+), 9 deletions(-) diff --git a/library/libsds.nim b/library/libsds.nim index 8a35e3b..1671b53 100644 --- a/library/libsds.nim +++ b/library/libsds.nim @@ -5,13 +5,14 @@ when defined(linux): {.passl: "-Wl,-soname,libsds.so".} -import std/[locks, typetraits, tables] # Added tables +import std/[locks, typetraits, tables, atomics] # Added tables import chronos import results import ./sds_thread/sds_thread, ./alloc, ./ffi_types, + ./sds_thread/inter_thread_communication/sds_thread_request, ../src/[reliability, reliability_utils, message] ################################################################################ diff --git a/library/sds_thread/inter_thread_communication/requests/sds_lifecycle_request.nim b/library/sds_thread/inter_thread_communication/requests/sds_lifecycle_request.nim index 58ca559..4ade389 100644 --- a/library/sds_thread/inter_thread_communication/requests/sds_lifecycle_request.nim +++ b/library/sds_thread/inter_thread_communication/requests/sds_lifecycle_request.nim @@ -2,6 +2,7 @@ import std/[options, json, strutils, net] import chronos, chronicles, results, confutils, confutils/std/net import ../../../alloc +import ../../../../src/[reliability_utils, reliability, message] type SdsLifecycleMsgType* = enum CREATE_RELIABILITY_MANAGER @@ -11,7 +12,7 @@ type SdsLifecycleRequest* = object channelId: cstring proc createShared*( - T: type SdsLifecycleRequest, op: SdsLifecycleMsgType, configJson: cstring = "" + T: type SdsLifecycleRequest, op: SdsLifecycleMsgType, channelId: cstring = "" ): ptr type T = var ret = createShared(T) ret[].operation = op @@ -22,7 +23,9 @@ proc destroyShared(self: ptr SdsLifecycleRequest) = deallocShared(self[].channelId) deallocShared(self) -proc createReliabilityManager(channelId: cstring): Result[ReliabilityManager, string] = +proc createReliabilityManager( + channelIdCStr: cstring +): Future[Result[ReliabilityManager, string]] {.async.} = let channelId = $channelIdCStr if channelId.len == 0: error "Failed creating ReliabilityManager: Channel ID cannot be empty" @@ -32,14 +35,15 @@ proc createReliabilityManager(channelId: cstring): Result[ReliabilityManager, st error "Failed creating reliability manager", error = error return err("Failed creating reliability manager: " & $error) - rm.onMessageReady = proc(msgId: MessageID) = + # TODO: instead of this, create events + #[ rm.onMessageReady = proc(msgId: MessageID) = nimMessageReadyCallback(rm, msgId) rm.onMessageSent = proc(msgId: MessageID) = nimMessageSentCallback(rm, msgId) rm.onMissingDependencies = proc(msgId: MessageID, deps: seq[MessageID]) = nimMissingDependenciesCallback(rm, msgId, deps) rm.onPeriodicSync = proc() = - nimPeriodicSyncCallback(rm) + nimPeriodicSyncCallback(rm) ]# return ok(rm) diff --git a/library/sds_thread/inter_thread_communication/sds_thread_request.nim b/library/sds_thread/inter_thread_communication/sds_thread_request.nim index 64c908f..855e77b 100644 --- a/library/sds_thread/inter_thread_communication/sds_thread_request.nim +++ b/library/sds_thread/inter_thread_communication/sds_thread_request.nim @@ -4,7 +4,8 @@ import std/json, results import chronos, chronos/threadsync -import ../../ffi_types, ./requests/sds_lifecycle_request +import + ../../ffi_types, ./requests/sds_lifecycle_request, ../../../src/[reliability_utils] type RequestType* {.pure.} = enum LIFECYCLE diff --git a/library/sds_thread/sds_thread.nim b/library/sds_thread/sds_thread.nim index 3cde969..4a2cce5 100644 --- a/library/sds_thread/sds_thread.nim +++ b/library/sds_thread/sds_thread.nim @@ -4,7 +4,10 @@ import std/[options, atomics, os, net, locks] import chronicles, chronos, chronos/threadsync, taskpools/channels_spsc_single, results -import ../ffi_types +import + ../ffi_types, + ./inter_thread_communication/sds_thread_request, + ../../src/[reliability_utils] type SdsContext* = object thread: Thread[(ptr SdsContext)] diff --git a/src/reliability_utils.nim b/src/reliability_utils.nim index 33b47e6..ec576dc 100644 --- a/src/reliability_utils.nim +++ b/src/reliability_utils.nim @@ -51,7 +51,7 @@ proc defaultConfig*(): ReliabilityConfig = resendInterval: DefaultResendInterval, maxResendAttempts: DefaultMaxResendAttempts, syncMessageInterval: DefaultSyncMessageInterval, - bufferSweepInterval: DefaultBufferSweepInterval + bufferSweepInterval: DefaultBufferSweepInterval, ) proc cleanup*(rm: ReliabilityManager) {.raises: [].} = @@ -76,7 +76,9 @@ proc addToHistory*(rm: ReliabilityManager, msgId: MessageID) {.gcsafe, raises: [ if rm.messageHistory.len > rm.config.maxMessageHistory: rm.messageHistory.delete(0) -proc updateLamportTimestamp*(rm: ReliabilityManager, msgTs: int64) {.gcsafe, raises: [].} = +proc updateLamportTimestamp*( + rm: ReliabilityManager, msgTs: int64 +) {.gcsafe, raises: [].} = rm.lamportTimestamp = max(msgTs, rm.lamportTimestamp) + 1 proc getRecentMessageIDs*(rm: ReliabilityManager, n: int): seq[MessageID] = From b90387799a83958a4fae2ea3a7ec70886740c2ed Mon Sep 17 00:00:00 2001 From: Gabriel mermelstein Date: Wed, 9 Apr 2025 17:58:42 +0300 Subject: [PATCH 08/29] adding NewReliabilityManager --- library/libsds.h | 7 +------ library/libsds.nim | 40 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 6 deletions(-) diff --git a/library/libsds.h b/library/libsds.h index f34daf4..aa2909a 100644 --- a/library/libsds.h +++ b/library/libsds.h @@ -28,12 +28,7 @@ typedef void (*SdsCallBack) (int callerRet, const char* msg, size_t len, void* u * @param channelId A unique identifier for the communication channel. * @return An opaque handle (void*) representing the instance, or NULL on failure. */ -void* sds_reliability_manager_new(char* channelId); - -/* void* waku_new( - const char* configJson, - WakuCallBack callback, - void* userData); */ +void* NewReliabilityManager(char* channelId); #ifdef __cplusplus diff --git a/library/libsds.nim b/library/libsds.nim index 1671b53..81e8362 100644 --- a/library/libsds.nim +++ b/library/libsds.nim @@ -13,6 +13,7 @@ import ./alloc, ./ffi_types, ./sds_thread/inter_thread_communication/sds_thread_request, + ./sds_thread/inter_thread_communication/requests/sds_lifecycle_request, ../src/[reliability, reliability_utils, message] ################################################################################ @@ -102,3 +103,42 @@ proc initializeLibrary() {.exported.} = ### End of library setup ################################################################################ + +################################################################################ +### Exported procs + +proc NewReliabilityManager( + channelId: cstring, callback: SdsCallback, userData: pointer +): pointer {.dynlib, exportc, cdecl.} = + initializeLibrary() + + ## Creates a new instance of the WakuNode. + if isNil(callback): + echo "error: missing callback in NewReliabilityManager" + return nil + + ## Create the SDS thread that will keep waiting for req from the main thread. + var ctx = sds_thread.createSdsThread().valueOr: + let msg = "Error in createSdsThread: " & $error + callback(RET_ERR, unsafeAddr msg[0], cast[csize_t](len(msg)), userData) + return nil + + ctx.userData = userData + + let retCode = handleRequest( + ctx, + RequestType.LIFECYCLE, + SdsLifecycleRequest.createShared( + SdsLifecycleMsgType.CREATE_RELIABILITY_MANAGER, channelId + ), + callback, + userData, + ) + + if retCode == RET_ERR: + return nil + + return ctx + +### End of exported procs +################################################################################ From f21c3e6fcb7054a6d98d4a2044e68056bacea968 Mon Sep 17 00:00:00 2001 From: Gabriel mermelstein Date: Wed, 9 Apr 2025 18:31:43 +0300 Subject: [PATCH 09/29] fixing function signature --- library/libsds.h | 2 +- library/libsds.nim | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/library/libsds.h b/library/libsds.h index aa2909a..1bc26a7 100644 --- a/library/libsds.h +++ b/library/libsds.h @@ -28,7 +28,7 @@ typedef void (*SdsCallBack) (int callerRet, const char* msg, size_t len, void* u * @param channelId A unique identifier for the communication channel. * @return An opaque handle (void*) representing the instance, or NULL on failure. */ -void* NewReliabilityManager(char* channelId); +void* NewReliabilityManager(char* channelId, SdsCallBack callback, void* userData); #ifdef __cplusplus diff --git a/library/libsds.nim b/library/libsds.nim index 81e8362..767586c 100644 --- a/library/libsds.nim +++ b/library/libsds.nim @@ -108,7 +108,7 @@ proc initializeLibrary() {.exported.} = ### Exported procs proc NewReliabilityManager( - channelId: cstring, callback: SdsCallback, userData: pointer + channelId: cstring, callback: SdsCallBack, userData: pointer ): pointer {.dynlib, exportc, cdecl.} = initializeLibrary() From 59c965997e945ad5da72b44e5fadf1801003e1a6 Mon Sep 17 00:00:00 2001 From: Gabriel mermelstein Date: Wed, 9 Apr 2025 18:55:12 +0300 Subject: [PATCH 10/29] using const char --- library/libsds.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/libsds.h b/library/libsds.h index 1bc26a7..c11a432 100644 --- a/library/libsds.h +++ b/library/libsds.h @@ -28,7 +28,7 @@ typedef void (*SdsCallBack) (int callerRet, const char* msg, size_t len, void* u * @param channelId A unique identifier for the communication channel. * @return An opaque handle (void*) representing the instance, or NULL on failure. */ -void* NewReliabilityManager(char* channelId, SdsCallBack callback, void* userData); +void* NewReliabilityManager(const char* channelId, SdsCallBack callback, void* userData); #ifdef __cplusplus From de702d223a336417e8b2b547ddc8b3ec0a4101e7 Mon Sep 17 00:00:00 2001 From: Gabriel mermelstein Date: Thu, 10 Apr 2025 12:41:48 +0300 Subject: [PATCH 11/29] adding SetEventCallback --- library/libsds.h | 7 ++----- library/libsds.nim | 7 +++++++ 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/library/libsds.h b/library/libsds.h index c11a432..5d25140 100644 --- a/library/libsds.h +++ b/library/libsds.h @@ -23,13 +23,10 @@ typedef void (*SdsCallBack) (int callerRet, const char* msg, size_t len, void* u // --- Core API Functions --- -/** - * @brief Creates a new ReliabilityManager instance. - * @param channelId A unique identifier for the communication channel. - * @return An opaque handle (void*) representing the instance, or NULL on failure. - */ + void* NewReliabilityManager(const char* channelId, SdsCallBack callback, void* userData); +void SetEventCallback(void* ctx, SdsCallBack callback, void* userData); #ifdef __cplusplus } diff --git a/library/libsds.nim b/library/libsds.nim index 767586c..903bbec 100644 --- a/library/libsds.nim +++ b/library/libsds.nim @@ -140,5 +140,12 @@ proc NewReliabilityManager( return ctx +proc SetEventCallback( + ctx: ptr SdsContext, callback: SdsCallBack, userData: pointer +) {.dynlib, exportc.} = + initializeLibrary() + ctx[].eventCallback = cast[pointer](callback) + ctx[].eventUserData = userData + ### End of exported procs ################################################################################ From 6235fb187603987c195bdabd8371896d7d203743 Mon Sep 17 00:00:00 2001 From: Gabriel mermelstein Date: Thu, 10 Apr 2025 12:52:04 +0300 Subject: [PATCH 12/29] adding CleanupReliabilityManager --- library/libsds.h | 3 +++ library/libsds.nim | 18 +++++++++++++++++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/library/libsds.h b/library/libsds.h index 5d25140..00b4a05 100644 --- a/library/libsds.h +++ b/library/libsds.h @@ -28,6 +28,9 @@ void* NewReliabilityManager(const char* channelId, SdsCallBack callback, void* u void SetEventCallback(void* ctx, SdsCallBack callback, void* userData); +int CleanupReliabilityManager(void* ctx, SdsCallBack callback, void* userData); + + #ifdef __cplusplus } #endif diff --git a/library/libsds.nim b/library/libsds.nim index 903bbec..1d40d10 100644 --- a/library/libsds.nim +++ b/library/libsds.nim @@ -112,7 +112,7 @@ proc NewReliabilityManager( ): pointer {.dynlib, exportc, cdecl.} = initializeLibrary() - ## Creates a new instance of the WakuNode. + ## Creates a new instance of the Reliability Manager. if isNil(callback): echo "error: missing callback in NewReliabilityManager" return nil @@ -147,5 +147,21 @@ proc SetEventCallback( ctx[].eventCallback = cast[pointer](callback) ctx[].eventUserData = userData +proc CleanupReliabilityManager( + ctx: ptr SdsContext, callback: SdsCallBack, userData: pointer +): cint {.dynlib, exportc.} = + initializeLibrary() + checkLibsdsParams(ctx, callback, userData) + + sds_thread.destroySdsThread(ctx).isOkOr: + let msg = "libsds error: " & $error + callback(RET_ERR, unsafeAddr msg[0], cast[csize_t](len(msg)), userData) + return RET_ERR + + ## always need to invoke the callback although we don't retrieve value to the caller + callback(RET_OK, nil, 0, userData) + + return RET_OK + ### End of exported procs ################################################################################ From 7609aa36b0db26a1ac60ab24469b64503925b73b Mon Sep 17 00:00:00 2001 From: Gabriel mermelstein Date: Thu, 10 Apr 2025 14:25:53 +0300 Subject: [PATCH 13/29] adding reset function --- library/libsds.h | 2 ++ library/libsds.nim | 12 ++++++++++++ .../requests/sds_lifecycle_request.nim | 4 ++++ 3 files changed, 18 insertions(+) diff --git a/library/libsds.h b/library/libsds.h index 00b4a05..8f6a57f 100644 --- a/library/libsds.h +++ b/library/libsds.h @@ -30,6 +30,8 @@ void SetEventCallback(void* ctx, SdsCallBack callback, void* userData); int CleanupReliabilityManager(void* ctx, SdsCallBack callback, void* userData); +int ResetReliabilityManager(void* ctx, SdsCallBack callback, void* userData); + #ifdef __cplusplus } diff --git a/library/libsds.nim b/library/libsds.nim index 1d40d10..e85f12b 100644 --- a/library/libsds.nim +++ b/library/libsds.nim @@ -163,5 +163,17 @@ proc CleanupReliabilityManager( return RET_OK +proc ResetReliabilityManager( + ctx: ptr SdsContext, callback: SdsCallBack, userData: pointer +): cint {.dynlib, exportc.} = + checkLibsdsParams(ctx, callback, userData) + handleRequest( + ctx, + RequestType.LIFECYCLE, + SdsLifecycleRequest.createShared(SdsLifecycleMsgType.RESET_RELIABILITY_MANAGER), + callback, + userData, + ) + ### End of exported procs ################################################################################ diff --git a/library/sds_thread/inter_thread_communication/requests/sds_lifecycle_request.nim b/library/sds_thread/inter_thread_communication/requests/sds_lifecycle_request.nim index 4ade389..1794bd2 100644 --- a/library/sds_thread/inter_thread_communication/requests/sds_lifecycle_request.nim +++ b/library/sds_thread/inter_thread_communication/requests/sds_lifecycle_request.nim @@ -6,6 +6,7 @@ import ../../../../src/[reliability_utils, reliability, message] type SdsLifecycleMsgType* = enum CREATE_RELIABILITY_MANAGER + RESET_RELIABILITY_MANAGER type SdsLifecycleRequest* = object operation: SdsLifecycleMsgType @@ -58,5 +59,8 @@ proc process*( rm[] = (await createReliabilityManager(self.channelId)).valueOr: error "CREATE_RELIABILITY_MANAGER failed", error = error return err("error processing CREATE_RELIABILITY_MANAGER request: " & $error) + of RESET_RELIABILITY_MANAGER: + resetReliabilityManager(rm[]).isOkOr: + return err("error processing RESET_RELIABILITY_MANAGER request: " & $error) return ok("") From 30d05f13bf0f9d058bc0aa0081a93aebbfd9ba11 Mon Sep 17 00:00:00 2001 From: Gabriel mermelstein Date: Thu, 10 Apr 2025 16:14:44 +0300 Subject: [PATCH 14/29] adding message request type --- library/libsds.h | 6 +++ library/libsds.nim | 29 ++++++++++++- .../requests/sds_message_request.nim | 42 +++++++++++++++++++ .../sds_thread_request.nim | 7 +++- 4 files changed, 82 insertions(+), 2 deletions(-) create mode 100644 library/sds_thread/inter_thread_communication/requests/sds_message_request.nim diff --git a/library/libsds.h b/library/libsds.h index 8f6a57f..91d59cc 100644 --- a/library/libsds.h +++ b/library/libsds.h @@ -32,6 +32,12 @@ int CleanupReliabilityManager(void* ctx, SdsCallBack callback, void* userData); int ResetReliabilityManager(void* ctx, SdsCallBack callback, void* userData); +int WrapOutgoingMessage(void* ctx, + const char* message, + const char* messageId, + SdsCallBack callback, + void* userData); + #ifdef __cplusplus } diff --git a/library/libsds.nim b/library/libsds.nim index e85f12b..8e1f017 100644 --- a/library/libsds.nim +++ b/library/libsds.nim @@ -13,7 +13,8 @@ import ./alloc, ./ffi_types, ./sds_thread/inter_thread_communication/sds_thread_request, - ./sds_thread/inter_thread_communication/requests/sds_lifecycle_request, + ./sds_thread/inter_thread_communication/requests/ + [sds_lifecycle_request, sds_message_request], ../src/[reliability, reliability_utils, message] ################################################################################ @@ -175,5 +176,31 @@ proc ResetReliabilityManager( userData, ) +proc WrapOutgoingMessage( + ctx: ptr SdsContext, + message: cstring, + messageId: cstring, + callback: SdsCallBack, + userData: pointer, +): cint {.dynlib, exportc.} = + initializeLibrary() + checkLibsdsParams(ctx, callback, userData) + + let + msg = message.alloc() + msgId = messageId.alloc() + + defer: + deallocShared(msg) + deallocShared(msgId) + + handleRequest( + ctx, + RequestType.MESSAGE, + SdsMessageRequest.createShared(SdsMessageMsgType.WRAP_MESSAGE, msg, msgId), + callback, + userData, + ) + ### End of exported procs ################################################################################ diff --git a/library/sds_thread/inter_thread_communication/requests/sds_message_request.nim b/library/sds_thread/inter_thread_communication/requests/sds_message_request.nim new file mode 100644 index 0000000..d1d025b --- /dev/null +++ b/library/sds_thread/inter_thread_communication/requests/sds_message_request.nim @@ -0,0 +1,42 @@ +import std/[options, json, strutils, net] +import chronos, chronicles, results, confutils, confutils/std/net + +import ../../../alloc +import ../../../../src/[reliability_utils, reliability, message] + +type SdsMessageMsgType* = enum + WRAP_MESSAGE + +type SdsMessageRequest* = object + operation: SdsMessageMsgType + message: cstring + messageId: cstring + +proc createShared*( + T: type SdsMessageRequest, + op: SdsMessageMsgType, + message: cstring = "", + messageId: cstring = "", +): ptr type T = + var ret = createShared(T) + ret[].operation = op + ret[].message = message.alloc() + ret[].messageId = messageId.alloc() + return ret + +proc destroyShared(self: ptr SdsMessageRequest) = + deallocShared(self[].message) + deallocShared(self[].messageId) + deallocShared(self) + +proc process*( + self: ptr SdsMessageRequest, rm: ptr ReliabilityManager +): Future[Result[string, string]] {.async.} = + defer: + destroyShared(self) + + case self.operation + of WRAP_MESSAGE: + echo "------- received wrap message request" + + return ok("") diff --git a/library/sds_thread/inter_thread_communication/sds_thread_request.nim b/library/sds_thread/inter_thread_communication/sds_thread_request.nim index 855e77b..9ea8951 100644 --- a/library/sds_thread/inter_thread_communication/sds_thread_request.nim +++ b/library/sds_thread/inter_thread_communication/sds_thread_request.nim @@ -5,10 +5,13 @@ import std/json, results import chronos, chronos/threadsync import - ../../ffi_types, ./requests/sds_lifecycle_request, ../../../src/[reliability_utils] + ../../ffi_types, + ./requests/[sds_lifecycle_request, sds_message_request], + ../../../src/[reliability_utils] type RequestType* {.pure.} = enum LIFECYCLE + MESSAGE type SdsThreadRequest* = object reqType: RequestType @@ -63,6 +66,8 @@ proc process*( case request[].reqType of LIFECYCLE: cast[ptr SdsLifecycleRequest](request[].reqContent).process(rm) + of MESSAGE: + cast[ptr SdsMessageRequest](request[].reqContent).process(rm) handleRes(await retFut, request) From 112008c906dc644bd9d51aa00ac7b9e36a1db285 Mon Sep 17 00:00:00 2001 From: Gabriel mermelstein Date: Fri, 11 Apr 2025 17:46:21 +0300 Subject: [PATCH 15/29] setting up shared seq for messages request --- library/alloc.nim | 11 +++++++++ library/libsds.h | 3 ++- library/libsds.nim | 24 ++++++++++++++----- .../requests/sds_message_request.nim | 17 ++++++++----- 4 files changed, 42 insertions(+), 13 deletions(-) diff --git a/library/alloc.nim b/library/alloc.nim index 1a6f118..e1ba4a4 100644 --- a/library/alloc.nim +++ b/library/alloc.nim @@ -40,3 +40,14 @@ proc toSeq*[T](s: SharedSeq[T]): seq[T] = for i in 0 ..< s.len: ret.add(s.data[i]) return ret + +proc allocSharedSeqFromCArray*[T](arr: ptr T, len: int): SharedSeq[T] = + ## Creates a SharedSeq[T] from a C array pointer and length. + ## The data is copied to shared memory. + ## There should be a corresponding manual deallocation with deallocSharedSeq! + if arr.isNil or len <= 0: + return (nil, 0) + + let data = allocShared(sizeof(T) * len) + copyMem(data, arr, sizeof(T) * len) + return (cast[ptr UncheckedArray[T]](data), len) diff --git a/library/libsds.h b/library/libsds.h index 91d59cc..590e6a4 100644 --- a/library/libsds.h +++ b/library/libsds.h @@ -33,7 +33,8 @@ int CleanupReliabilityManager(void* ctx, SdsCallBack callback, void* userData); int ResetReliabilityManager(void* ctx, SdsCallBack callback, void* userData); int WrapOutgoingMessage(void* ctx, - const char* message, + void* message, + size_t messageLen, const char* messageId, SdsCallBack callback, void* userData); diff --git a/library/libsds.nim b/library/libsds.nim index 8e1f017..cbe3e93 100644 --- a/library/libsds.nim +++ b/library/libsds.nim @@ -178,7 +178,8 @@ proc ResetReliabilityManager( proc WrapOutgoingMessage( ctx: ptr SdsContext, - message: cstring, + message: pointer, + messageLen: csize_t, messageId: cstring, callback: SdsCallBack, userData: pointer, @@ -186,18 +187,29 @@ proc WrapOutgoingMessage( initializeLibrary() checkLibsdsParams(ctx, callback, userData) - let - msg = message.alloc() - msgId = messageId.alloc() + if message == nil and messageLen > 0: + let msg = "libsds error: " & "message pointer is NULL but length > 0" + callback(RET_ERR, unsafeAddr msg[0], cast[csize_t](len(msg)), userData) + return RET_ERR + + if messageId == nil: + let msg = "libsds error: " & "message ID pointer is NULL" + callback(RET_ERR, unsafeAddr msg[0], cast[csize_t](len(msg)), userData) + return RET_ERR + + var msg = allocSharedSeqFromCArray(cast[ptr byte](message), messageLen.int) + let msgId = messageId.alloc() defer: - deallocShared(msg) + deallocSharedSeq(msg) deallocShared(msgId) handleRequest( ctx, RequestType.MESSAGE, - SdsMessageRequest.createShared(SdsMessageMsgType.WRAP_MESSAGE, msg, msgId), + SdsMessageRequest.createShared( + SdsMessageMsgType.WRAP_MESSAGE, msg, messageLen, msgId + ), callback, userData, ) diff --git a/library/sds_thread/inter_thread_communication/requests/sds_message_request.nim b/library/sds_thread/inter_thread_communication/requests/sds_message_request.nim index d1d025b..32fea23 100644 --- a/library/sds_thread/inter_thread_communication/requests/sds_message_request.nim +++ b/library/sds_thread/inter_thread_communication/requests/sds_message_request.nim @@ -9,24 +9,27 @@ type SdsMessageMsgType* = enum type SdsMessageRequest* = object operation: SdsMessageMsgType - message: cstring + message: SharedSeq[byte] + messageLen: csize_t messageId: cstring proc createShared*( T: type SdsMessageRequest, op: SdsMessageMsgType, - message: cstring = "", + message: SharedSeq[byte], + messageLen: csize_t = 0, messageId: cstring = "", ): ptr type T = var ret = createShared(T) ret[].operation = op - ret[].message = message.alloc() - ret[].messageId = messageId.alloc() + ret[].message = message # check if alloc is needed + ret[].messageLen = messageLen + ret[].messageId = messageId # check if alloc is needed return ret proc destroyShared(self: ptr SdsMessageRequest) = - deallocShared(self[].message) - deallocShared(self[].messageId) + #deallocShared(self[].message) + #deallocShared(self[].messageId) deallocShared(self) proc process*( @@ -37,6 +40,8 @@ proc process*( case self.operation of WRAP_MESSAGE: + let byteSeq = self.message.toSeq() + echo "------------ byteSeq: ", byteSeq echo "------- received wrap message request" return ok("") From 6320667dfd30e18f38b226b4fc4ea774b3a25430 Mon Sep 17 00:00:00 2001 From: Gabriel mermelstein Date: Tue, 15 Apr 2025 13:15:18 +0300 Subject: [PATCH 16/29] adding WrapOutgoingMessage --- .../requests/sds_message_request.nim | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/library/sds_thread/inter_thread_communication/requests/sds_message_request.nim b/library/sds_thread/inter_thread_communication/requests/sds_message_request.nim index 32fea23..99fddc0 100644 --- a/library/sds_thread/inter_thread_communication/requests/sds_message_request.nim +++ b/library/sds_thread/inter_thread_communication/requests/sds_message_request.nim @@ -1,4 +1,4 @@ -import std/[options, json, strutils, net] +import std/[options, json, strutils, net, sequtils] import chronos, chronicles, results, confutils, confutils/std/net import ../../../alloc @@ -40,8 +40,13 @@ proc process*( case self.operation of WRAP_MESSAGE: - let byteSeq = self.message.toSeq() - echo "------------ byteSeq: ", byteSeq - echo "------- received wrap message request" + let messageBytes = self.message.toSeq() + + let wrappedMessage = wrapOutgoingMessage(rm[], messageBytes, $self.messageId).valueOr: + error "WRAP_MESSAGE failed", error = error + return err("error processing WRAP_MESSAGE request: " & $error) + + # returns a comma-separates string of bytes + return ok(wrappedMessage.mapIt($it).join(",")) return ok("") From 79f42fb4617c94deec51942da62501475284854f Mon Sep 17 00:00:00 2001 From: Gabriel mermelstein Date: Tue, 15 Apr 2025 16:23:06 +0300 Subject: [PATCH 17/29] initial unwrap implementation --- library/libsds.h | 6 ++ library/libsds.nim | 28 +++++++ .../requests/sds_message_request.nim | 16 ++++ src/reliability.nim | 80 ++++++++++--------- src/reliability_utils.nim | 7 +- 5 files changed, 97 insertions(+), 40 deletions(-) diff --git a/library/libsds.h b/library/libsds.h index 590e6a4..ff637d8 100644 --- a/library/libsds.h +++ b/library/libsds.h @@ -39,6 +39,12 @@ int WrapOutgoingMessage(void* ctx, SdsCallBack callback, void* userData); +int UnwrapReceivedMessage(void* ctx, + void* message, + size_t messageLen, + SdsCallBack callback, + void* userData); + #ifdef __cplusplus } diff --git a/library/libsds.nim b/library/libsds.nim index cbe3e93..8fea804 100644 --- a/library/libsds.nim +++ b/library/libsds.nim @@ -214,5 +214,33 @@ proc WrapOutgoingMessage( userData, ) +proc UnwrapReceivedMessage( + ctx: ptr SdsContext, + message: pointer, + messageLen: csize_t, + callback: SdsCallBack, + userData: pointer, +): cint {.dynlib, exportc.} = + initializeLibrary() + checkLibsdsParams(ctx, callback, userData) + + if message == nil and messageLen > 0: + let msg = "libsds error: " & "message pointer is NULL but length > 0" + callback(RET_ERR, unsafeAddr msg[0], cast[csize_t](len(msg)), userData) + return RET_ERR + + var msg = allocSharedSeqFromCArray(cast[ptr byte](message), messageLen.int) + + defer: + deallocSharedSeq(msg) + + handleRequest( + ctx, + RequestType.MESSAGE, + SdsMessageRequest.createShared(SdsMessageMsgType.UNWRAP_MESSAGE, msg, messageLen), + callback, + userData, + ) + ### End of exported procs ################################################################################ diff --git a/library/sds_thread/inter_thread_communication/requests/sds_message_request.nim b/library/sds_thread/inter_thread_communication/requests/sds_message_request.nim index 99fddc0..77ec25f 100644 --- a/library/sds_thread/inter_thread_communication/requests/sds_message_request.nim +++ b/library/sds_thread/inter_thread_communication/requests/sds_message_request.nim @@ -6,6 +6,7 @@ import ../../../../src/[reliability_utils, reliability, message] type SdsMessageMsgType* = enum WRAP_MESSAGE + UNWRAP_MESSAGE type SdsMessageRequest* = object operation: SdsMessageMsgType @@ -13,6 +14,10 @@ type SdsMessageRequest* = object messageLen: csize_t messageId: cstring +type SdsUnwrapResponse* = object + message*: seq[byte] + missingDeps*: seq[MessageID] + proc createShared*( T: type SdsMessageRequest, op: SdsMessageMsgType, @@ -48,5 +53,16 @@ proc process*( # returns a comma-separates string of bytes return ok(wrappedMessage.mapIt($it).join(",")) + of UNWRAP_MESSAGE: + let messageBytes = self.message.toSeq() + + let (unwrappedMessage, missingDeps) = unwrapReceivedMessage(rm[], messageBytes).valueOr: + error "UNWRAP_MESSAGE failed", error = error + return err("error processing UNWRAP_MESSAGE request: " & $error) + + let res = SdsUnwrapResponse(message: unwrappedMessage, missingDeps: missingDeps) + + # return the result as a json string + return ok($(%*(res))) return ok("") diff --git a/src/reliability.nim b/src/reliability.nim index 29e3c36..0ad08e0 100644 --- a/src/reliability.nim +++ b/src/reliability.nim @@ -2,7 +2,9 @@ import std/[times, locks, tables, sets] import chronos, results import ../src/[message, protobuf, reliability_utils, rolling_bloom_filter] -proc newReliabilityManager*(channelId: string, config: ReliabilityConfig = defaultConfig()): Result[ReliabilityManager, ReliabilityError] = +proc newReliabilityManager*( + channelId: string, config: ReliabilityConfig = defaultConfig() +): Result[ReliabilityManager, ReliabilityError] = ## Creates a new ReliabilityManager with the specified channel ID and configuration. ## ## Parameters: @@ -13,14 +15,12 @@ proc newReliabilityManager*(channelId: string, config: ReliabilityConfig = defau ## A Result containing either a new ReliabilityManager instance or an error. if channelId.len == 0: return err(reInvalidArgument) - + try: let bloomFilter = newRollingBloomFilter( - config.bloomFilterCapacity, - config.bloomFilterErrorRate, - config.bloomFilterWindow + config.bloomFilterCapacity, config.bloomFilterErrorRate, config.bloomFilterWindow ) - + let rm = ReliabilityManager( lamportTimestamp: 0, messageHistory: @[], @@ -28,7 +28,7 @@ proc newReliabilityManager*(channelId: string, config: ReliabilityConfig = defau outgoingBuffer: @[], incomingBuffer: @[], channelId: channelId, - config: config + config: config, ) initLock(rm.lock) return ok(rm) @@ -40,27 +40,25 @@ proc reviewAckStatus(rm: ReliabilityManager, msg: Message) = while i < rm.outgoingBuffer.len: var acknowledged = false let outMsg = rm.outgoingBuffer[i] - + # Check if message is in causal history for msgID in msg.causalHistory: if outMsg.message.messageId == msgID: acknowledged = true break - + # Check bloom filter if not already acknowledged if not acknowledged and msg.bloomFilter.len > 0: let bfResult = deserializeBloomFilter(msg.bloomFilter) if bfResult.isOk: var rbf = RollingBloomFilter( - filter: bfResult.get(), - window: rm.bloomFilter.window, - messages: @[] + filter: bfResult.get(), window: rm.bloomFilter.window, messages: @[] ) if rbf.contains(outMsg.message.messageId): acknowledged = true else: logError("Failed to deserialize bloom filter") - + if acknowledged: if rm.onMessageSent != nil: rm.onMessageSent(outMsg.message.messageId) @@ -68,7 +66,9 @@ proc reviewAckStatus(rm: ReliabilityManager, msg: Message) = else: inc i -proc wrapOutgoingMessage*(rm: ReliabilityManager, message: seq[byte], messageId: MessageID): Result[seq[byte], ReliabilityError] = +proc wrapOutgoingMessage*( + rm: ReliabilityManager, message: seq[byte], messageId: MessageID +): Result[seq[byte], ReliabilityError] = ## Wraps an outgoing message with reliability metadata. ## ## Parameters: @@ -84,7 +84,7 @@ proc wrapOutgoingMessage*(rm: ReliabilityManager, message: seq[byte], messageId: withLock rm.lock: try: rm.updateLamportTimestamp(getTime().toUnix) - + # Serialize current bloom filter var bloomBytes: seq[byte] let bfResult = serializeBloomFilter(rm.bloomFilter.filter) @@ -100,15 +100,13 @@ proc wrapOutgoingMessage*(rm: ReliabilityManager, message: seq[byte], messageId: causalHistory: rm.getRecentMessageIDs(rm.config.maxCausalHistory), channelId: rm.channelId, content: message, - bloomFilter: bloomBytes + bloomFilter: bloomBytes, ) # Add to outgoing buffer - rm.outgoingBuffer.add(UnacknowledgedMessage( - message: msg, - sendTime: getTime(), - resendAttempts: 0 - )) + rm.outgoingBuffer.add( + UnacknowledgedMessage(message: msg, sendTime: getTime(), resendAttempts: 0) + ) # Add to causal history and bloom filter rm.bloomFilter.add(msg.messageId) @@ -156,7 +154,7 @@ proc processIncomingBuffer(rm: ReliabilityManager) = if rm.onMessageReady != nil: rm.onMessageReady(msg.messageId) processed.incl(msgId) - + # Add any dependent messages that might now be ready if msgId in dependencies: for dependentId in dependencies[msgId]: @@ -170,7 +168,11 @@ proc processIncomingBuffer(rm: ReliabilityManager) = rm.incomingBuffer = newIncomingBuffer -proc unwrapReceivedMessage*(rm: ReliabilityManager, message: seq[byte]): Result[tuple[message: seq[byte], missingDeps: seq[MessageID]], ReliabilityError] = +proc unwrapReceivedMessage*( + rm: ReliabilityManager, message: seq[byte] +): Result[tuple[message: seq[byte], missingDeps: seq[MessageID]], ReliabilityError] {. + gcsafe +.} = ## Unwraps a received message and processes its reliability metadata. ## ## Parameters: @@ -182,7 +184,7 @@ proc unwrapReceivedMessage*(rm: ReliabilityManager, message: seq[byte]): Result[ let msgResult = deserializeMessage(message) if not msgResult.isOk: return err(msgResult.error) - + let msg = msgResult.get if rm.bloomFilter.contains(msg.messageId): return ok((msg.content, @[])) @@ -225,7 +227,9 @@ proc unwrapReceivedMessage*(rm: ReliabilityManager, message: seq[byte]): Result[ except: return err(reInternalError) -proc markDependenciesMet*(rm: ReliabilityManager, messageIds: seq[MessageID]): Result[void, ReliabilityError] = +proc markDependenciesMet*( + rm: ReliabilityManager, messageIds: seq[MessageID] +): Result[void, ReliabilityError] = ## Marks the specified message dependencies as met. ## ## Parameters: @@ -240,16 +244,19 @@ proc markDependenciesMet*(rm: ReliabilityManager, messageIds: seq[MessageID]): R rm.bloomFilter.add(msgId) # rm.addToHistory(msgId) -- not needed as this proc usually called when msg in long-term storage of application? rm.processIncomingBuffer() - + return ok() except: return err(reInternalError) -proc setCallbacks*(rm: ReliabilityManager, - onMessageReady: proc(messageId: MessageID) {.gcsafe.}, - onMessageSent: proc(messageId: MessageID) {.gcsafe.}, - onMissingDependencies: proc(messageId: MessageID, missingDeps: seq[MessageID]) {.gcsafe.}, - onPeriodicSync: PeriodicSyncCallback = nil) = +proc setCallbacks*( + rm: ReliabilityManager, + onMessageReady: proc(messageId: MessageID) {.gcsafe.}, + onMessageSent: proc(messageId: MessageID) {.gcsafe.}, + onMissingDependencies: + proc(messageId: MessageID, missingDeps: seq[MessageID]) {.gcsafe.}, + onPeriodicSync: PeriodicSyncCallback = nil, +) = ## Sets the callback functions for various events in the ReliabilityManager. ## ## Parameters: @@ -268,7 +275,7 @@ proc checkUnacknowledgedMessages*(rm: ReliabilityManager) {.raises: [].} = withLock rm.lock: let now = getTime() var newOutgoingBuffer: seq[UnacknowledgedMessage] = @[] - + try: for unackMsg in rm.outgoingBuffer: let elapsed = now - unackMsg.sendTime @@ -298,7 +305,7 @@ proc periodicBufferSweep(rm: ReliabilityManager) {.async: (raises: [CancelledErr rm.cleanBloomFilter() except Exception as e: logError("Error in periodic buffer sweep: " & e.msg) - + await sleepAsync(chronos.milliseconds(rm.config.bufferSweepInterval.inMilliseconds)) proc periodicSyncMessage(rm: ReliabilityManager) {.async: (raises: [CancelledError]).} = @@ -333,10 +340,9 @@ proc resetReliabilityManager*(rm: ReliabilityManager): Result[void, ReliabilityE rm.outgoingBuffer.setLen(0) rm.incomingBuffer.setLen(0) rm.bloomFilter = newRollingBloomFilter( - rm.config.bloomFilterCapacity, - rm.config.bloomFilterErrorRate, - rm.config.bloomFilterWindow + rm.config.bloomFilterCapacity, rm.config.bloomFilterErrorRate, + rm.config.bloomFilterWindow, ) return ok() except: - return err(reInternalError) \ No newline at end of file + return err(reInternalError) diff --git a/src/reliability_utils.nim b/src/reliability_utils.nim index ec576dc..25abb6d 100644 --- a/src/reliability_utils.nim +++ b/src/reliability_utils.nim @@ -24,9 +24,10 @@ type channelId*: string config*: ReliabilityConfig lock*: Lock - onMessageReady*: proc(messageId: MessageID) - onMessageSent*: proc(messageId: MessageID) - onMissingDependencies*: proc(messageId: MessageID, missingDeps: seq[MessageID]) + onMessageReady*: proc(messageId: MessageID) {.gcsafe.} + onMessageSent*: proc(messageId: MessageID) {.gcsafe.} + onMissingDependencies*: + proc(messageId: MessageID, missingDeps: seq[MessageID]) {.gcsafe.} onPeriodicSync*: proc() ReliabilityError* = enum From c0431d53a6e4a273d0c2fa44b7a266ee6366e533 Mon Sep 17 00:00:00 2001 From: Gabriel mermelstein Date: Tue, 15 Apr 2025 18:03:49 +0300 Subject: [PATCH 18/29] initial addition of dependencies request --- library/alloc.nim | 26 ++++++++-- library/libsds.h | 7 +++ library/libsds.nim | 32 +++++++++++- .../requests/sds_dependencies_request.nim | 49 +++++++++++++++++++ .../sds_thread_request.nim | 5 +- 5 files changed, 114 insertions(+), 5 deletions(-) create mode 100644 library/sds_thread/inter_thread_communication/requests/sds_dependencies_request.nim diff --git a/library/alloc.nim b/library/alloc.nim index e1ba4a4..b185dd7 100644 --- a/library/alloc.nim +++ b/library/alloc.nim @@ -30,6 +30,14 @@ proc allocSharedSeq*[T](s: seq[T]): SharedSeq[T] = return (cast[ptr UncheckedArray[T]](data), s.len) proc deallocSharedSeq*[T](s: var SharedSeq[T]) = + if not s.data.isNil: + when T is cstring: + # For array of cstrings, deallocate each string first + for i in 0 ..< s.len: + if not s.data[i].isNil: + # Deallocate each cstring + deallocShared(s.data[i]) + deallocShared(s.data) s.len = 0 @@ -48,6 +56,18 @@ proc allocSharedSeqFromCArray*[T](arr: ptr T, len: int): SharedSeq[T] = if arr.isNil or len <= 0: return (nil, 0) - let data = allocShared(sizeof(T) * len) - copyMem(data, arr, sizeof(T) * len) - return (cast[ptr UncheckedArray[T]](data), len) + when T is cstring: + # Special handling for arrays of cstrings + let data = cast[ptr UncheckedArray[cstring]](allocShared(sizeof(cstring) * len)) + let cstrArr = cast[ptr UncheckedArray[cstring]](arr) + + for i in 0 ..< len: + # Use the existing alloc proc to properly allocate each cstring + data[i] = cstrArr[i].alloc() + + return (data, len) + else: + # Original handling for non-cstring types + let data = allocShared(sizeof(T) * len) + copyMem(data, arr, sizeof(T) * len) + return (cast[ptr UncheckedArray[T]](data), len) diff --git a/library/libsds.h b/library/libsds.h index ff637d8..63a16c8 100644 --- a/library/libsds.h +++ b/library/libsds.h @@ -45,6 +45,13 @@ int UnwrapReceivedMessage(void* ctx, SdsCallBack callback, void* userData); +int MarkDependenciesMet(void* ctx, + char** messageIDs, + size_t count, + SdsCallBack callback, + void* userData); + + #ifdef __cplusplus } diff --git a/library/libsds.nim b/library/libsds.nim index 8fea804..2491850 100644 --- a/library/libsds.nim +++ b/library/libsds.nim @@ -14,7 +14,7 @@ import ./ffi_types, ./sds_thread/inter_thread_communication/sds_thread_request, ./sds_thread/inter_thread_communication/requests/ - [sds_lifecycle_request, sds_message_request], + [sds_lifecycle_request, sds_message_request, sds_dependencies_request], ../src/[reliability, reliability_utils, message] ################################################################################ @@ -242,5 +242,35 @@ proc UnwrapReceivedMessage( userData, ) +proc MarkDependenciesMet( + ctx: ptr SdsContext, + messageIds: pointer, + count: csize_t, + callback: SdsCallBack, + userData: pointer, +): cint {.dynlib, exportc.} = + initializeLibrary() + checkLibsdsParams(ctx, callback, userData) + + if messageIds == nil and count > 0: + let msg = "libsds error: " & "MessageIDs pointer is NULL but count > 0" + callback(RET_ERR, unsafeAddr msg[0], cast[csize_t](len(msg)), userData) + return RET_ERR + + var messageIds = allocSharedSeqFromCArray(cast[ptr cstring](messageIds), count.int) + + defer: + deallocSharedSeq(messageIds) + + handleRequest( + ctx, + RequestType.DEPENDENCIES, + SdsDependenciesRequest.createShared( + SdsDependenciesMsgType.MARK_DEPENDENCIES_MET, messageIds, count + ), + callback, + userData, + ) + ### End of exported procs ################################################################################ diff --git a/library/sds_thread/inter_thread_communication/requests/sds_dependencies_request.nim b/library/sds_thread/inter_thread_communication/requests/sds_dependencies_request.nim new file mode 100644 index 0000000..7c45087 --- /dev/null +++ b/library/sds_thread/inter_thread_communication/requests/sds_dependencies_request.nim @@ -0,0 +1,49 @@ +import std/[options, json, strutils, net, sequtils] +import chronos, chronicles, results, confutils, confutils/std/net + +import ../../../alloc +import ../../../../src/[reliability_utils, reliability, message] + +type SdsDependenciesMsgType* = enum + MARK_DEPENDENCIES_MET + +type SdsDependenciesRequest* = object + operation: SdsDependenciesMsgType + messageIds: SharedSeq[cstring] + count: csize_t + +proc createShared*( + T: type SdsDependenciesRequest, + op: SdsDependenciesMsgType, + messageIds: SharedSeq[cstring], + count: csize_t = 0, +): ptr type T = + var ret = createShared(T) + ret[].operation = op + ret[].messageIds = messageIds # check if alloc is needed + ret[].count = count + return ret + +proc destroyShared(self: ptr SdsDependenciesRequest) = + #deallocShared(self[].message) + #deallocShared(self[].messageId) + deallocShared(self) + +proc process*( + self: ptr SdsDependenciesRequest, rm: ptr ReliabilityManager +): Future[Result[string, string]] {.async.} = + defer: + destroyShared(self) + + case self.operation + of MARK_DEPENDENCIES_MET: + let messageIdsC = self.messageIds.toSeq() + let messageIds = messageIdsC.mapIt($it) + + markDependenciesMet(rm[], messageIds).isOkOr: + error "MARK_DEPENDENCIES_MET failed", error = error + return err("error processing MARK_DEPENDENCIES_MET request: " & $error) + + return ok("") + + return ok("") diff --git a/library/sds_thread/inter_thread_communication/sds_thread_request.nim b/library/sds_thread/inter_thread_communication/sds_thread_request.nim index 9ea8951..f40bef4 100644 --- a/library/sds_thread/inter_thread_communication/sds_thread_request.nim +++ b/library/sds_thread/inter_thread_communication/sds_thread_request.nim @@ -6,12 +6,13 @@ import std/json, results import chronos, chronos/threadsync import ../../ffi_types, - ./requests/[sds_lifecycle_request, sds_message_request], + ./requests/[sds_lifecycle_request, sds_message_request, sds_dependencies_request], ../../../src/[reliability_utils] type RequestType* {.pure.} = enum LIFECYCLE MESSAGE + DEPENDENCIES type SdsThreadRequest* = object reqType: RequestType @@ -68,6 +69,8 @@ proc process*( cast[ptr SdsLifecycleRequest](request[].reqContent).process(rm) of MESSAGE: cast[ptr SdsMessageRequest](request[].reqContent).process(rm) + of DEPENDENCIES: + cast[ptr SdsDependenciesRequest](request[].reqContent).process(rm) handleRes(await retFut, request) From 521ac4a3bff6692e42164afa2ae4ba7c0c59fd71 Mon Sep 17 00:00:00 2001 From: Gabriel mermelstein Date: Wed, 16 Apr 2025 11:01:48 +0300 Subject: [PATCH 19/29] start adding events --- library/events/json_base_event.nim | 6 ++++++ library/events/json_message_ready_event.nim | 14 ++++++++++++++ library/events/json_message_sent_event.nim | 14 ++++++++++++++ library/libsds.nim | 17 +++++++++++++---- src/reliability.nim | 7 +++---- src/reliability_utils.nim | 13 +++++++++++++ 6 files changed, 63 insertions(+), 8 deletions(-) create mode 100644 library/events/json_base_event.nim create mode 100644 library/events/json_message_ready_event.nim create mode 100644 library/events/json_message_sent_event.nim diff --git a/library/events/json_base_event.nim b/library/events/json_base_event.nim new file mode 100644 index 0000000..8c51d2c --- /dev/null +++ b/library/events/json_base_event.nim @@ -0,0 +1,6 @@ +type JsonEvent* = ref object of RootObj # https://rfc.vac.dev/spec/36/#jsonsignal-type + eventType* {.requiresInit.}: string + +method `$`*(jsonEvent: JsonEvent): string {.base.} = + discard + # All events should implement this diff --git a/library/events/json_message_ready_event.nim b/library/events/json_message_ready_event.nim new file mode 100644 index 0000000..4a4b18f --- /dev/null +++ b/library/events/json_message_ready_event.nim @@ -0,0 +1,14 @@ +import std/json +import ./json_base_event, ../../src/[message] + +type JsonMessageReadyEvent* = ref object of JsonEvent + messageId*: MessageID + +proc new*(T: type JsonMessageReadyEvent, messageId: MessageID): T = + # Returns a MessageReady event as indicated in + # https://rfc.vac.dev/spec/36/#jsonmessageevent-type + + return JsonMessageReadyEvent(eventType: "message_ready", messageId: messageId) + +method `$`*(jsonMessageReady: JsonMessageReadyEvent): string = + $(%*jsonMessageReady) diff --git a/library/events/json_message_sent_event.nim b/library/events/json_message_sent_event.nim new file mode 100644 index 0000000..714a933 --- /dev/null +++ b/library/events/json_message_sent_event.nim @@ -0,0 +1,14 @@ +import std/json +import ./json_base_event, ../../src/[message] + +type JsonMessageSentEvent* = ref object of JsonEvent + messageId*: MessageID + +proc new*(T: type JsonMessageSentEvent, messageId: MessageID): T = + # Returns a MessageSent event as indicated in + # https://rfc.vac.dev/spec/36/#jsonmessageevent-type + + return JsonMessageSentEvent(eventType: "message_sent", messageId: messageId) + +method `$`*(jsonMessageSent: JsonMessageSentEvent): string = + $(%*jsonMessageSent) diff --git a/library/libsds.nim b/library/libsds.nim index 2491850..162f5a9 100644 --- a/library/libsds.nim +++ b/library/libsds.nim @@ -5,9 +5,7 @@ when defined(linux): {.passl: "-Wl,-soname,libsds.so".} -import std/[locks, typetraits, tables, atomics] # Added tables -import chronos -import results +import std/[locks, typetraits, tables, atomics], chronos, chronicles import ./sds_thread/sds_thread, ./alloc, @@ -15,7 +13,8 @@ import ./sds_thread/inter_thread_communication/sds_thread_request, ./sds_thread/inter_thread_communication/requests/ [sds_lifecycle_request, sds_message_request, sds_dependencies_request], - ../src/[reliability, reliability_utils, message] + ../src/[reliability, reliability_utils, message], + ./events/[json_message_ready_event, json_message_sent_event] ################################################################################ ### Wrapper around the reliability manager @@ -69,6 +68,16 @@ proc handleRequest( return RET_OK +proc onMessageReady(ctx: ptr SdsContext): MessageReadyCallback = + return proc(messageId: MessageID) {.gcsafe.} = + callEventCallback(ctx, "onMessageReady"): + $JsonMessageReadyEvent.new(messageId) + +proc onMessageSent(ctx: ptr SdsContext): MessageSentCallback = + return proc(messageId: MessageID) {.gcsafe.} = + callEventCallback(ctx, "onMessageSent"): + $JsonMessageSentEvent.new(messageId) + ### End of not-exported components ################################################################################ diff --git a/src/reliability.nim b/src/reliability.nim index 0ad08e0..1262c7d 100644 --- a/src/reliability.nim +++ b/src/reliability.nim @@ -251,10 +251,9 @@ proc markDependenciesMet*( proc setCallbacks*( rm: ReliabilityManager, - onMessageReady: proc(messageId: MessageID) {.gcsafe.}, - onMessageSent: proc(messageId: MessageID) {.gcsafe.}, - onMissingDependencies: - proc(messageId: MessageID, missingDeps: seq[MessageID]) {.gcsafe.}, + onMessageReady: MessageReadyCallback, + onMessageSent: MessageSentCallback, + onMissingDependencies: MissingDependenciesCallback, onPeriodicSync: PeriodicSyncCallback = nil, ) = ## Sets the callback functions for various events in the ReliabilityManager. diff --git a/src/reliability_utils.nim b/src/reliability_utils.nim index 25abb6d..367e965 100644 --- a/src/reliability_utils.nim +++ b/src/reliability_utils.nim @@ -2,8 +2,21 @@ import std/[times, locks] import ./[rolling_bloom_filter, message] type + MessageReadyCallback* = proc(messageId: MessageID) {.gcsafe.} + + MessageSentCallback* = proc(messageId: MessageID) {.gcsafe.} + + MissingDependenciesCallback* = + proc(messageId: MessageID, missingDeps: seq[MessageID]) {.gcsafe.} + PeriodicSyncCallback* = proc() {.gcsafe, raises: [].} + AppCallbacks* = ref object + messageReadyCb*: MessageReadyCallback + messageSentCb*: MessageSentCallback + missingDependenciesCb*: MissingDependenciesCallback + periodicSyncCb*: PeriodicSyncCallback + ReliabilityConfig* = object bloomFilterCapacity*: int bloomFilterErrorRate*: float From 0999f443666c6e28d399b17d47e43d1630f2abad Mon Sep 17 00:00:00 2001 From: Gabriel mermelstein Date: Wed, 16 Apr 2025 11:22:42 +0300 Subject: [PATCH 20/29] rest of event definitions --- .../json_missing_dependencies_event.nim | 21 +++++++++++++++++++ library/events/json_periodic_sync_event.nim | 13 ++++++++++++ library/libsds.nim | 15 ++++++++++++- 3 files changed, 48 insertions(+), 1 deletion(-) create mode 100644 library/events/json_missing_dependencies_event.nim create mode 100644 library/events/json_periodic_sync_event.nim diff --git a/library/events/json_missing_dependencies_event.nim b/library/events/json_missing_dependencies_event.nim new file mode 100644 index 0000000..2fb4689 --- /dev/null +++ b/library/events/json_missing_dependencies_event.nim @@ -0,0 +1,21 @@ +import std/json +import ./json_base_event, ../../src/[message] + +type JsonMissingDependenciesEvent* = ref object of JsonEvent + messageId*: MessageID + missingDeps: seq[MessageID] + +proc new*( + T: type JsonMissingDependenciesEvent, + messageId: MessageID, + missingDeps: seq[MessageID], +): T = + # Returns a MissingDependencies event as indicated in + # https://rfc.vac.dev/spec/36/#jsonmessageevent-type + + return JsonMissingDependenciesEvent( + eventType: "missing_dependencies", messageId: messageId, missingDeps: missingDeps + ) + +method `$`*(jsonMissingDependencies: JsonMissingDependenciesEvent): string = + $(%*jsonMissingDependencies) diff --git a/library/events/json_periodic_sync_event.nim b/library/events/json_periodic_sync_event.nim new file mode 100644 index 0000000..2cb6540 --- /dev/null +++ b/library/events/json_periodic_sync_event.nim @@ -0,0 +1,13 @@ +import std/json +import ./json_base_event + +type JsonPeriodicSyncEvent* = ref object of JsonEvent + +proc new*(T: type JsonPeriodicSyncEvent): T = + # Returns a PeriodicSync event as indicated in + # https://rfc.vac.dev/spec/36/#jsonmessageevent-type + + return JsonPeriodicSyncEvent(eventType: "periodic_sync") + +method `$`*(jsonPeriodicSync: JsonPeriodicSyncEvent): string = + $(%*jsonPeriodicSync) diff --git a/library/libsds.nim b/library/libsds.nim index 162f5a9..31c1def 100644 --- a/library/libsds.nim +++ b/library/libsds.nim @@ -14,7 +14,10 @@ import ./sds_thread/inter_thread_communication/requests/ [sds_lifecycle_request, sds_message_request, sds_dependencies_request], ../src/[reliability, reliability_utils, message], - ./events/[json_message_ready_event, json_message_sent_event] + ./events/[ + json_message_ready_event, json_message_sent_event, json_missing_dependencies_event, + json_periodic_sync_event, + ] ################################################################################ ### Wrapper around the reliability manager @@ -78,6 +81,16 @@ proc onMessageSent(ctx: ptr SdsContext): MessageSentCallback = callEventCallback(ctx, "onMessageSent"): $JsonMessageSentEvent.new(messageId) +proc onMissingDependencies(ctx: ptr SdsContext): MissingDependenciesCallback = + return proc(messageId: MessageID, missingDeps: seq[MessageID]) {.gcsafe.} = + callEventCallback(ctx, "onMissingDependencies"): + $JsonMissingDependenciesEvent.new(messageId, missingDeps) + +proc onPeriodicSync(ctx: ptr SdsContext): PeriodicSyncCallback = + return proc() {.gcsafe.} = + callEventCallback(ctx, "onPeriodicSync"): + $JsonPeriodicSyncEvent.new() + ### End of not-exported components ################################################################################ From 11cd637004feac081b736e955cb18eada271604a Mon Sep 17 00:00:00 2001 From: Gabriel mermelstein Date: Wed, 16 Apr 2025 11:35:42 +0300 Subject: [PATCH 21/29] setting up callbacks --- library/libsds.nim | 9 ++++++- .../requests/sds_lifecycle_request.nim | 24 +++++++++---------- 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/library/libsds.nim b/library/libsds.nim index 31c1def..cecfdf5 100644 --- a/library/libsds.nim +++ b/library/libsds.nim @@ -148,11 +148,18 @@ proc NewReliabilityManager( ctx.userData = userData + let appCallbacks = AppCallbacks( + messageReadyCb: onMessageReady(ctx), + messageSentCb: onMessageSent(ctx), + missingDependenciesCb: onMissingDependencies(ctx), + periodicSyncCb: onPeriodicSync(ctx), + ) + let retCode = handleRequest( ctx, RequestType.LIFECYCLE, SdsLifecycleRequest.createShared( - SdsLifecycleMsgType.CREATE_RELIABILITY_MANAGER, channelId + SdsLifecycleMsgType.CREATE_RELIABILITY_MANAGER, channelId, appCallbacks ), callback, userData, diff --git a/library/sds_thread/inter_thread_communication/requests/sds_lifecycle_request.nim b/library/sds_thread/inter_thread_communication/requests/sds_lifecycle_request.nim index 1794bd2..a70ab3a 100644 --- a/library/sds_thread/inter_thread_communication/requests/sds_lifecycle_request.nim +++ b/library/sds_thread/inter_thread_communication/requests/sds_lifecycle_request.nim @@ -11,12 +11,17 @@ type SdsLifecycleMsgType* = enum type SdsLifecycleRequest* = object operation: SdsLifecycleMsgType channelId: cstring + appCallbacks: AppCallbacks proc createShared*( - T: type SdsLifecycleRequest, op: SdsLifecycleMsgType, channelId: cstring = "" + T: type SdsLifecycleRequest, + op: SdsLifecycleMsgType, + channelId: cstring = "", + appCallbacks: AppCallbacks = nil, ): ptr type T = var ret = createShared(T) ret[].operation = op + ret[].appCallbacks = appCallbacks ret[].channelId = channelId.alloc() return ret @@ -25,7 +30,7 @@ proc destroyShared(self: ptr SdsLifecycleRequest) = deallocShared(self) proc createReliabilityManager( - channelIdCStr: cstring + channelIdCStr: cstring, appCallbacks: AppCallbacks = nil ): Future[Result[ReliabilityManager, string]] {.async.} = let channelId = $channelIdCStr if channelId.len == 0: @@ -36,15 +41,10 @@ proc createReliabilityManager( error "Failed creating reliability manager", error = error return err("Failed creating reliability manager: " & $error) - # TODO: instead of this, create events - #[ rm.onMessageReady = proc(msgId: MessageID) = - nimMessageReadyCallback(rm, msgId) - rm.onMessageSent = proc(msgId: MessageID) = - nimMessageSentCallback(rm, msgId) - rm.onMissingDependencies = proc(msgId: MessageID, deps: seq[MessageID]) = - nimMissingDependenciesCallback(rm, msgId, deps) - rm.onPeriodicSync = proc() = - nimPeriodicSyncCallback(rm) ]# + rm.setCallbacks( + appCallbacks.messageReadyCb, appCallbacks.messageSentCb, + appCallbacks.missingDependenciesCb, appCallbacks.periodicSyncCb, + ) return ok(rm) @@ -56,7 +56,7 @@ proc process*( case self.operation of CREATE_RELIABILITY_MANAGER: - rm[] = (await createReliabilityManager(self.channelId)).valueOr: + rm[] = (await createReliabilityManager(self.channelId, self.appCallbacks)).valueOr: error "CREATE_RELIABILITY_MANAGER failed", error = error return err("error processing CREATE_RELIABILITY_MANAGER request: " & $error) of RESET_RELIABILITY_MANAGER: From e7a6435407b1a7bdd2063d32e64a2328254e86da Mon Sep 17 00:00:00 2001 From: Gabriel mermelstein Date: Wed, 16 Apr 2025 11:47:17 +0300 Subject: [PATCH 22/29] adding startPeriodicTasks request --- library/libsds.h | 2 ++ library/libsds.nim | 12 ++++++++++++ .../requests/sds_lifecycle_request.nim | 3 +++ 3 files changed, 17 insertions(+) diff --git a/library/libsds.h b/library/libsds.h index 63a16c8..044151a 100644 --- a/library/libsds.h +++ b/library/libsds.h @@ -51,6 +51,8 @@ int MarkDependenciesMet(void* ctx, SdsCallBack callback, void* userData); +int StartPeriodicTasks(void* ctx, SdsCallBack callback, void* userData); + #ifdef __cplusplus diff --git a/library/libsds.nim b/library/libsds.nim index cecfdf5..895a960 100644 --- a/library/libsds.nim +++ b/library/libsds.nim @@ -301,5 +301,17 @@ proc MarkDependenciesMet( userData, ) +proc StartPeriodicTasks( + ctx: ptr SdsContext, callback: SdsCallBack, userData: pointer +): cint {.dynlib, exportc.} = + checkLibsdsParams(ctx, callback, userData) + handleRequest( + ctx, + RequestType.LIFECYCLE, + SdsLifecycleRequest.createShared(SdsLifecycleMsgType.START_PERIODIC_TASKS), + callback, + userData, + ) + ### End of exported procs ################################################################################ diff --git a/library/sds_thread/inter_thread_communication/requests/sds_lifecycle_request.nim b/library/sds_thread/inter_thread_communication/requests/sds_lifecycle_request.nim index a70ab3a..ce90d2f 100644 --- a/library/sds_thread/inter_thread_communication/requests/sds_lifecycle_request.nim +++ b/library/sds_thread/inter_thread_communication/requests/sds_lifecycle_request.nim @@ -7,6 +7,7 @@ import ../../../../src/[reliability_utils, reliability, message] type SdsLifecycleMsgType* = enum CREATE_RELIABILITY_MANAGER RESET_RELIABILITY_MANAGER + START_PERIODIC_TASKS type SdsLifecycleRequest* = object operation: SdsLifecycleMsgType @@ -62,5 +63,7 @@ proc process*( of RESET_RELIABILITY_MANAGER: resetReliabilityManager(rm[]).isOkOr: return err("error processing RESET_RELIABILITY_MANAGER request: " & $error) + of START_PERIODIC_TASKS: + rm[].startPeriodicTasks() return ok("") From 415d0d8a42aec319097aa47d38304c8abb957b34 Mon Sep 17 00:00:00 2001 From: Gabriel mermelstein Date: Wed, 16 Apr 2025 12:57:07 +0300 Subject: [PATCH 23/29] setting up nimbus-build-system --- .gitignore | 7 +++++ .gitmodules | 55 +++++++++++++++++++++++++++++++++++ Makefile | 32 ++++++++++++++++++++ add_submodule.sh | 20 +++++++++++++ vendor/nim-chronicles | 1 + vendor/nim-chronos | 1 + vendor/nim-confutils | 1 + vendor/nim-faststreams | 1 + vendor/nim-json-serialization | 1 + vendor/nim-libp2p | 1 + vendor/nim-results | 1 + vendor/nim-serialization | 1 + vendor/nim-stew | 1 + vendor/nim-taskpools | 1 + vendor/nimbus-build-system | 1 + 15 files changed, 125 insertions(+) create mode 100644 .gitmodules create mode 100755 add_submodule.sh create mode 160000 vendor/nim-chronicles create mode 160000 vendor/nim-chronos create mode 160000 vendor/nim-confutils create mode 160000 vendor/nim-faststreams create mode 160000 vendor/nim-json-serialization create mode 160000 vendor/nim-libp2p create mode 160000 vendor/nim-results create mode 160000 vendor/nim-serialization create mode 160000 vendor/nim-stew create mode 160000 vendor/nim-taskpools create mode 160000 vendor/nimbus-build-system diff --git a/.gitignore b/.gitignore index 6f5bb9f..898171b 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,10 @@ for_reference do_not_commit build/* sds.nims +/.update.timestamp + +# Nimbus Build System +nimbus-build-system.paths + +# Nimble packages +/vendor/.nimble diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..fe38e76 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,55 @@ +[submodule "vendor/nimbus-build-system"] + path = vendor/nimbus-build-system + url = https://github.com/status-im/nimbus-build-system.git + ignore = untracked + branch = master +[submodule "vendor/nim-chronos"] + path = vendor/nim-chronos + url = https://github.com/status-im/nim-chronos.git + ignore = untracked + branch = master +[submodule "vendor/nim-results"] + path = vendor/nim-results + url = https://github.com/arnetheduck/nim-results.git + ignore = untracked + branch = master +[submodule "vendor/nim-stew"] + path = vendor/nim-stew + url = https://github.com/status-im/nim-stew.git + ignore = untracked + branch = master +[submodule "vendor/nim-chronicles"] + path = vendor/nim-chronicles + url = https://github.com/status-im/nim-chronicles.git + ignore = untracked + branch = master +[submodule "vendor/nim-faststreams"] + path = vendor/nim-faststreams + url = https://github.com/status-im/nim-faststreams.git + ignore = untracked + branch = master +[submodule "vendor/nim-json-serialization"] + path = vendor/nim-json-serialization + url = https://github.com/status-im/nim-json-serialization.git + ignore = untracked + branch = master +[submodule "vendor/nim-serialization"] + path = vendor/nim-serialization + url = https://github.com/status-im/nim-serialization.git + ignore = untracked + branch = master +[submodule "vendor/nim-taskpools"] + path = vendor/nim-taskpools + url = https://github.com/status-im/nim-taskpools.git + ignore = untracked + branch = master +[submodule "vendor/nim-confutils"] + path = vendor/nim-confutils + url = https://github.com/status-im/nim-confutils.git + ignore = untracked + branch = master +[submodule "vendor/nim-libp2p"] + path = vendor/nim-libp2p + url = https://github.com/vacp2p/nim-libp2p.git + ignore = untracked + branch = master diff --git a/Makefile b/Makefile index 1c478cd..31788b2 100644 --- a/Makefile +++ b/Makefile @@ -1,10 +1,41 @@ .PHONY: libsds +export BUILD_SYSTEM_DIR := vendor/nimbus-build-system +# we don't want an error here, so we can handle things later, in the ".DEFAULT" target +-include $(BUILD_SYSTEM_DIR)/makefiles/variables.mk + +ifeq ($(NIM_PARAMS),) +# "variables.mk" was not included, so we update the submodules. +GIT_SUBMODULE_UPDATE := git submodule update --init --recursive +.DEFAULT: + +@ echo -e "Git submodules not found. Running '$(GIT_SUBMODULE_UPDATE)'.\n"; \ + $(GIT_SUBMODULE_UPDATE); \ + echo +# Now that the included *.mk files appeared, and are newer than this file, Make will restart itself: +# https://www.gnu.org/software/make/manual/make.html#Remaking-Makefiles +# +# After restarting, it will execute its original goal, so we don't have to start a child Make here +# with "$(MAKE) $(MAKECMDGOALS)". Isn't hidden control flow great? + +else # "variables.mk" was included. Business as usual until the end of this file. + +# default target, because it's the first one that doesn't start with '.' +all: | libsds + sds.nims: ln -s sds.nimble $@ +update: | update-common + rm -rf sds.nims && \ + $(MAKE) sds.nims $(HANDLE_OUTPUT) + +clean: + rm -rf build + deps: | sds.nims +# must be included after the default target +-include $(BUILD_SYSTEM_DIR)/makefiles/targets.mk STATIC ?= 0 @@ -16,4 +47,5 @@ ifeq ($(STATIC), 1) else echo -e $(BUILD_MSG) "build/$@.so" && \ $(ENV_SCRIPT) nim libsdsDynamic $(NIM_PARAMS) sds.nims +endif endif \ No newline at end of file diff --git a/add_submodule.sh b/add_submodule.sh new file mode 100755 index 0000000..133cb0a --- /dev/null +++ b/add_submodule.sh @@ -0,0 +1,20 @@ +#!/usr/bin/env bash + +# Copyright (c) 2018-2020 Status Research & Development GmbH. Licensed under +# either of: +# - Apache License, version 2.0 +# - MIT license +# at your option. This file may not be copied, modified, or distributed except +# according to those terms. + +[ -z "$1" -o `echo "$1" | tr '/' '\n' | wc -l` != 2 ] && \ + { echo "Usage: `basename $0` some/repo [destdir] # 'destdir' defaults to 'vendor/repo'"; exit 1; } +REPO="$1" + +DEST="vendor/${REPO#*/}" +[ -n "$2" ] && DEST="$2" + +git submodule add --force https://github.com/${REPO}.git "$DEST" +git config -f .gitmodules submodule.${DEST}.ignore untracked +git config -f .gitmodules submodule.${DEST}.branch master + diff --git a/vendor/nim-chronicles b/vendor/nim-chronicles new file mode 160000 index 0000000..a8fb38a --- /dev/null +++ b/vendor/nim-chronicles @@ -0,0 +1 @@ +Subproject commit a8fb38a10bcb548df78e9a70bd77b26bb50abd12 diff --git a/vendor/nim-chronos b/vendor/nim-chronos new file mode 160000 index 0000000..b55e281 --- /dev/null +++ b/vendor/nim-chronos @@ -0,0 +1 @@ +Subproject commit b55e2816eb45f698ddaca8d8473e401502562db2 diff --git a/vendor/nim-confutils b/vendor/nim-confutils new file mode 160000 index 0000000..e214b39 --- /dev/null +++ b/vendor/nim-confutils @@ -0,0 +1 @@ +Subproject commit e214b3992a31acece6a9aada7d0a1ad37c928f3b diff --git a/vendor/nim-faststreams b/vendor/nim-faststreams new file mode 160000 index 0000000..2b08c77 --- /dev/null +++ b/vendor/nim-faststreams @@ -0,0 +1 @@ +Subproject commit 2b08c774afaafd600cf4c6f994cf78b8aa090c0c diff --git a/vendor/nim-json-serialization b/vendor/nim-json-serialization new file mode 160000 index 0000000..2b1c5eb --- /dev/null +++ b/vendor/nim-json-serialization @@ -0,0 +1 @@ +Subproject commit 2b1c5eb11df3647a2cee107cd4cce3593cbb8bcf diff --git a/vendor/nim-libp2p b/vendor/nim-libp2p new file mode 160000 index 0000000..ac25da6 --- /dev/null +++ b/vendor/nim-libp2p @@ -0,0 +1 @@ +Subproject commit ac25da6cea158768bbc060b7be2fbe004206f3bb diff --git a/vendor/nim-results b/vendor/nim-results new file mode 160000 index 0000000..df8113d --- /dev/null +++ b/vendor/nim-results @@ -0,0 +1 @@ +Subproject commit df8113dda4c2d74d460a8fa98252b0b771bf1f27 diff --git a/vendor/nim-serialization b/vendor/nim-serialization new file mode 160000 index 0000000..548d0ad --- /dev/null +++ b/vendor/nim-serialization @@ -0,0 +1 @@ +Subproject commit 548d0adc9797a10b2db7f788b804330306293088 diff --git a/vendor/nim-stew b/vendor/nim-stew new file mode 160000 index 0000000..d7a6868 --- /dev/null +++ b/vendor/nim-stew @@ -0,0 +1 @@ +Subproject commit d7a6868ba84165e7fdde427af9a1fc3f5f5cc151 diff --git a/vendor/nim-taskpools b/vendor/nim-taskpools new file mode 160000 index 0000000..7b74a71 --- /dev/null +++ b/vendor/nim-taskpools @@ -0,0 +1 @@ +Subproject commit 7b74a716a40249720fd7da428113147942b9642d diff --git a/vendor/nimbus-build-system b/vendor/nimbus-build-system new file mode 160000 index 0000000..5f10509 --- /dev/null +++ b/vendor/nimbus-build-system @@ -0,0 +1 @@ +Subproject commit 5f10509cf880dc035e517ca7bac3163cd5206ba8 From d9c3bccc31d08db8c676c468ac3b5cf05ebde806 Mon Sep 17 00:00:00 2001 From: Gabriel mermelstein Date: Wed, 16 Apr 2025 17:54:48 +0300 Subject: [PATCH 24/29] removing old go wrappers --- sds_wrapper.go | 281 -------------------------------------------- sds_wrapper_test.go | 259 ---------------------------------------- 2 files changed, 540 deletions(-) delete mode 100644 sds_wrapper.go delete mode 100644 sds_wrapper_test.go diff --git a/sds_wrapper.go b/sds_wrapper.go deleted file mode 100644 index 1150a46..0000000 --- a/sds_wrapper.go +++ /dev/null @@ -1,281 +0,0 @@ -package main - -/* -#cgo CFLAGS: -I${SRCDIR}/library -#cgo LDFLAGS: -L${SRCDIR}/build -llibsds -#cgo LDFLAGS: -Wl,-rpath,${SRCDIR}/build - -#include // For C.free -#include "library/libsds.h" // Update include path - -// Forward declaration for the single Go callback relay function -extern void globalCallbackRelay(void* handle, CEventType eventType, void* data1, void* data2, size_t data3); - -// Helper function to call the C memory freeing functions -static void callFreeCResultError(CResult res) { FreeCResultError(res); } -static void callFreeCWrapResult(CWrapResult res) { FreeCWrapResult(res); } -static void callFreeCUnwrapResult(CUnwrapResult res) { FreeCUnwrapResult(res); } - -*/ -import "C" -import ( - "errors" - "fmt" - "sync" - "unsafe" -) - -// --- Go Types --- - -// ReliabilityManagerHandle represents the opaque handle to the Nim object -type ReliabilityManagerHandle unsafe.Pointer - -// MessageID is a type alias for string for clarity -type MessageID string - -// Callbacks holds the Go functions to be called by the Nim library -type Callbacks struct { - OnMessageReady func(messageId MessageID) - OnMessageSent func(messageId MessageID) - OnMissingDependencies func(messageId MessageID, missingDeps []MessageID) - OnPeriodicSync func() -} - -// Global map to store callbacks associated with handles -var ( - callbackRegistry = make(map[ReliabilityManagerHandle]*Callbacks) - registryMutex sync.RWMutex -) - -// --- Go Wrapper Functions --- - -// NewReliabilityManager creates a new instance of the Nim ReliabilityManager -func NewReliabilityManager(channelId string) (ReliabilityManagerHandle, error) { - cChannelId := C.CString(channelId) - defer C.free(unsafe.Pointer(cChannelId)) - - handle := C.NewReliabilityManager(cChannelId) - if handle == nil { - // Note: Nim side currently just prints to stdout on creation failure - return nil, errors.New("failed to create ReliabilityManager (check Nim logs/stdout)") - } - return ReliabilityManagerHandle(handle), nil -} - -// CleanupReliabilityManager frees the resources associated with the handle -func CleanupReliabilityManager(handle ReliabilityManagerHandle) { - if handle == nil { - return - } - registryMutex.Lock() - delete(callbackRegistry, handle) - registryMutex.Unlock() - C.CleanupReliabilityManager(unsafe.Pointer(handle)) -} - -// ResetReliabilityManager resets the state of the manager -func ResetReliabilityManager(handle ReliabilityManagerHandle) error { - if handle == nil { - return errors.New("handle is nil") - } - cResult := C.ResetReliabilityManager(unsafe.Pointer(handle)) - if !cResult.is_ok { - errMsg := C.GoString(cResult.error_message) - C.callFreeCResultError(cResult) // Free the error message - return errors.New(errMsg) - } - return nil -} - -// WrapOutgoingMessage wraps a message with reliability metadata -func WrapOutgoingMessage(handle ReliabilityManagerHandle, message []byte, messageId MessageID) ([]byte, error) { - if handle == nil { - return nil, errors.New("handle is nil") - } - cMessageId := C.CString(string(messageId)) - defer C.free(unsafe.Pointer(cMessageId)) - - var cMessagePtr unsafe.Pointer - if len(message) > 0 { - cMessagePtr = C.CBytes(message) // C.CBytes allocates memory that needs to be freed - defer C.free(cMessagePtr) - } else { - cMessagePtr = nil - } - cMessageLen := C.size_t(len(message)) - - cWrapResult := C.WrapOutgoingMessage(unsafe.Pointer(handle), cMessagePtr, cMessageLen, cMessageId) - - if !cWrapResult.base_result.is_ok { - errMsg := C.GoString(cWrapResult.base_result.error_message) - C.callFreeCWrapResult(cWrapResult) // Free error and potentially allocated message - return nil, errors.New(errMsg) - } - - // Copy the wrapped message from C memory to Go slice - // Explicitly cast the message pointer to unsafe.Pointer - wrappedMessage := C.GoBytes(unsafe.Pointer(cWrapResult.message), C.int(cWrapResult.message_len)) - C.callFreeCWrapResult(cWrapResult) // Free the C-allocated message buffer - - return wrappedMessage, nil -} - -// UnwrapReceivedMessage unwraps a received message -func UnwrapReceivedMessage(handle ReliabilityManagerHandle, message []byte) ([]byte, []MessageID, error) { - if handle == nil { - return nil, nil, errors.New("handle is nil") - } - - var cMessagePtr unsafe.Pointer - if len(message) > 0 { - cMessagePtr = C.CBytes(message) - defer C.free(cMessagePtr) - } else { - cMessagePtr = nil - } - cMessageLen := C.size_t(len(message)) - - cUnwrapResult := C.UnwrapReceivedMessage(unsafe.Pointer(handle), cMessagePtr, cMessageLen) - - if !cUnwrapResult.base_result.is_ok { - errMsg := C.GoString(cUnwrapResult.base_result.error_message) - C.callFreeCUnwrapResult(cUnwrapResult) // Free error and potentially allocated fields - return nil, nil, errors.New(errMsg) - } - - // Copy unwrapped message content - unwrappedContent := C.GoBytes(unsafe.Pointer(cUnwrapResult.message), C.int(cUnwrapResult.message_len)) - - // Copy missing dependencies - missingDeps := make([]MessageID, cUnwrapResult.missing_deps_count) - if cUnwrapResult.missing_deps_count > 0 { - // Convert C array of C strings to Go slice of strings - cDepsArray := (*[1 << 30]*C.char)(unsafe.Pointer(cUnwrapResult.missing_deps))[:cUnwrapResult.missing_deps_count:cUnwrapResult.missing_deps_count] - for i, s := range cDepsArray { - missingDeps[i] = MessageID(C.GoString(s)) - } - } - - C.callFreeCUnwrapResult(cUnwrapResult) // Free C-allocated message, deps array, and strings - - return unwrappedContent, missingDeps, nil -} - -// MarkDependenciesMet informs the library that dependencies are met -func MarkDependenciesMet(handle ReliabilityManagerHandle, messageIDs []MessageID) error { - if handle == nil { - return errors.New("handle is nil") - } - if len(messageIDs) == 0 { - return nil // Nothing to do - } - - // Convert Go string slice to C array of C strings (char**) - cMessageIDs := make([]*C.char, len(messageIDs)) - for i, id := range messageIDs { - cMessageIDs[i] = C.CString(string(id)) - defer C.free(unsafe.Pointer(cMessageIDs[i])) // Ensure each CString is freed - } - - // Create a pointer (**C.char) to the first element of the slice - var cMessageIDsPtr **C.char - if len(cMessageIDs) > 0 { - cMessageIDsPtr = &cMessageIDs[0] - } else { - cMessageIDsPtr = nil // Handle empty slice case - } - - // Pass the pointer variable (cMessageIDsPtr) directly, which is of type **C.char - cResult := C.MarkDependenciesMet(unsafe.Pointer(handle), cMessageIDsPtr, C.size_t(len(messageIDs))) - - if !cResult.is_ok { - errMsg := C.GoString(cResult.error_message) - C.callFreeCResultError(cResult) - return errors.New(errMsg) - } - return nil -} - -// RegisterCallback sets the single Go callback relay function -func RegisterCallback(handle ReliabilityManagerHandle, callbacks Callbacks) error { - if handle == nil { - return errors.New("handle is nil") - } - - // Store the Go callbacks associated with this handle - registryMutex.Lock() - callbackRegistry[handle] = &callbacks - registryMutex.Unlock() - - // Register the single global Go relay function with the Nim library - // Nim will call globalCallbackRelay, passing the handle as the first argument. - C.RegisterCallback( - unsafe.Pointer(handle), - (C.CEventCallback)(C.globalCallbackRelay), // Pass the Go relay function pointer - nil, // user_data is not used here, handle is passed directly by Nim wrapper - ) - return nil -} - -// StartPeriodicTasks starts the background tasks in the Nim library -func StartPeriodicTasks(handle ReliabilityManagerHandle) error { - if handle == nil { - return errors.New("handle is nil") - } - C.StartPeriodicTasks(unsafe.Pointer(handle)) - // Assuming StartPeriodicTasks doesn't return an error status in C API - return nil -} - -// globalCallbackRelay is called by Nim for all events. -// It uses the handle to find the correct Go Callbacks struct and dispatch the call. -// -//export globalCallbackRelay -func globalCallbackRelay(handle unsafe.Pointer, eventType C.CEventType, data1 unsafe.Pointer, data2 unsafe.Pointer, data3 C.size_t) { - goHandle := ReliabilityManagerHandle(handle) - - registryMutex.RLock() - callbacks, ok := callbackRegistry[goHandle] - registryMutex.RUnlock() - - if !ok || callbacks == nil { - fmt.Printf("Go: globalCallbackRelay: No callbacks registered for handle %v\n", goHandle) // Uncommented - return - } - - // Use a goroutine to avoid blocking the Nim thread - go func() { - switch eventType { - case C.EVENT_MESSAGE_READY: - if callbacks.OnMessageReady != nil { - msgIdStr := C.GoString((*C.char)(data1)) - callbacks.OnMessageReady(MessageID(msgIdStr)) - } - case C.EVENT_MESSAGE_SENT: - if callbacks.OnMessageSent != nil { - msgIdStr := C.GoString((*C.char)(data1)) - callbacks.OnMessageSent(MessageID(msgIdStr)) - } - case C.EVENT_MISSING_DEPENDENCIES: - if callbacks.OnMissingDependencies != nil { - msgIdStr := C.GoString((*C.char)(data1)) - depsCount := int(data3) - deps := make([]MessageID, depsCount) - if depsCount > 0 { - // Convert C array of C strings (**char) to Go slice - cDepsArray := (*[1 << 30]*C.char)(data2)[:depsCount:depsCount] - for i, s := range cDepsArray { - deps[i] = MessageID(C.GoString(s)) - } - } - callbacks.OnMissingDependencies(MessageID(msgIdStr), deps) - } - case C.EVENT_PERIODIC_SYNC: - if callbacks.OnPeriodicSync != nil { - callbacks.OnPeriodicSync() - } - default: - fmt.Printf("Go: globalCallbackRelay: Received unknown event type %d for handle %v\n", eventType, goHandle) - } - }() -} diff --git a/sds_wrapper_test.go b/sds_wrapper_test.go deleted file mode 100644 index ee1185a..0000000 --- a/sds_wrapper_test.go +++ /dev/null @@ -1,259 +0,0 @@ -package main -import ( - "fmt" - "sync" - "testing" - "time" -) - -// Test basic creation, cleanup, and reset -func TestLifecycle(t *testing.T) { - channelID := "test-lifecycle" - handle, err := NewReliabilityManager(channelID) - if err != nil { - t.Fatalf("NewReliabilityManager failed: %v", err) - } - if handle == nil { - t.Fatal("NewReliabilityManager returned a nil handle") - } - defer CleanupReliabilityManager(handle) // Ensure cleanup even on test failure - - err = ResetReliabilityManager(handle) - if err != nil { - t.Errorf("ResetReliabilityManager failed: %v", err) - } -} - -// Test wrapping and unwrapping a simple message -func TestWrapUnwrap(t *testing.T) { - channelID := "test-wrap-unwrap" - handle, err := NewReliabilityManager(channelID) - if err != nil { - t.Fatalf("NewReliabilityManager failed: %v", err) - } - defer CleanupReliabilityManager(handle) - - originalPayload := []byte("hello reliability") - messageID := MessageID("msg-wrap-1") - - wrappedMsg, err := WrapOutgoingMessage(handle, originalPayload, messageID) - if err != nil { - t.Fatalf("WrapOutgoingMessage failed: %v", err) - } - if len(wrappedMsg) == 0 { - t.Fatal("WrapOutgoingMessage returned empty bytes") - } - - // Simulate receiving the wrapped message - unwrappedPayload, missingDeps, err := UnwrapReceivedMessage(handle, wrappedMsg) - if err != nil { - t.Fatalf("UnwrapReceivedMessage failed: %v", err) - } - - if string(unwrappedPayload) != string(originalPayload) { - t.Errorf("Unwrapped payload mismatch: got %q, want %q", unwrappedPayload, originalPayload) - } - if len(missingDeps) != 0 { - t.Errorf("Expected 0 missing dependencies, got %d: %v", len(missingDeps), missingDeps) - } -} - -// Test dependency handling -func TestDependencies(t *testing.T) { - channelID := "test-deps" - handle, err := NewReliabilityManager(channelID) - if err != nil { - t.Fatalf("NewReliabilityManager failed: %v", err) - } - defer CleanupReliabilityManager(handle) - - // 1. Send message 1 (will become a dependency) - payload1 := []byte("message one") - msgID1 := MessageID("msg-dep-1") - wrappedMsg1, err := WrapOutgoingMessage(handle, payload1, msgID1) - if err != nil { - t.Fatalf("WrapOutgoingMessage (1) failed: %v", err) - } - // Simulate receiving msg1 to add it to history (implicitly acknowledges it) - _, _, err = UnwrapReceivedMessage(handle, wrappedMsg1) - if err != nil { - t.Fatalf("UnwrapReceivedMessage (1) failed: %v", err) - } - - // 2. Send message 2 (depends on message 1 implicitly via causal history) - payload2 := []byte("message two") - msgID2 := MessageID("msg-dep-2") - wrappedMsg2, err := WrapOutgoingMessage(handle, payload2, msgID2) - if err != nil { - t.Fatalf("WrapOutgoingMessage (2) failed: %v", err) - } - - // 3. Create a new manager to simulate a different peer receiving msg2 without msg1 - handle2, err := NewReliabilityManager(channelID) // Same channel ID - if err != nil { - t.Fatalf("NewReliabilityManager (2) failed: %v", err) - } - defer CleanupReliabilityManager(handle2) - - // 4. Unwrap message 2 on the second manager - should report msg1 as missing - _, missingDeps, err := UnwrapReceivedMessage(handle2, wrappedMsg2) - if err != nil { - t.Fatalf("UnwrapReceivedMessage (2) on handle2 failed: %v", err) - } - - if len(missingDeps) == 0 { - t.Fatalf("Expected missing dependencies, got none") - } - foundDep1 := false - for _, dep := range missingDeps { - if dep == msgID1 { - foundDep1 = true - break - } - } - if !foundDep1 { - t.Errorf("Expected missing dependency %q, got %v", msgID1, missingDeps) - } - - // 5. Mark the dependency as met - err = MarkDependenciesMet(handle2, []MessageID{msgID1}) - if err != nil { - t.Fatalf("MarkDependenciesMet failed: %v", err) - } -} - -// Test callbacks -func TestCallbacks(t *testing.T) { - channelID := "test-callbacks" - handle, err := NewReliabilityManager(channelID) - if err != nil { - t.Fatalf("NewReliabilityManager failed: %v", err) - } - defer CleanupReliabilityManager(handle) - - var wg sync.WaitGroup - receivedReady := make(map[MessageID]bool) - receivedSent := make(map[MessageID]bool) - receivedMissing := make(map[MessageID][]MessageID) - syncRequested := false - var cbMutex sync.Mutex // Protect access to callback tracking maps/vars - - callbacks := Callbacks{ - OnMessageReady: func(messageId MessageID) { - fmt.Printf("Test: OnMessageReady received: %s\n", messageId) - cbMutex.Lock() - receivedReady[messageId] = true - cbMutex.Unlock() - wg.Done() - }, - OnMessageSent: func(messageId MessageID) { - fmt.Printf("Test: OnMessageSent received: %s\n", messageId) - cbMutex.Lock() - receivedSent[messageId] = true - cbMutex.Unlock() - wg.Done() - }, - OnMissingDependencies: func(messageId MessageID, missingDeps []MessageID) { - fmt.Printf("Test: OnMissingDependencies received for %s: %v\n", messageId, missingDeps) - cbMutex.Lock() - receivedMissing[messageId] = missingDeps - cbMutex.Unlock() - wg.Done() - }, - OnPeriodicSync: func() { - fmt.Println("Test: OnPeriodicSync received") - cbMutex.Lock() - syncRequested = true - cbMutex.Unlock() - // Don't wg.Done() here, it might be called multiple times - }, - } - - err = RegisterCallback(handle, callbacks) - if err != nil { - t.Fatalf("RegisterCallback failed: %v", err) - } - - // Start tasks AFTER registering callbacks - err = StartPeriodicTasks(handle) - if err != nil { - t.Fatalf("StartPeriodicTasks failed: %v", err) - } - - // --- Test Scenario --- - - // 1. Send msg1 - wg.Add(1) // Expect OnMessageSent for msg1 eventually - payload1 := []byte("callback test 1") - msgID1 := MessageID("cb-msg-1") - wrappedMsg1, err := WrapOutgoingMessage(handle, payload1, msgID1) - if err != nil { - t.Fatalf("WrapOutgoingMessage (1) failed: %v", err) - } - - // 2. Receive msg1 (triggers OnMessageReady for msg1, OnMessageSent for msg1 via causal history) - wg.Add(1) // Expect OnMessageReady for msg1 - _, _, err = UnwrapReceivedMessage(handle, wrappedMsg1) - if err != nil { - t.Fatalf("UnwrapReceivedMessage (1) failed: %v", err) - } - - // 3. Send msg2 (depends on msg1) - wg.Add(1) // Expect OnMessageSent for msg2 eventually - payload2 := []byte("callback test 2") - msgID2 := MessageID("cb-msg-2") - wrappedMsg2, err := WrapOutgoingMessage(handle, payload2, msgID2) - if err != nil { - t.Fatalf("WrapOutgoingMessage (2) failed: %v", err) - } - - // 4. Receive msg2 (triggers OnMessageReady for msg2, OnMessageSent for msg2) - wg.Add(1) // Expect OnMessageReady for msg2 - _, _, err = UnwrapReceivedMessage(handle, wrappedMsg2) - if err != nil { - t.Fatalf("UnwrapReceivedMessage (2) failed: %v", err) - } - - // --- Verification --- - // Wait for expected callbacks with a timeout - waitTimeout(&wg, 5*time.Second, t) - - cbMutex.Lock() - defer cbMutex.Unlock() - - if !receivedReady[msgID1] { - t.Errorf("OnMessageReady not called for %s", msgID1) - } - if !receivedReady[msgID2] { - t.Errorf("OnMessageReady not called for %s", msgID2) - } - if !receivedSent[msgID1] { - t.Errorf("OnMessageSent not called for %s", msgID1) - } - if !receivedSent[msgID2] { - t.Errorf("OnMessageSent not called for %s", msgID2) - } - // We didn't explicitly test missing deps in this path - if len(receivedMissing) > 0 { - t.Errorf("Unexpected OnMissingDependencies calls: %v", receivedMissing) - } - // Periodic sync is harder to guarantee in a short test, just check if it was ever true - if !syncRequested { - t.Logf("Warning: OnPeriodicSync might not have been called within the test timeout") - } -} - -// Helper function to wait for WaitGroup with a timeout -func waitTimeout(wg *sync.WaitGroup, timeout time.Duration, t *testing.T) { - c := make(chan struct{}) - go func() { - defer close(c) - wg.Wait() - }() - select { - case <-c: - // Completed normally - case <-time.After(timeout): - t.Fatalf("Timed out waiting for callbacks") - } -} From 9fceb619a2a4d670adc706774f61f48a9ae62a67 Mon Sep 17 00:00:00 2001 From: Gabriel mermelstein Date: Wed, 16 Apr 2025 18:20:16 +0300 Subject: [PATCH 25/29] remove comments --- library/events/json_message_ready_event.nim | 3 --- library/events/json_message_sent_event.nim | 3 --- library/events/json_missing_dependencies_event.nim | 3 --- library/events/json_periodic_sync_event.nim | 3 --- 4 files changed, 12 deletions(-) diff --git a/library/events/json_message_ready_event.nim b/library/events/json_message_ready_event.nim index 4a4b18f..dd3864d 100644 --- a/library/events/json_message_ready_event.nim +++ b/library/events/json_message_ready_event.nim @@ -5,9 +5,6 @@ type JsonMessageReadyEvent* = ref object of JsonEvent messageId*: MessageID proc new*(T: type JsonMessageReadyEvent, messageId: MessageID): T = - # Returns a MessageReady event as indicated in - # https://rfc.vac.dev/spec/36/#jsonmessageevent-type - return JsonMessageReadyEvent(eventType: "message_ready", messageId: messageId) method `$`*(jsonMessageReady: JsonMessageReadyEvent): string = diff --git a/library/events/json_message_sent_event.nim b/library/events/json_message_sent_event.nim index 714a933..3c60e36 100644 --- a/library/events/json_message_sent_event.nim +++ b/library/events/json_message_sent_event.nim @@ -5,9 +5,6 @@ type JsonMessageSentEvent* = ref object of JsonEvent messageId*: MessageID proc new*(T: type JsonMessageSentEvent, messageId: MessageID): T = - # Returns a MessageSent event as indicated in - # https://rfc.vac.dev/spec/36/#jsonmessageevent-type - return JsonMessageSentEvent(eventType: "message_sent", messageId: messageId) method `$`*(jsonMessageSent: JsonMessageSentEvent): string = diff --git a/library/events/json_missing_dependencies_event.nim b/library/events/json_missing_dependencies_event.nim index 2fb4689..0468738 100644 --- a/library/events/json_missing_dependencies_event.nim +++ b/library/events/json_missing_dependencies_event.nim @@ -10,9 +10,6 @@ proc new*( messageId: MessageID, missingDeps: seq[MessageID], ): T = - # Returns a MissingDependencies event as indicated in - # https://rfc.vac.dev/spec/36/#jsonmessageevent-type - return JsonMissingDependenciesEvent( eventType: "missing_dependencies", messageId: messageId, missingDeps: missingDeps ) diff --git a/library/events/json_periodic_sync_event.nim b/library/events/json_periodic_sync_event.nim index 2cb6540..03e875a 100644 --- a/library/events/json_periodic_sync_event.nim +++ b/library/events/json_periodic_sync_event.nim @@ -4,9 +4,6 @@ import ./json_base_event type JsonPeriodicSyncEvent* = ref object of JsonEvent proc new*(T: type JsonPeriodicSyncEvent): T = - # Returns a PeriodicSync event as indicated in - # https://rfc.vac.dev/spec/36/#jsonmessageevent-type - return JsonPeriodicSyncEvent(eventType: "periodic_sync") method `$`*(jsonPeriodicSync: JsonPeriodicSyncEvent): string = From f70532403a650c99b18469243d6f1cceac888890 Mon Sep 17 00:00:00 2001 From: Gabriel mermelstein Date: Thu, 17 Apr 2025 10:59:51 +0300 Subject: [PATCH 26/29] removing add_submodule script --- add_submodule.sh | 20 -------------------- 1 file changed, 20 deletions(-) delete mode 100755 add_submodule.sh diff --git a/add_submodule.sh b/add_submodule.sh deleted file mode 100755 index 133cb0a..0000000 --- a/add_submodule.sh +++ /dev/null @@ -1,20 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2018-2020 Status Research & Development GmbH. Licensed under -# either of: -# - Apache License, version 2.0 -# - MIT license -# at your option. This file may not be copied, modified, or distributed except -# according to those terms. - -[ -z "$1" -o `echo "$1" | tr '/' '\n' | wc -l` != 2 ] && \ - { echo "Usage: `basename $0` some/repo [destdir] # 'destdir' defaults to 'vendor/repo'"; exit 1; } -REPO="$1" - -DEST="vendor/${REPO#*/}" -[ -n "$2" ] && DEST="$2" - -git submodule add --force https://github.com/${REPO}.git "$DEST" -git config -f .gitmodules submodule.${DEST}.ignore untracked -git config -f .gitmodules submodule.${DEST}.branch master - From fe41628527cdf1ae3c50724f2300eee541fa4cbe Mon Sep 17 00:00:00 2001 From: Gabriel mermelstein Date: Thu, 17 Apr 2025 11:40:16 +0300 Subject: [PATCH 27/29] improved alloc-dealloc structure --- library/libsds.nim | 23 ++++--------------- .../requests/sds_dependencies_request.nim | 7 +++--- .../requests/sds_message_request.nim | 11 +++++---- 3 files changed, 13 insertions(+), 28 deletions(-) diff --git a/library/libsds.nim b/library/libsds.nim index 895a960..6763e9d 100644 --- a/library/libsds.nim +++ b/library/libsds.nim @@ -226,18 +226,11 @@ proc WrapOutgoingMessage( callback(RET_ERR, unsafeAddr msg[0], cast[csize_t](len(msg)), userData) return RET_ERR - var msg = allocSharedSeqFromCArray(cast[ptr byte](message), messageLen.int) - let msgId = messageId.alloc() - - defer: - deallocSharedSeq(msg) - deallocShared(msgId) - handleRequest( ctx, RequestType.MESSAGE, SdsMessageRequest.createShared( - SdsMessageMsgType.WRAP_MESSAGE, msg, messageLen, msgId + SdsMessageMsgType.WRAP_MESSAGE, message, messageLen, messageId ), callback, userData, @@ -258,15 +251,12 @@ proc UnwrapReceivedMessage( callback(RET_ERR, unsafeAddr msg[0], cast[csize_t](len(msg)), userData) return RET_ERR - var msg = allocSharedSeqFromCArray(cast[ptr byte](message), messageLen.int) - - defer: - deallocSharedSeq(msg) - handleRequest( ctx, RequestType.MESSAGE, - SdsMessageRequest.createShared(SdsMessageMsgType.UNWRAP_MESSAGE, msg, messageLen), + SdsMessageRequest.createShared( + SdsMessageMsgType.UNWRAP_MESSAGE, message, messageLen + ), callback, userData, ) @@ -286,11 +276,6 @@ proc MarkDependenciesMet( callback(RET_ERR, unsafeAddr msg[0], cast[csize_t](len(msg)), userData) return RET_ERR - var messageIds = allocSharedSeqFromCArray(cast[ptr cstring](messageIds), count.int) - - defer: - deallocSharedSeq(messageIds) - handleRequest( ctx, RequestType.DEPENDENCIES, diff --git a/library/sds_thread/inter_thread_communication/requests/sds_dependencies_request.nim b/library/sds_thread/inter_thread_communication/requests/sds_dependencies_request.nim index 7c45087..ebb45c2 100644 --- a/library/sds_thread/inter_thread_communication/requests/sds_dependencies_request.nim +++ b/library/sds_thread/inter_thread_communication/requests/sds_dependencies_request.nim @@ -15,18 +15,17 @@ type SdsDependenciesRequest* = object proc createShared*( T: type SdsDependenciesRequest, op: SdsDependenciesMsgType, - messageIds: SharedSeq[cstring], + messageIds: pointer, count: csize_t = 0, ): ptr type T = var ret = createShared(T) ret[].operation = op - ret[].messageIds = messageIds # check if alloc is needed ret[].count = count + ret[].messageIds = allocSharedSeqFromCArray(cast[ptr cstring](messageIds), count.int) return ret proc destroyShared(self: ptr SdsDependenciesRequest) = - #deallocShared(self[].message) - #deallocShared(self[].messageId) + deallocSharedSeq(self[].messageIds) deallocShared(self) proc process*( diff --git a/library/sds_thread/inter_thread_communication/requests/sds_message_request.nim b/library/sds_thread/inter_thread_communication/requests/sds_message_request.nim index 77ec25f..6917ccf 100644 --- a/library/sds_thread/inter_thread_communication/requests/sds_message_request.nim +++ b/library/sds_thread/inter_thread_communication/requests/sds_message_request.nim @@ -21,20 +21,21 @@ type SdsUnwrapResponse* = object proc createShared*( T: type SdsMessageRequest, op: SdsMessageMsgType, - message: SharedSeq[byte], + message: pointer, messageLen: csize_t = 0, messageId: cstring = "", ): ptr type T = var ret = createShared(T) ret[].operation = op - ret[].message = message # check if alloc is needed ret[].messageLen = messageLen - ret[].messageId = messageId # check if alloc is needed + ret[].messageId = messageId.alloc() + ret[].message = allocSharedSeqFromCArray(cast[ptr byte](message), messageLen.int) + return ret proc destroyShared(self: ptr SdsMessageRequest) = - #deallocShared(self[].message) - #deallocShared(self[].messageId) + deallocSharedSeq(self[].message) + deallocShared(self[].messageId) deallocShared(self) proc process*( From f866c1765adcb973baebb4d87d6dd133b5074468 Mon Sep 17 00:00:00 2001 From: Gabriel mermelstein Date: Thu, 17 Apr 2025 11:42:26 +0300 Subject: [PATCH 28/29] adding log line --- .../requests/sds_lifecycle_request.nim | 1 + 1 file changed, 1 insertion(+) diff --git a/library/sds_thread/inter_thread_communication/requests/sds_lifecycle_request.nim b/library/sds_thread/inter_thread_communication/requests/sds_lifecycle_request.nim index ce90d2f..717e310 100644 --- a/library/sds_thread/inter_thread_communication/requests/sds_lifecycle_request.nim +++ b/library/sds_thread/inter_thread_communication/requests/sds_lifecycle_request.nim @@ -62,6 +62,7 @@ proc process*( return err("error processing CREATE_RELIABILITY_MANAGER request: " & $error) of RESET_RELIABILITY_MANAGER: resetReliabilityManager(rm[]).isOkOr: + error "RESET_RELIABILITY_MANAGER failed", error = error return err("error processing RESET_RELIABILITY_MANAGER request: " & $error) of START_PERIODIC_TASKS: rm[].startPeriodicTasks() From 63823b3f561d5baa5446f2433d0f780af5181141 Mon Sep 17 00:00:00 2001 From: Gabriel mermelstein Date: Sun, 27 Apr 2025 23:17:13 +0200 Subject: [PATCH 29/29] add missing initializeLibrary calls --- library/libsds.nim | 2 ++ 1 file changed, 2 insertions(+) diff --git a/library/libsds.nim b/library/libsds.nim index 6763e9d..5d9f669 100644 --- a/library/libsds.nim +++ b/library/libsds.nim @@ -196,6 +196,7 @@ proc CleanupReliabilityManager( proc ResetReliabilityManager( ctx: ptr SdsContext, callback: SdsCallBack, userData: pointer ): cint {.dynlib, exportc.} = + initializeLibrary() checkLibsdsParams(ctx, callback, userData) handleRequest( ctx, @@ -289,6 +290,7 @@ proc MarkDependenciesMet( proc StartPeriodicTasks( ctx: ptr SdsContext, callback: SdsCallBack, userData: pointer ): cint {.dynlib, exportc.} = + initializeLibrary() checkLibsdsParams(ctx, callback, userData) handleRequest( ctx,