From 419503db1de3954d6cc1b5b63f2fcb445be77f03 Mon Sep 17 00:00:00 2001 From: Emery Hemingway Date: Sat, 27 Jan 2018 19:49:25 +0100 Subject: [PATCH] Update World Nim libraries - Prevent GC from freeing active signal handlers. - New module for implementing Nim servers. - Stricter typing for compatibility with upcoming language release. Fix #96 --- include/nim/genode.nim | 10 +- include/nim/genodeservers.nim | 227 ++++++++++++++++++++++++++++++++++ include/nim/inputevent.nim | 5 +- 3 files changed, 238 insertions(+), 4 deletions(-) create mode 100644 include/nim/genodeservers.nim diff --git a/include/nim/genode.nim b/include/nim/genode.nim index 14ee83e..f2d0484 100644 --- a/include/nim/genode.nim +++ b/include/nim/genode.nim @@ -46,6 +46,7 @@ type cap {.importcpp.}: SignalContextCapability SignalDispatcher* = ref object + ## Nim object enclosing a Genode signal dispatcher. cpp: SignalDispatcherCpp handler*: proc() {.closure.} @@ -54,14 +55,19 @@ proc init(cpp: SignalDispatcherCpp; sh: SignalDispatcher) proc deinit(sd: SignalDispatcherCpp) {.importcpp.} -proc newSignalDispatcher*(): SignalDispatcher = +proc newSignalDispatcher*(label = "unspecified"): SignalDispatcher = + ## Create a new signal dispatcher. A label is recommended for + ## debugging purposes. A signal dispatcher will not be garbage + ## collected until after it has been dissolved. new result init result.cpp, result + GCref result proc dissolve*(sig: SignalDispatcher) = ## Dissolve signal dispatcher from entrypoint. deinit sig.cpp sig.handler = nil + GCunref sig proc cap*(sig: SignalDispatcher): SignalContextCapability = sig.cpp.cap ## Signal context capability. Can be delegated to external components. @@ -112,7 +118,7 @@ proc detach*(p: ByteAddress) {. type DataspaceStream* = ref DataspaceStreamObj - ## a stream that provides safe access to dataspace content + ## A stream that provides safe access to dataspace content DataspaceStreamObj* = object of StreamObj cap: DataspaceCapability base: ByteAddress diff --git a/include/nim/genodeservers.nim b/include/nim/genodeservers.nim new file mode 100644 index 0000000..2686ad5 --- /dev/null +++ b/include/nim/genodeservers.nim @@ -0,0 +1,227 @@ +# +# \brief Nim utilities for Genode servers +# \author Emery Hemingway +# \date 2017-12-10 +# + +# +# Copyright (C) 2017 Genode Labs GmbH +# +# This file is part of the Genode OS framework, which is distributed +# under the terms of the GNU Affero General Public License version 3. +# + +# TODO: Provide sesion creation and destruction examples. + +import parseutils, strutils, strtabs, streams, + parsexml, xmlparser, xmltree + +const LabelSep = " ->" + ## Pattern used to seperate session label elements. + +proc lastLabelElement*(label: TaintedString): string = + ## Return the last element in a session label. + let i = label.rFind LabelSep + if unlikely(i == -1): + label + else: + label[i+1+LabelSep.len..label.high] + +iterator elements*(label: TaintedString): string = + ## Iterate the elements of a session label from left to right. + var + buf = "" + pos = 0 + while pos < label.len: + let n = parseuntil(label, buf, LabelSep, pos) + if n == 0: break + yield buf.strip + pos.inc(n + LabelSep.len+1) + +proc lookupPolicy*(policies: seq[XmlNode]; label: TaintedString): XmlNode = + ## Return a policy matching a given label or return nil. + # TODO: use less GC intensive parsing. + var resultScore: int + for p in policies.items: + if p.tag == "default-policy" and result.isNil: + # this is now the fallthrough policy + result = p + else: + let attrs = p.attrs + if attrs.contains "label": + if attrs["label"] == label: + # return the first policy with a label match + return p + else: + let + prefix = attrs.getOrDefault "prefix" + suffix = attrs.getOrDefault "suffix" + if label.startsWith(prefix) and label.endsWith(suffix): + # match the label against prefix and suffic (empty strings match) + let score = prefix.len*2 + suffix.len + # a prefix match is more significant that a suffix match + if score > resultScore: + # this is now the best match this far + resultScore = score + result = p + +proc parseArgString*(args, key: string; default = ""): string = + ## Extract a keyed value from session request arguments. + result = default + var off = args.find key + if off != -1: + off.inc key.len + if args[off] == '=' and args[off+1] == '"': + off.inc 2 + off.inc parseuntil(args, result, '"', off) + if args[off] != '"': + result = default + +proc parseArgInt*(args, key: string; default = -1): BiggestInt = + ## Extract an integral argument from session request arguments. + result = default + var off = args.find key + if off != -1: + off.inc key.len + if args[off] == '=': + inc off + off.inc parseBiggestInt(args, result, off) + if off < args.len: + case args[off] + of ',': + discard + of 'K': + result = result shl 10 + of 'M': + result = result shl 20 + of 'G': + result = result shl 30 + of 'P': + result = result shl 40 + else: + result = -1 + +type SessionId* = distinct int + ## Session identifier shared between + ## parent and child components. +proc `==`*(x, y: SessionId): bool {.borrow.} + +type + SessionRequestsParser = object + s: string + xp: XmlParser + args*: string ## Arguments of the session the parser is positioned on. + +proc initSessionRequestsParser*(sessionRequestsROM: string): SessionRequestsParser = + ## Initialize a parser of the Genode "session_requests" ROM. + result.s = sessionRequestsROM + result.args = "" + +proc argString*(srp: var SessionRequestsParser; key: string; default = ""): string = + ## Parse an string from the current session arguments. + if srp.args != "": + parseArgString(srp.args, key, default) + else: + default + +proc argInt*(srp: var SessionRequestsParser; key: string; default = -1): BiggestInt = + ## Parse an integer from the current session arguments. + if srp.args != "": + parseArgInt(srp.args, key, default) + else: + default + +proc streamThatCanBeClosed(srp: var SessionRequestsParser): Stream {.inline.} = + # Need to create a new stream because the XML parser closes the stream + # when the XML parser is closed. And the XML parser needs to be closed, + # but that shouldn't mess up the dataspace stream that is stuck to the ROM + # client. + srp.s.newStringStream + +proc next(srp: var SessionRequestsParser) {.inline.} = + next srp.xp + +proc skipRest(srp: var SessionRequestsParser) = + var depth = 1 + while depth > 0: + case srp.xp.kind + of xmlElementStart, xmlElementOpen: inc depth + of xmlElementEnd, xmlElementClose: dec depth + of xmlEof: break + of xmlError: raise newException(ValueError, srp.xp.errorMsg) + else: discard + next srp + +iterator create*(srp: var SessionRequestsParser): tuple[id: SessionId, service, label: TaintedString] = + ## Iterate over session creation requests. + let str = srp.streamThatCanBeClosed + open srp.xp, str, "session_requests" + next srp + block requestsLoop: + while true: + case srp.xp.kind: + of xmlElementOpen: + if srp.xp.elementName == "create": + next srp + block createLoop: + var result: tuple[id: SessionId, service, label: TaintedString] + srp.args.setLen 0 + while srp.xp.kind == xmlAttribute: + case srp.xp.attrKey: + of "id": + result.id = srp.xp.attrValue.parseInt.SessionId + of "service": + result.service = srp.xp.attrValue + of "label": + result.label = srp.xp.attrValue + next srp + while srp.xp.kind != xmlElementClose: # skip until ``>`` + next srp + next srp + if srp.xp.kind == xmlElementStart and srp.xp.elementName == "args": + next srp + while srp.xp.kind != xmlElementEnd: + if srp.xp.kind == xmlCharData: + srp.args.add srp.xp.charData + next srp + next srp + skipRest srp + yield result + else: + next srp + of xmlEof: + break requestsLoop + of xmlError: + raise newException(ValueError, srp.xp.errorMsg) + else: next srp # skip other events + close srp.xp + +iterator close*(srp: var SessionRequestsParser): SessionId = + ## Iterate over session close requests. + let str = srp.streamThatCanBeClosed + open srp.xp, str, "session_requests" + next srp + block requestsLoop: + while true: + case srp.xp.kind: + of xmlElementOpen: + case srp.xp.elementName: + of "close": + var id: SessionId + next srp + while srp.xp.kind == xmlAttribute: + if srp.xp.attrKey == "id": + id = srp.xp.attrValue.parseInt.SessionId + next srp + break + next srp + skipRest srp + yield id + else: + next srp + of xmlEof: + break requestsLoop + of xmlError: + raise newException(ValueError, srp.xp.errorMsg) + else: next srp # skip other events + close srp.xp diff --git a/include/nim/inputevent.nim b/include/nim/inputevent.nim index 1c17b4e..644655e 100644 --- a/include/nim/inputevent.nim +++ b/include/nim/inputevent.nim @@ -34,5 +34,6 @@ proc `$`*(c: KeyCode): string = $key_name(c) proc lookupKey*(s: string): KeyCode = result = KEY_UNKNOWN.KeyCode for k in 0..