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:
committed by
Norman Feske
parent
aa18c86ddf
commit
419503db1d
@@ -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
|
||||
|
||||
227
include/nim/genodeservers.nim
Normal file
227
include/nim/genodeservers.nim
Normal 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
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user