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
This commit is contained in:
Emery Hemingway
2018-01-27 19:49:25 +01:00
committed by Norman Feske
parent aa18c86ddf
commit 419503db1d
3 changed files with 238 additions and 4 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -34,5 +34,6 @@ proc `$`*(c: KeyCode): string = $key_name(c)
proc lookupKey*(s: string): KeyCode =
result = KEY_UNKNOWN.KeyCode
for k in 0..<KEY_MAX.KeyCode:
if $k == s:
return k
if $(k.KeyCode) == s:
result = k.KeyCode
break