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
parent 57246cbcec
commit 7e351fc7fa
14 changed files with 348 additions and 33 deletions

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>
#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 */
#define NIM_CHECK_SIZE(typ, sz) \
_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 {.
importc: "sysctl", nodecl.}
when defined(genode):
proc affinitySpaceTotal(): cuint {.
importcpp: "genodeEnv->cpu().affinity_space().total()".}
proc countProcessors*(): int {.rtl, extern: "ncpi$1".} =
## returns the numer of the processors/cores the machine has.
## Returns 0 if it cannot be detected.
@@ -61,7 +65,8 @@ proc countProcessors*(): int {.rtl, extern: "ncpi$1".} =
elif defined(irix):
var SC_NPROC_ONLN {.importc: "_SC_NPROC_ONLN", header: "<unistd.h>".}: cint
result = sysconf(SC_NPROC_ONLN)
elif defined(genode):
result = affinitySpaceTotal().int
else:
result = sysconf(SC_NPROCESSORS_ONLN)
if result <= 0: result = 1

View File

@@ -825,16 +825,16 @@ proc putEnv*(key, val: string) {.tags: [WriteEnvEffect].} =
else:
add environment, (key & '=' & val)
indx = high(environment)
when defined(unix):
if c_putenv(environment[indx]) != 0'i32:
raiseOSError(osLastError())
else:
when defined(windows):
when useWinUnicode:
var k = newWideCString(key)
var v = newWideCString(val)
if setEnvironmentVariableW(k, v) == 0'i32: raiseOSError(osLastError())
else:
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].} =
## Iterate over all `environments variables`:idx:. In the first component
@@ -1091,7 +1091,7 @@ proc rawCreateDir(dir: string): bool =
result = false
else:
raiseOSError(osLastError())
elif defined(unix):
elif defined(posix):
let res = mkdir(dir, 0o777)
if res == 0'i32:
result = true
@@ -1452,7 +1452,9 @@ elif defined(windows):
if isNil(ownArgv): ownArgv = parseCmdLine($getCommandLine())
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.
var
cmdCount {.importc: "cmdCount".}: cint
@@ -1606,6 +1608,8 @@ proc getAppFilename*(): string {.rtl, extern: "nos$1", tags: [ReadIOEffect].} =
result = getApplAux("/proc/self/exe")
elif defined(solaris):
result = getApplAux("/proc/" & $getpid() & "/path/a.out")
elif defined(genode):
raiseOSError("POSIX command line not supported")
elif defined(freebsd) or defined(dragonfly):
result = getApplFreebsd()
# 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
## prematurely using ``quit``, this value is ignored.
proc quit*(errorcode: int = QuitSuccess) {.
magic: "Exit", importc: "exit", header: "<stdlib.h>", noreturn.}
## Stops the program immediately with an exit code.
##
## Before stopping the program the "quit procedures" are called in the
## opposite order they were added with `addQuitProc <#addQuitProc>`_.
## ``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
## all the memory, unless a quit procedure calls `GC_fullCollect
## <#GC_fullCollect>`_.
##
## The proc ``quit(QuitSuccess)`` is called implicitly when your nim
## program finishes without incident. A raised unhandled exception is
## equivalent to calling ``quit(QuitFailure)``.
##
## 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
## macro, use the `error <manual.html#error-pragma>`_ or `fatal
## <manual.html#fatal-pragma>`_ pragmas.
when defined(nimdoc):
proc quit*(errorcode: int = QuitSuccess) {.magic: "Exit", noreturn.}
## Stops the program immediately with an exit code.
##
## Before stopping the program the "quit procedures" are called in the
## opposite order they were added with `addQuitProc <#addQuitProc>`_.
## ``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
## all the memory, unless a quit procedure calls `GC_fullCollect
## <#GC_fullCollect>`_.
##
## The proc ``quit(QuitSuccess)`` is called implicitly when your nim
## program finishes without incident. A raised unhandled exception is
## equivalent to calling ``quit(QuitFailure)``.
##
## 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
## macro, use the `error <manual.html#error-pragma>`_ or `fatal
## <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) =
when defined(useSysAssert):

View File

@@ -146,6 +146,17 @@ elif defined(windows) or defined(dos):
if result != nil: return
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:
{.error: "no implementation for dyncalls".}

View File

@@ -77,6 +77,19 @@ when defined(emscripten):
var mmapDescr = cast[EmscriptenMMapBlock](mmapDescrPos)
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):
const
PROT_READ = 1 # page can be read

View File

@@ -75,6 +75,28 @@ when defined(Windows):
proc waitSysCondWindows(cond: var SysCond) =
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:
type
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
useStackMaskHack = false ## use the stack mask hack for better performance
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
when defined(windows):
@@ -115,6 +119,49 @@ when defined(windows):
## get the ID of the currently running thread.
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:
when not defined(macosx):
{.passL: "-pthread".}
@@ -451,6 +498,9 @@ when defined(windows):
proc threadProcWrapper[TArg](closure: pointer): int32 {.stdcall.} =
threadProcWrapperBody(closure)
# implicitly return 0
elif defined(genode):
proc threadProcWrapper[TArg](closure: pointer) {.noconv.} =
threadProcWrapperBody(closure)
else:
proc threadProcWrapper[TArg](closure: pointer): pointer {.noconv.} =
threadProcWrapperBody(closure)
@@ -482,6 +532,14 @@ when hostOS == "windows":
cast[ptr SysThread](addr(a)), 1, -1)
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:
proc joinThread*[TArg](t: Thread[TArg]) {.inline.} =
## waits for the thread `t` to finish.
@@ -531,6 +589,21 @@ when hostOS == "windows":
## shouldn't use this proc.
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:
proc createThread*[TArg](t: var Thread[TArg],
tp: proc (arg: TArg) {.thread, nimcall.},