summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--lib/genode/constructibles.nim21
-rw-r--r--lib/genode/entrypoints.nim22
-rw-r--r--lib/genode/signals.nim77
-rw-r--r--lib/genode_cpp/signals.h39
-rw-r--r--lib/pure/asyncdispatch.nim32
5 files changed, 188 insertions, 3 deletions
diff --git a/lib/genode/constructibles.nim b/lib/genode/constructibles.nim
new file mode 100644
index 000000000..3a4a646e0
--- /dev/null
+++ b/lib/genode/constructibles.nim
@@ -0,0 +1,21 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2022 Emery Hemingway
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+type Constructible*[T] {.
+  importcpp: "Genode::Constructible",
+  header: "<util/reconstructible.h>", byref, pure.} = object
+
+proc construct*[T](x: Constructible[T]) {.importcpp.}
+  ## Construct a constructible C++ object.
+
+proc destruct*[T](x: Constructible[T]) {.importcpp.}
+  ## Destruct a constructible C++ object.
+
+proc constructed*[T](x: Constructible[T]): bool {.importcpp.}
+  ## Test if an object is constructed.
diff --git a/lib/genode/entrypoints.nim b/lib/genode/entrypoints.nim
new file mode 100644
index 000000000..0bf5e0e0e
--- /dev/null
+++ b/lib/genode/entrypoints.nim
@@ -0,0 +1,22 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2022 Emery Hemingway
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## See `Genode Foundations - Entrypoint <https://genode.org/documentation/genode-foundations/21.05/functional_specification/Entrypoint.html>`
+## for a description of Entrypoints.
+
+type
+  EntrypointObj {.
+    importcpp: "Genode::Entrypoint",
+    header: "<base/entrypoint.h>",
+    pure.} = object
+  Entrypoint* = ptr EntrypointObj
+    ## Opaque Entrypoint object.
+
+proc ep*(env: GenodeEnv): Entrypoint {.importcpp: "(&#->ep())".}
+  ## Access the entrypoint associated with `env`.
diff --git a/lib/genode/signals.nim b/lib/genode/signals.nim
new file mode 100644
index 000000000..7d1875730
--- /dev/null
+++ b/lib/genode/signals.nim
@@ -0,0 +1,77 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2022 Emery Hemingway
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## See `Genode Foundations - Asynchronous notifications <https://genode.org/documentation/genode-foundations/21.05/architecture/Inter-component_communication.html#Asynchronous_notifications>`
+## for a description of Genode signals.
+
+when not defined(genode) or defined(nimdoc):
+  {.error: "Genode only module".}
+
+import ./entrypoints, ./constructibles
+
+export ep # Entrypoint accessor on GenodeEnv
+
+type
+  SignalContextCapability* {.
+    importcpp: "Genode::Signal_context_capability",
+    header: "<base/signal.h>", pure.} = object
+    ## Capability to an asynchronous signal context.
+
+proc isValid*(cap: SignalContextCapability): bool {.importcpp: "#.valid()".}
+  ## Call the Genode core to check if this `SignalContextCapability` is valid.
+  # TODO: RpcEffect
+
+type
+  HandlerProc = proc () {.closure, gcsafe.}
+
+  SignalHandlerBase {.
+    importcpp: "Nim::SignalHandler",
+    header: "genode_cpp/signals.h",
+    pure.} = object
+
+  SignalHandlerCpp = Constructible[SignalHandlerBase]
+
+  SignalHandlerObj = object
+    cpp: SignalHandlerCpp
+    cb: HandlerProc
+      ## Signal handling procedure called during dispatch.
+
+  SignalHandler* = ref SignalHandlerObj
+    ## Nim object enclosing a Genode signal handler.
+
+proc construct(cpp: SignalHandlerCpp; ep: Entrypoint; sh: SignalHandler) {.importcpp.}
+
+proc cap(cpp: SignalHandlerCpp): SignalContextCapability {.importcpp: "#->cap()".}
+
+proc newSignalHandler*(ep: Entrypoint; cb: HandlerProc): SignalHandler =
+  ## Create a new signal handler. A label is recommended for
+  ## debugging purposes. A signal handler will not be garbage
+  ## collected until after it has been dissolved.
+  result = SignalHandler(cb: cb)
+  result.cpp.construct(ep, result)
+  GCref result
+
+proc dissolve*(sig: SignalHandler) =
+  ## Dissolve signal dispatcher from entrypoint.
+  # TODO: =destroy?
+  destruct sig.cpp
+  sig.cb = nil # lose the callback
+  GCunref sig
+
+proc cap*(sig: SignalHandler): SignalContextCapability =
+  ## Signal context capability. Can be delegated to external components.
+  sig.cpp.cap
+
+proc submit*(cap: SignalContextCapability) {.
+    importcpp: "Genode::Signal_transmitter(#).submit()".}
+  ## Submit a signal to a context capability.
+
+proc nimHandleSignal(p: pointer) {.exportc.} =
+  ## C symbol invoked by entrypoint during signal dispatch.
+  cast[SignalHandler](p).cb()
diff --git a/lib/genode_cpp/signals.h b/lib/genode_cpp/signals.h
new file mode 100644
index 000000000..fa3975d38
--- /dev/null
+++ b/lib/genode_cpp/signals.h
@@ -0,0 +1,39 @@
+/*
+ *
+ *           Nim's Runtime Library
+ *       (c) Copyright 2022 Emery Hemingway
+ *
+ *   See the file "copying.txt", included in this
+ *   distribution, for details about the copyright.
+ *
+ */
+
+#ifndef _NIM_SIGNALS_H_
+#define _NIM_SIGNALS_H_
+
+#include <libc/component.h>
+#include <base/signal.h>
+#include <util/reconstructible.h>
+
+// Symbol for calling back into Nim
+extern "C" void nimHandleSignal(void *arg);
+
+namespace Nim { struct SignalHandler; }
+
+struct Nim::SignalHandler
+{
+	// Pointer to the Nim handler object.
+	void *arg;
+
+	void handle_signal() {
+		Libc::with_libc([this] () { nimHandleSignal(arg); }); }
+
+	Genode::Signal_handler<SignalHandler> handler;
+
+	SignalHandler(Genode::Entrypoint *ep, void *arg)
+	: arg(arg), handler(*ep, *this, &SignalHandler::handle_signal) { }
+
+	Genode::Signal_context_capability cap() { return handler; }
+};
+
+#endif
diff --git a/lib/pure/asyncdispatch.nim b/lib/pure/asyncdispatch.nim
index 12378d780..a1d68e85e 100644
--- a/lib/pure/asyncdispatch.nim
+++ b/lib/pure/asyncdispatch.nim
@@ -281,6 +281,8 @@ proc adjustTimeout(
   result = max(nextTimer.get(), 0)
   result = min(pollTimeout, result)
 
+proc runOnce(timeout: int): bool {.gcsafe.}
+
 proc callSoon*(cbproc: proc () {.gcsafe.}) {.gcsafe.}
   ## Schedule `cbproc` to be called as soon as possible.
   ## The callback is called when control returns to the event loop.
@@ -390,7 +392,7 @@ when defined(windows) or defined(nimdoc):
     let p = getGlobalDispatcher()
     p.handles.len != 0 or p.timers.len != 0 or p.callbacks.len != 0
 
-  proc runOnce(timeout = 500): bool =
+  proc runOnce(timeout: int): bool =
     let p = getGlobalDispatcher()
     if p.handles.len == 0 and p.timers.len == 0 and p.callbacks.len == 0:
       raise newException(ValueError,
@@ -1169,6 +1171,9 @@ else:
                     MSG_NOSIGNAL
   when declared(posix.accept4):
     from posix import accept4, SOCK_CLOEXEC
+  when defined(genode):
+    include genode/env # get the implicit Genode env
+    import genode/signals
 
   const
     InitCallbackListSize = 4         # initial size of callbacks sequence,
@@ -1187,6 +1192,8 @@ else:
 
     PDispatcher* = ref object of PDispatcherBase
       selector: Selector[AsyncData]
+      when defined(genode):
+        signalHandler: SignalHandler
 
   proc `==`*(x, y: AsyncFD): bool {.borrow.}
   proc `==`*(x, y: AsyncEvent): bool {.borrow.}
@@ -1202,6 +1209,10 @@ else:
     result.selector = newSelector[AsyncData]()
     result.timers.clear()
     result.callbacks = initDeque[proc () {.closure, gcsafe.}](InitDelayedCallbackListSize)
+    when defined(genode):
+      let entrypoint = ep(cast[GenodeEnv](runtimeEnv))
+      result.signalHandler = newSignalHandler(entrypoint):
+        discard runOnce(0)
 
   var gDisp{.threadvar.}: owned PDispatcher ## Global dispatcher
 
@@ -1371,10 +1382,11 @@ else:
           ValueError, "Expecting async operations to stop when fd has closed."
         )
 
-
-  proc runOnce(timeout = 500): bool =
+  proc runOnce(timeout: int): bool =
     let p = getGlobalDispatcher()
     if p.selector.isEmpty() and p.timers.len == 0 and p.callbacks.len == 0:
+      when defined(genode):
+        if timeout == 0: return
       raise newException(ValueError,
         "No handles or timers registered in dispatcher.")
 
@@ -2025,3 +2037,17 @@ when defined(linux) or defined(windows) or defined(macosx) or defined(bsd) or
       if getrlimit(RLIMIT_NOFILE, fdLim) < 0:
         raiseOSError(osLastError())
       result = int(fdLim.rlim_cur) - 1
+
+when defined(genode):
+  proc scheduleCallbacks*(): bool {.discardable.} =
+    ## *Genode only.*
+    ## Schedule callback processing and return immediately.
+    ## Returns `false` if there is nothing to schedule.
+    ## RPC servers should call this to dispatch `callSoon`
+    ## bodies after retiring an RPC to its client.
+    ## This is effectively a non-blocking `poll(…)` and is
+    ## equivalent to scheduling a momentary no-op timeout
+    ## but faster and with less overhead.
+    let dis = getGlobalDispatcher()
+    result = dis.callbacks.len > 0
+    if result: submit(dis.signalHandler.cap)