support for the Genode OS framework (#5560)

This commit is contained in:
Emery Hemingway 2017-03-31 16:13:06 -05:00 • committed by Andreas Rumpf
commit 7e351fc7fa
14 changed files with 348 additions and 33 deletions

View file

@ -944,7 +944,6 @@ proc genEcho(p: BProc, n: PNode) =
# this unusal way of implementing it ensures that e.g. ``echo("hallo", 45)`` # this unusal way of implementing it ensures that e.g. ``echo("hallo", 45)``
# is threadsafe. # is threadsafe.
internalAssert n.kind == nkBracket internalAssert n.kind == nkBracket
p.module.includeHeader("<stdio.h>")
var args: Rope = nil var args: Rope = nil
var a: TLoc var a: TLoc
for i in countup(0, n.len-1): for i in countup(0, n.len-1):
@ -953,9 +952,15 @@ proc genEcho(p: BProc, n: PNode) =
else: else:
initLocExpr(p, n.sons[i], a) initLocExpr(p, n.sons[i], a)
addf(args, ", $1? ($1)->data:\"nil\"", [rdLoc(a)]) addf(args, ", $1? ($1)->data:\"nil\"", [rdLoc(a)])
linefmt(p, cpsStmts, "printf($1$2);$n", if platform.targetOS == osGenode:
makeCString(repeat("%s", n.len) & tnl), args) # bypass libc and print directly to the Genode LOG session
linefmt(p, cpsStmts, "fflush(stdout);$n") p.module.includeHeader("<base/log.h>")
linefmt(p, cpsStmts, """Genode::log(""$1);$n""", args)
else:
p.module.includeHeader("<stdio.h>")
linefmt(p, cpsStmts, "printf($1$2);$n",
makeCString(repeat("%s", n.len) & tnl), args)
linefmt(p, cpsStmts, "fflush(stdout);$n")
proc gcUsage(n: PNode) = proc gcUsage(n: PNode) =
if gSelectedGC == gcNone: message(n.info, warnGcMem, n.renderTree) if gSelectedGC == gcNone: message(n.info, warnGcMem, n.renderTree)

View file

@ -966,6 +966,19 @@ proc genMainProc(m: BModule) =
MainProcs & MainProcs &
"}$N$N" "}$N$N"
GenodeNimMain =
"Libc::Env *genodeEnv;$N" &
NimMainBody
ComponentConstruct =
"void Libc::Component::construct(Libc::Env &env) {$N" &
"\tgenodeEnv = &env;$N" &
"\tLibc::with_libc([&] () {$n\t" &
MainProcs &
"\t});$N" &
"\tenv.parent().exit(0);$N" &
"}$N$N"
var nimMain, otherMain: FormatStr var nimMain, otherMain: FormatStr
if platform.targetOS == osWindows and if platform.targetOS == osWindows and
gGlobalOptions * {optGenGuiApp, optGenDynLib} != {}: gGlobalOptions * {optGenGuiApp, optGenDynLib} != {}:
@ -976,6 +989,9 @@ proc genMainProc(m: BModule) =
nimMain = WinNimDllMain nimMain = WinNimDllMain
otherMain = WinCDllMain otherMain = WinCDllMain
m.includeHeader("<windows.h>") m.includeHeader("<windows.h>")
elif platform.targetOS == osGenode:
nimMain = GenodeNimMain
otherMain = ComponentConstruct
elif optGenDynLib in gGlobalOptions: elif optGenDynLib in gGlobalOptions:
nimMain = PosixNimDllMain nimMain = PosixNimDllMain
otherMain = PosixCDllMain otherMain = PosixCDllMain

View file

@ -21,7 +21,7 @@ type
# conditionals to condsyms (end of module). # conditionals to condsyms (end of module).
osNone, osDos, osWindows, osOs2, osLinux, osMorphos, osSkyos, osSolaris, osNone, osDos, osWindows, osOs2, osLinux, osMorphos, osSkyos, osSolaris,
osIrix, osNetbsd, osFreebsd, osOpenbsd, osDragonfly, osAix, osPalmos, osQnx, osIrix, osNetbsd, osFreebsd, osOpenbsd, osDragonfly, osAix, osPalmos, osQnx,
osAmiga, osAtari, osNetware, osMacos, osMacosx, osHaiku, osVxworks, osAmiga, osAtari, osNetware, osMacos, osMacosx, osHaiku, osVxworks, osGenode
osJS, osNimrodVM, osStandalone osJS, osNimrodVM, osStandalone
type type
@ -147,6 +147,11 @@ const
objExt: ".o", newLine: "\x0A", pathSep: ";", dirSep: "\\", objExt: ".o", newLine: "\x0A", pathSep: ";", dirSep: "\\",
scriptExt: ".sh", curDir: ".", exeExt: ".vxe", extSep: ".", scriptExt: ".sh", curDir: ".", exeExt: ".vxe", extSep: ".",
props: {ospNeedsPIC, ospPosix, ospLacksThreadVars}), props: {ospNeedsPIC, ospPosix, ospLacksThreadVars}),
(name: "Genode", pardir: "..", dllFrmt: "$1.lib.so", altDirSep: "/",
objExt: ".o", newLine: "\x0A", pathSep: ":", dirSep: "/",
scriptExt: "", curDir: "/", exeExt: "", extSep: ".",
props: {ospNeedsPIC, ospLacksThreadVars}),
(name: "JS", parDir: "..", (name: "JS", parDir: "..",
dllFrmt: "lib$1.so", altDirSep: "/", dllFrmt: "lib$1.so", altDirSep: "/",
objExt: ".o", newLine: "\x0A", objExt: ".o", newLine: "\x0A",

View file

@ -39,7 +39,7 @@ directly or indirectly through a call to a GC unsafe proc.
The `gcsafe`:idx: annotation can be used to mark a proc to be gcsafe, The `gcsafe`:idx: annotation can be used to mark a proc to be gcsafe,
otherwise this property is inferred by the compiler. Note that ``noSideEffect`` otherwise this property is inferred by the compiler. Note that ``noSideEffect``
implies ``gcsafe``. The only way to create a thread is via ``spawn`` or implies ``gcsafe``. The only way to create a thread is via ``spawn`` or
``createThead``. ``spawn`` is usually the preferable method. Either way ``createThread``. ``spawn`` is usually the preferable method. Either way
the invoked proc must not use ``var`` parameters nor must any of its parameters the invoked proc must not use ``var`` parameters nor must any of its parameters
contain a ``ref`` or ``closure`` type. This enforces contain a ``ref`` or ``closure`` type. This enforces
the *no heap sharing restriction*. the *no heap sharing restriction*.

78
lib/genode_cpp/syslocks.h Normal file
View file

@ -0,0 +1,78 @@
/*
*
* Nim's Runtime Library
* (c) Copyright 2017 Emery Hemingway
*
* See the file "copying.txt", included in this
* distribution, for details about the copyright.
*
*/
#ifndef _GENODE_CPP__SYSLOCKS_H_
#define _GENODE_CPP__SYSLOCKS_H_
/* Genode includes */
#include <base/semaphore.h>
#include <base/lock.h>
namespace Nim {
struct SysLock;
struct SysCond;
}
struct Nim::SysLock
{
Genode::Lock _lock_a, _lock_b;
bool _locked;
void acquireSys()
{
_lock_a.lock();
_locked = true;
_lock_a.unlock();
_lock_b.lock();
}
bool tryAcquireSys()
{
if (_locked)
return false;
_lock_a.lock();
if (_locked) {
_lock_a.unlock();
return false;
} else {
_locked = true;
_lock_b.lock();
_lock_a.unlock();
return true;
}
}
void releaseSys()
{
_locked = false;
_lock_a.unlock();
_lock_b.unlock();
}
};
struct Nim::SysCond
{
Genode::Semaphore _semaphore;
void waitSysCond(SysLock &syslock)
{
syslock.releaseSys();
_semaphore.down();
syslock.acquireSys();
}
void signalSysCond()
{
_semaphore.up();
}
};
#endif

69
lib/genode_cpp/threads.h Normal file
View file

@ -0,0 +1,69 @@
/*
*
* Nim's Runtime Library
* (c) Copyright 2017 Emery Hemingway
*
* See the file "copying.txt", included in this
* distribution, for details about the copyright.
*
*/
#ifndef _GENODE_CPP__THREAD_H_
#define _GENODE_CPP__THREAD_H_
#include <base/thread.h>
#include <util/avl_tree.h>
#include <util/reconstructible.h>
namespace Nim { struct SysThread; }
struct Nim::SysThread
{
typedef void (Entry)(void*);
struct Thread : Genode::Thread
{
void *_tls;
Entry *_func;
void *_arg;
void entry() override {
(_func)(_arg); }
Thread(Genode::Env &env, Genode::size_t stack_size, Entry func, void *arg)
: Genode::Thread(env, "nim-thread", stack_size), _func(func), _arg(arg)
{
Genode::Thread::start();
}
};
Genode::Constructible<Thread> _thread;
void initThread(Genode::Env *env, Genode::size_t stack_size, Entry func, void *arg) {
_thread.construct(*env, stack_size, func, arg); }
void joinThread() {
_thread->join(); }
static bool offMainThread() {
return dynamic_cast<SysThread::Thread*>(Genode::Thread::myself()); }
static void *threadVarGetValue()
{
SysThread::Thread *thr =
static_cast<SysThread::Thread*>(Genode::Thread::myself());
return thr->_tls;
}
static void threadVarSetValue(void *value)
{
SysThread::Thread *thr =
static_cast<SysThread::Thread*>(Genode::Thread::myself());
thr->_tls = value;
}
};
#endif

View file

@ -498,6 +498,11 @@ typedef int Nim_and_C_compiler_disagree_on_target_architecture[sizeof(NI) == siz
# include <sys/types.h> # include <sys/types.h>
#endif #endif
#if defined(__GENODE__)
#include <libc/component.h>
extern Libc::Env *genodeEnv;
#endif
/* Compile with -d:checkAbi and a sufficiently C11:ish compiler to enable */ /* Compile with -d:checkAbi and a sufficiently C11:ish compiler to enable */
#define NIM_CHECK_SIZE(typ, sz) \ #define NIM_CHECK_SIZE(typ, sz) \
_Static_assert(sizeof(typ) == sz, "Nim & C disagree on type size") _Static_assert(sizeof(typ) == sz, "Nim & C disagree on type size")

View file

@ -37,6 +37,10 @@ when defined(macosx) or defined(bsd):
a: var csize, b: pointer, c: int): cint {. a: var csize, b: pointer, c: int): cint {.
importc: "sysctl", nodecl.} importc: "sysctl", nodecl.}
when defined(genode):
proc affinitySpaceTotal(): cuint {.
importcpp: "genodeEnv->cpu().affinity_space().total()".}
proc countProcessors*(): int {.rtl, extern: "ncpi$1".} = proc countProcessors*(): int {.rtl, extern: "ncpi$1".} =
## returns the numer of the processors/cores the machine has. ## returns the numer of the processors/cores the machine has.
## Returns 0 if it cannot be detected. ## Returns 0 if it cannot be detected.
@ -61,7 +65,8 @@ proc countProcessors*(): int {.rtl, extern: "ncpi$1".} =
elif defined(irix): elif defined(irix):
var SC_NPROC_ONLN {.importc: "_SC_NPROC_ONLN", header: "<unistd.h>".}: cint var SC_NPROC_ONLN {.importc: "_SC_NPROC_ONLN", header: "<unistd.h>".}: cint
result = sysconf(SC_NPROC_ONLN) result = sysconf(SC_NPROC_ONLN)
elif defined(genode):
result = affinitySpaceTotal().int
else: else:
result = sysconf(SC_NPROCESSORS_ONLN) result = sysconf(SC_NPROCESSORS_ONLN)
if result <= 0: result = 1 if result <= 0: result = 1

View file

@ -825,16 +825,16 @@ proc putEnv*(key, val: string) {.tags: [WriteEnvEffect].} =
else: else:
add environment, (key & '=' & val) add environment, (key & '=' & val)
indx = high(environment) indx = high(environment)
when defined(unix): when defined(windows):
if c_putenv(environment[indx]) != 0'i32:
raiseOSError(osLastError())
else:
when useWinUnicode: when useWinUnicode:
var k = newWideCString(key) var k = newWideCString(key)
var v = newWideCString(val) var v = newWideCString(val)
if setEnvironmentVariableW(k, v) == 0'i32: raiseOSError(osLastError()) if setEnvironmentVariableW(k, v) == 0'i32: raiseOSError(osLastError())
else: else:
if setEnvironmentVariableA(key, val) == 0'i32: raiseOSError(osLastError()) if setEnvironmentVariableA(key, val) == 0'i32: raiseOSError(osLastError())
else:
if c_putenv(environment[indx]) != 0'i32:
raiseOSError(osLastError())
iterator envPairs*(): tuple[key, value: TaintedString] {.tags: [ReadEnvEffect].} = iterator envPairs*(): tuple[key, value: TaintedString] {.tags: [ReadEnvEffect].} =
## Iterate over all `environments variables`:idx:. In the first component ## Iterate over all `environments variables`:idx:. In the first component
@ -1091,7 +1091,7 @@ proc rawCreateDir(dir: string): bool =
result = false result = false
else: else:
raiseOSError(osLastError()) raiseOSError(osLastError())
elif defined(unix): elif defined(posix):
let res = mkdir(dir, 0o777) let res = mkdir(dir, 0o777)
if res == 0'i32: if res == 0'i32:
result = true result = true
@ -1452,7 +1452,9 @@ elif defined(windows):
if isNil(ownArgv): ownArgv = parseCmdLine($getCommandLine()) if isNil(ownArgv): ownArgv = parseCmdLine($getCommandLine())
return TaintedString(ownArgv[i]) return TaintedString(ownArgv[i])
elif not defined(createNimRtl) and not(defined(posix) and appType == "lib"): elif not defined(createNimRtl) and
not(defined(posix) and appType == "lib") and
not defined(genode):
# On Posix, there is no portable way to get the command line from a DLL. # On Posix, there is no portable way to get the command line from a DLL.
var var
cmdCount {.importc: "cmdCount".}: cint cmdCount {.importc: "cmdCount".}: cint
@ -1606,6 +1608,8 @@ proc getAppFilename*(): string {.rtl, extern: "nos$1", tags: [ReadIOEffect].} =
result = getApplAux("/proc/self/exe") result = getApplAux("/proc/self/exe")
elif defined(solaris): elif defined(solaris):
result = getApplAux("/proc/" & $getpid() & "/path/a.out") result = getApplAux("/proc/" & $getpid() & "/path/a.out")
elif defined(genode):
raiseOSError("POSIX command line not supported")
elif defined(freebsd) or defined(dragonfly): elif defined(freebsd) or defined(dragonfly):
result = getApplFreebsd() result = getApplFreebsd()
# little heuristic that may work on other POSIX-like systems: # little heuristic that may work on other POSIX-like systems:

View file

@ -1384,25 +1384,34 @@ var programResult* {.exportc: "nim_program_result".}: int
## under normal circumstances. When the program is terminated ## under normal circumstances. When the program is terminated
## prematurely using ``quit``, this value is ignored. ## prematurely using ``quit``, this value is ignored.
proc quit*(errorcode: int = QuitSuccess) {. when defined(nimdoc):
magic: "Exit", importc: "exit", header: "<stdlib.h>", noreturn.} proc quit*(errorcode: int = QuitSuccess) {.magic: "Exit", noreturn.}
## Stops the program immediately with an exit code. ## Stops the program immediately with an exit code.
## ##
## Before stopping the program the "quit procedures" are called in the ## Before stopping the program the "quit procedures" are called in the
## opposite order they were added with `addQuitProc <#addQuitProc>`_. ## opposite order they were added with `addQuitProc <#addQuitProc>`_.
## ``quit`` never returns and ignores any exception that may have been raised ## ``quit`` never returns and ignores any exception that may have been raised
## by the quit procedures. It does *not* call the garbage collector to free ## by the quit procedures. It does *not* call the garbage collector to free
## all the memory, unless a quit procedure calls `GC_fullCollect ## all the memory, unless a quit procedure calls `GC_fullCollect
## <#GC_fullCollect>`_. ## <#GC_fullCollect>`_.
## ##
## The proc ``quit(QuitSuccess)`` is called implicitly when your nim ## The proc ``quit(QuitSuccess)`` is called implicitly when your nim
## program finishes without incident. A raised unhandled exception is ## program finishes without incident. A raised unhandled exception is
## equivalent to calling ``quit(QuitFailure)``. ## equivalent to calling ``quit(QuitFailure)``.
## ##
## Note that this is a *runtime* call and using ``quit`` inside a macro won't ## Note that this is a *runtime* call and using ``quit`` inside a macro won't
## have any compile time effect. If you need to stop the compiler inside a ## have any compile time effect. If you need to stop the compiler inside a
## macro, use the `error <manual.html#error-pragma>`_ or `fatal ## macro, use the `error <manual.html#error-pragma>`_ or `fatal
## <manual.html#fatal-pragma>`_ pragmas. ## <manual.html#fatal-pragma>`_ pragmas.
elif defined(genode):
proc quit*(errorcode: int = QuitSuccess) {.magic: "Exit", noreturn,
importcpp: "genodeEnv->parent().exit(@)", header: "<base/env.h>".}
else:
proc quit*(errorcode: int = QuitSuccess) {.
magic: "Exit", importc: "exit", header: "<stdlib.h>", noreturn.}
template sysAssert(cond: bool, msg: string) = template sysAssert(cond: bool, msg: string) =
when defined(useSysAssert): when defined(useSysAssert):

View file

@ -146,6 +146,17 @@ elif defined(windows) or defined(dos):
if result != nil: return if result != nil: return
procAddrError(name) procAddrError(name)
elif defined(genode):
proc nimUnloadLibrary(lib: LibHandle) {.
error: "nimUnloadLibrary not implemented".}
proc nimLoadLibrary(path: string): LibHandle {.
error: "nimLoadLibrary not implemented".}
proc nimGetProcAddr(lib: LibHandle, name: cstring): ProcAddr {.
error: "nimGetProcAddr not implemented".}
else: else:
{.error: "no implementation for dyncalls".} {.error: "no implementation for dyncalls".}

View file

@ -77,6 +77,19 @@ when defined(emscripten):
var mmapDescr = cast[EmscriptenMMapBlock](mmapDescrPos) var mmapDescr = cast[EmscriptenMMapBlock](mmapDescrPos)
munmap(mmapDescr.realPointer, mmapDescr.realSize) munmap(mmapDescr.realPointer, mmapDescr.realSize)
elif defined(genode):
proc osAllocPages(size: int): pointer {.
importcpp: "genodeEnv->rm().attach(genodeEnv->ram().alloc(@))".}
proc osTryAllocPages(size: int): pointer =
{.emit: """try {""".}
result = osAllocPages size
{.emit: """} catch (...) { }""".}
proc osDeallocPages(p: pointer, size: int) {.
importcpp: "genodeEnv->rm().detach(#)".}
elif defined(posix): elif defined(posix):
const const
PROT_READ = 1 # page can be read PROT_READ = 1 # page can be read

View file

@ -75,6 +75,28 @@ when defined(Windows):
proc waitSysCondWindows(cond: var SysCond) = proc waitSysCondWindows(cond: var SysCond) =
discard waitForSingleObject(cond, -1'i32) discard waitForSingleObject(cond, -1'i32)
elif defined(genode):
const
Header = "genode_cpp/syslocks.h"
type
SysLock {.importcpp: "Nim::SysLock", pure, final,
header: Header.} = object
SysCond {.importcpp: "Nim::SysCond", pure, final,
header: Header.} = object
proc initSysLock(L: var SysLock) = discard
proc deinitSys(L: var SysLock) = discard
proc acquireSys(L: var SysLock) {.noSideEffect, importcpp.}
proc tryAcquireSys(L: var SysLock): bool {.noSideEffect, importcpp.}
proc releaseSys(L: var SysLock) {.noSideEffect, importcpp.}
proc initSysCond(L: var SysCond) = discard
proc deinitSysCond(L: var SysCond) = discard
proc waitSysCond(cond: var SysCond, lock: var SysLock) {.
noSideEffect, importcpp.}
proc signalSysCond(cond: var SysCond) {.
noSideEffect, importcpp.}
else: else:
type type
SysLock {.importc: "pthread_mutex_t", pure, final, SysLock {.importc: "pthread_mutex_t", pure, final,

View file

@ -46,7 +46,11 @@ const
maxRegisters = 256 # don't think there is an arch with more registers maxRegisters = 256 # don't think there is an arch with more registers
useStackMaskHack = false ## use the stack mask hack for better performance useStackMaskHack = false ## use the stack mask hack for better performance
StackGuardSize = 4096 StackGuardSize = 4096
ThreadStackMask = 1024*256*sizeof(int)-1 ThreadStackMask =
when defined(genode):
1024*64*sizeof(int)-1
else:
1024*256*sizeof(int)-1
ThreadStackSize = ThreadStackMask+1 - StackGuardSize ThreadStackSize = ThreadStackMask+1 - StackGuardSize
when defined(windows): when defined(windows):
@ -115,6 +119,49 @@ when defined(windows):
## get the ID of the currently running thread. ## get the ID of the currently running thread.
result = int(getCurrentThreadId()) result = int(getCurrentThreadId())
elif defined(genode):
const
GenodeHeader = "genode_cpp/threads.h"
type
SysThread* {.importcpp: "Nim::SysThread",
header: GenodeHeader, final, pure.} = object
GenodeThreadProc = proc (x: pointer) {.noconv.}
ThreadVarSlot = int
proc initThread(s: var SysThread,
stackSize: culonglong,
entry: GenodeThreadProc,
arg: pointer) {.
importcpp: "#.initThread(genodeEnv, @)".}
proc threadVarAlloc(): ThreadVarSlot = 0
proc offMainThread(): bool {.
importcpp: "Nim::SysThread::offMainThread",
header: GenodeHeader.}
proc threadVarSetValue(value: pointer) {.
importcpp: "Nim::SysThread::threadVarSetValue(@)",
header: GenodeHeader.}
proc threadVarGetValue(): pointer {.
importcpp: "Nim::SysThread::threadVarGetValue()",
header: GenodeHeader.}
var mainTls: pointer
proc threadVarSetValue(s: ThreadVarSlot, value: pointer) {.inline.} =
if offMainThread():
threadVarSetValue(value);
else:
mainTls = value
proc threadVarGetValue(s: ThreadVarSlot): pointer {.inline.} =
if offMainThread():
threadVarGetValue();
else:
mainTls
else: else:
when not defined(macosx): when not defined(macosx):
{.passL: "-pthread".} {.passL: "-pthread".}
@ -451,6 +498,9 @@ when defined(windows):
proc threadProcWrapper[TArg](closure: pointer): int32 {.stdcall.} = proc threadProcWrapper[TArg](closure: pointer): int32 {.stdcall.} =
threadProcWrapperBody(closure) threadProcWrapperBody(closure)
# implicitly return 0 # implicitly return 0
elif defined(genode):
proc threadProcWrapper[TArg](closure: pointer) {.noconv.} =
threadProcWrapperBody(closure)
else: else:
proc threadProcWrapper[TArg](closure: pointer): pointer {.noconv.} = proc threadProcWrapper[TArg](closure: pointer): pointer {.noconv.} =
threadProcWrapperBody(closure) threadProcWrapperBody(closure)
@ -482,6 +532,14 @@ when hostOS == "windows":
cast[ptr SysThread](addr(a)), 1, -1) cast[ptr SysThread](addr(a)), 1, -1)
inc(k, MAXIMUM_WAIT_OBJECTS) inc(k, MAXIMUM_WAIT_OBJECTS)
elif defined(genode):
proc joinThread*[TArg](t: Thread[TArg]) {.importcpp.}
## waits for the thread `t` to finish.
proc joinThreads*[TArg](t: varargs[Thread[TArg]]) =
## waits for every thread in `t` to finish.
for i in 0..t.high: joinThread(t[i])
else: else:
proc joinThread*[TArg](t: Thread[TArg]) {.inline.} = proc joinThread*[TArg](t: Thread[TArg]) {.inline.} =
## waits for the thread `t` to finish. ## waits for the thread `t` to finish.
@ -531,6 +589,21 @@ when hostOS == "windows":
## shouldn't use this proc. ## shouldn't use this proc.
setThreadAffinityMask(t.sys, uint(1 shl cpu)) setThreadAffinityMask(t.sys, uint(1 shl cpu))
elif defined(genode):
proc createThread*[TArg](t: var Thread[TArg],
tp: proc (arg: TArg) {.thread, nimcall.},
param: TArg) =
when TArg isnot void: t.data = param
t.dataFn = tp
when hasSharedHeap: t.stackSize = ThreadStackSize
t.sys.initThread(
ThreadStackSize.culonglong,
threadProcWrapper[TArg], addr(t))
proc pinToCpu*[Arg](t: var Thread[Arg]; cpu: Natural) =
{.hint: "cannot change Genode thread CPU affinity after initialization".}
discard
else: else:
proc createThread*[TArg](t: var Thread[TArg], proc createThread*[TArg](t: var Thread[TArg],
tp: proc (arg: TArg) {.thread, nimcall.}, tp: proc (arg: TArg) {.thread, nimcall.},