mirror of
https://github.com/logos-storage/logos-storage-nim.git
synced 2026-01-03 14:03:10 +00:00
166 lines
5.1 KiB
Nim
166 lines
5.1 KiB
Nim
|
|
# libcodex.nim - C-exported interface for the Codex shared library
|
|||
|
|
#
|
|||
|
|
# This file implements the public C API for libcodex.
|
|||
|
|
# It acts as the bridge between C programs and the internal Nim implementation.
|
|||
|
|
#
|
|||
|
|
# This file defines:
|
|||
|
|
# - Initialization logic for the Nim runtime (once per process)
|
|||
|
|
# - Thread-safe exported procs callable from C
|
|||
|
|
# - Callback registration and invocation for asynchronous communication
|
|||
|
|
|
|||
|
|
# cdecl is C declaration calling convention.
|
|||
|
|
# It’s the standard way C compilers expect functions to behave:
|
|||
|
|
# 1- Caller cleans up the stack after the call
|
|||
|
|
# 2- Symbol names are exported in a predictable way
|
|||
|
|
# In other termes, it is a glue that makes Nim functions callable as normal C functions.
|
|||
|
|
{.pragma: exported, exportc, cdecl, raises: [].}
|
|||
|
|
{.pragma: callback, cdecl, raises: [], gcsafe.}
|
|||
|
|
|
|||
|
|
# Ensure code is position-independent so it can be built into a shared library (.so).
|
|||
|
|
# In other terms, the code that can run no matter where it’s placed in memory.
|
|||
|
|
{.passc: "-fPIC".}
|
|||
|
|
|
|||
|
|
when defined(linux):
|
|||
|
|
# Define the canonical name for this library
|
|||
|
|
{.passl: "-Wl,-soname,libcodex.so".}
|
|||
|
|
|
|||
|
|
import std/[atomics]
|
|||
|
|
import chronicles
|
|||
|
|
import chronos
|
|||
|
|
import ./codex_context
|
|||
|
|
import ./codex_thread_requests/codex_thread_request
|
|||
|
|
import ./codex_thread_requests/requests/node_lifecycle_request
|
|||
|
|
import ./ffi_types
|
|||
|
|
|
|||
|
|
template checkLibcodexParams*(
|
|||
|
|
ctx: ptr CodexContext, callback: CodexCallback, userData: pointer
|
|||
|
|
) =
|
|||
|
|
if not isNil(ctx):
|
|||
|
|
ctx[].userData = userData
|
|||
|
|
|
|||
|
|
if isNil(callback):
|
|||
|
|
return RET_MISSING_CALLBACK
|
|||
|
|
|
|||
|
|
proc handleRequest(
|
|||
|
|
ctx: ptr CodexContext,
|
|||
|
|
requestType: RequestType,
|
|||
|
|
content: pointer,
|
|||
|
|
callback: CodexCallback,
|
|||
|
|
userData: pointer,
|
|||
|
|
): cint =
|
|||
|
|
codex_context.sendRequestToCodexThread(ctx, requestType, content, callback, userData).isOkOr:
|
|||
|
|
let msg = "libcodex error: " & $error
|
|||
|
|
callback(RET_ERR, unsafeAddr msg[0], cast[csize_t](len(msg)), userData)
|
|||
|
|
return RET_ERR
|
|||
|
|
|
|||
|
|
return RET_OK
|
|||
|
|
|
|||
|
|
# From Nim doc:
|
|||
|
|
# "the C targets require you to initialize Nim's internals, which is done calling a NimMain function."
|
|||
|
|
# "The name NimMain can be influenced via the --nimMainPrefix:prefix switch."
|
|||
|
|
# "Use --nimMainPrefix:MyLib and the function to call is named MyLibNimMain."
|
|||
|
|
proc libcodexNimMain() {.importc.}
|
|||
|
|
|
|||
|
|
# Atomic flag to prevent multiple initializations
|
|||
|
|
var initialized: Atomic[bool]
|
|||
|
|
|
|||
|
|
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
|
|||
|
|
|
|||
|
|
# Initializes the Nim runtime and foreign-thread GC
|
|||
|
|
proc initializeLibrary() {.exported.} =
|
|||
|
|
if not initialized.exchange(true):
|
|||
|
|
## Every Nim library must call `<prefix>NimMain()` once
|
|||
|
|
libcodexNimMain()
|
|||
|
|
when declared(setupForeignThreadGc):
|
|||
|
|
setupForeignThreadGc()
|
|||
|
|
when declared(nimGC_setStackBottom):
|
|||
|
|
var locals {.volatile, noinit.}: pointer
|
|||
|
|
locals = addr(locals)
|
|||
|
|
nimGC_setStackBottom(locals)
|
|||
|
|
|
|||
|
|
proc codex_new(
|
|||
|
|
configJson: cstring, callback: CodexCallback, userData: pointer
|
|||
|
|
): pointer {.dynlib, exportc, cdecl.} =
|
|||
|
|
initializeLibrary()
|
|||
|
|
|
|||
|
|
if isNil(callback):
|
|||
|
|
error "Missing callback in codex_new"
|
|||
|
|
return nil
|
|||
|
|
|
|||
|
|
var ctx = codex_context.createCodexContext().valueOr:
|
|||
|
|
let msg = "Error in createCodexContext: " & $error
|
|||
|
|
callback(RET_ERR, unsafeAddr msg[0], cast[csize_t](len(msg)), userData)
|
|||
|
|
return nil
|
|||
|
|
|
|||
|
|
ctx.userData = userData
|
|||
|
|
|
|||
|
|
let retCode = handleRequest(
|
|||
|
|
ctx,
|
|||
|
|
RequestType.LIFECYCLE,
|
|||
|
|
NodeLifecycleRequest.createShared(
|
|||
|
|
NodeLifecycleMsgType.CREATE_NODE, configJson # , appCallbacks
|
|||
|
|
),
|
|||
|
|
callback,
|
|||
|
|
userData,
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
if retCode == RET_ERR:
|
|||
|
|
return nil
|
|||
|
|
|
|||
|
|
return ctx
|
|||
|
|
|
|||
|
|
proc codex_destroy(
|
|||
|
|
ctx: ptr CodexContext, callback: COdexCallback, userData: pointer
|
|||
|
|
): cint {.dynlib, exportc.} =
|
|||
|
|
initializeLibrary()
|
|||
|
|
checkLibcodexParams(ctx, callback, userData)
|
|||
|
|
|
|||
|
|
codex_context.destroyCodexContext(ctx).isOkOr:
|
|||
|
|
let msg = "libcodex 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
|
|||
|
|
|
|||
|
|
proc codex_start(
|
|||
|
|
ctx: ptr CodexContext, callback: CodexCallback, userData: pointer
|
|||
|
|
): cint {.dynlib, exportc.} =
|
|||
|
|
initializeLibrary()
|
|||
|
|
checkLibcodexParams(ctx, callback, userData)
|
|||
|
|
handleRequest(
|
|||
|
|
ctx,
|
|||
|
|
RequestType.LIFECYCLE,
|
|||
|
|
NodeLifecycleRequest.createShared(NodeLifecycleMsgType.START_NODE),
|
|||
|
|
callback,
|
|||
|
|
userData,
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
proc codex_stop(
|
|||
|
|
ctx: ptr CodexContext, callback: CodexCallback, userData: pointer
|
|||
|
|
): cint {.dynlib, exportc.} =
|
|||
|
|
initializeLibrary()
|
|||
|
|
checkLibcodexParams(ctx, callback, userData)
|
|||
|
|
handleRequest(
|
|||
|
|
ctx,
|
|||
|
|
RequestType.LIFECYCLE,
|
|||
|
|
NodeLifecycleRequest.createShared(NodeLifecycleMsgType.STOP_NODE),
|
|||
|
|
callback,
|
|||
|
|
userData,
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
proc codex_set_event_callback(
|
|||
|
|
ctx: ptr CodexContext, callback: CodexCallback, userData: pointer
|
|||
|
|
) {.dynlib, exportc.} =
|
|||
|
|
initializeLibrary()
|
|||
|
|
ctx[].eventCallback = cast[pointer](callback)
|
|||
|
|
ctx[].eventUserData = userData
|