mirror of
https://github.com/logos-messaging/nim-sds.git
synced 2026-01-07 00:23:12 +00:00
adding missing files
This commit is contained in:
parent
8806391371
commit
dbf271e0f9
2
Makefile
2
Makefile
@ -9,7 +9,7 @@ deps: | sds.nims
|
|||||||
STATIC ?= 0
|
STATIC ?= 0
|
||||||
|
|
||||||
libsds: deps
|
libsds: deps
|
||||||
rm -f build/libwaku*
|
rm -f build/libsds*
|
||||||
ifeq ($(STATIC), 1)
|
ifeq ($(STATIC), 1)
|
||||||
echo -e $(BUILD_MSG) "build/$@.a" && \
|
echo -e $(BUILD_MSG) "build/$@.a" && \
|
||||||
$(ENV_SCRIPT) nim libsdsStatic $(NIM_PARAMS) sds.nims
|
$(ENV_SCRIPT) nim libsdsStatic $(NIM_PARAMS) sds.nims
|
||||||
|
|||||||
42
library/alloc.nim
Normal file
42
library/alloc.nim
Normal file
@ -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
|
||||||
30
library/ffi_types.nim
Normal file
30
library/ffi_types.nim
Normal file
@ -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
|
||||||
|
################################################################################
|
||||||
@ -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("")
|
||||||
@ -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
|
||||||
129
library/sds_thread/sds_thread.nim
Normal file
129
library/sds_thread/sds_thread.nim
Normal file
@ -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()
|
||||||
Loading…
x
Reference in New Issue
Block a user