sigil/overlay/nim.patch

2254 lines
80 KiB
Diff

From b50326bf9b53f02d7a1244dbf12a00b35a0ff71e Mon Sep 17 00:00:00 2001
From: Emery Hemingway <ehmry@posteo.net>
Date: Sun, 30 Jan 2022 10:20:04 +0100
Subject: [PATCH 1/8] nimNoLibc float formatting
---
lib/pure/strutils.nim | 6 ++-
lib/system/formatfloat.nim | 76 +++++++++++++++++++-------------------
lib/system/strmantle.nim | 67 ++++++++++++++++++---------------
3 files changed, 80 insertions(+), 69 deletions(-)
diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim
index 361fa72ef..9c6dd5225 100644
--- a/lib/pure/strutils.nim
+++ b/lib/pure/strutils.nim
@@ -1839,7 +1839,7 @@ func find*(a: SkipTable, s, sub: string, start: Natural = 0, last = 0): int {.
inc skip, a[s[skip + subLast]]
return -1
-when not (defined(js) or defined(nimdoc) or defined(nimscript)):
+when not (defined(js) or defined(nimdoc) or defined(nimscript) or defined(nimNoLibc)):
func c_memchr(cstr: pointer, c: char, n: csize_t): pointer {.
importc: "memchr", header: "<string.h>".}
func c_strstr(haystack, needle: cstring): cstring {.
@@ -2327,7 +2327,7 @@ func validIdentifier*(s: string): bool {.rtl, extern: "nsuValidIdentifier".} =
# floating point formatting:
-when not defined(js):
+when not (defined(js) or defined(nimNoLibc)):
func c_sprintf(buf, frmt: cstring): cint {.header: "<stdio.h>",
importc: "sprintf", varargs}
@@ -2377,6 +2377,8 @@ func formatBiggestFloat*(f: BiggestFloat, format: FloatFormatMode = ffDefault,
# Depending on the locale either dot or comma is produced,
# but nothing else is possible:
if result[i] in {'.', ','}: result[i] = decimalSep
+ elif defined(nimNoLibc):
+ discard # TODO
else:
const floatFormatToChar: array[FloatFormatMode, char] = ['g', 'f', 'e']
var
diff --git a/lib/system/formatfloat.nim b/lib/system/formatfloat.nim
index 3bcd3257b..c81c08dda 100644
--- a/lib/system/formatfloat.nim
+++ b/lib/system/formatfloat.nim
@@ -7,14 +7,14 @@
# distribution, for details about the copyright.
#
-proc c_memcpy(a, b: pointer, size: csize_t): pointer {.importc: "memcpy", header: "<string.h>", discardable.}
+from system/memory import nimCopyMem
proc addCstringN(result: var string, buf: cstring; buflen: int) =
# no nimvm support needed, so it doesn't need to be fast here either
let oldLen = result.len
let newLen = oldLen + buflen
result.setLen newLen
- c_memcpy(result[oldLen].addr, buf, buflen.csize_t)
+ nimCopyMem(result[oldLen].addr, buf, buflen.csize_t)
import dragonbox, schubfach
@@ -30,8 +30,9 @@ proc writeFloatToBufferRoundtrip*(buf: var array[65, char]; value: float32): int
result = float32ToChars(buf, value, forceTrailingDotZero=true)
buf[result] = '\0'
-proc c_sprintf(buf, frmt: cstring): cint {.header: "<stdio.h>",
- importc: "sprintf", varargs, noSideEffect.}
+when not defined(nimNoLibc):
+ proc c_sprintf(buf, frmt: cstring): cint {.header: "<stdio.h>",
+ importc: "sprintf", varargs, noSideEffect.}
proc writeToBuffer(buf: var array[65, char]; value: cstring) =
var i = 0
@@ -39,42 +40,43 @@ proc writeToBuffer(buf: var array[65, char]; value: cstring) =
buf[i] = value[i]
inc i
-proc writeFloatToBufferSprintf*(buf: var array[65, char]; value: BiggestFloat): int =
- ## This is the implementation to format floats.
- ##
- ## returns the amount of bytes written to `buf` not counting the
- ## terminating '\0' character.
- var n: int = c_sprintf(addr buf, "%.16g", value)
- var hasDot = false
- for i in 0..n-1:
- if buf[i] == ',':
- buf[i] = '.'
- hasDot = true
- elif buf[i] in {'a'..'z', 'A'..'Z', '.'}:
- hasDot = true
- if not hasDot:
- buf[n] = '.'
- buf[n+1] = '0'
- buf[n+2] = '\0'
- result = n + 2
- else:
- result = n
- # On Windows nice numbers like '1.#INF', '-1.#INF' or '1.#NAN' or 'nan(ind)'
- # of '-1.#IND' are produced.
- # We want to get rid of these here:
- if buf[n-1] in {'n', 'N', 'D', 'd', ')'}:
- writeToBuffer(buf, "nan")
- result = 3
- elif buf[n-1] == 'F':
- if buf[0] == '-':
- writeToBuffer(buf, "-inf")
- result = 4
+when defined(c_sprintf):
+ proc writeFloatToBufferSprintf*(buf: var array[65, char]; value: BiggestFloat): int =
+ ## This is the implementation to format floats.
+ ##
+ ## returns the amount of bytes written to `buf` not counting the
+ ## terminating '\0' character.
+ var n: int = c_sprintf(addr buf, "%.16g", value)
+ var hasDot = false
+ for i in 0..n-1:
+ if buf[i] == ',':
+ buf[i] = '.'
+ hasDot = true
+ elif buf[i] in {'a'..'z', 'A'..'Z', '.'}:
+ hasDot = true
+ if not hasDot:
+ buf[n] = '.'
+ buf[n+1] = '0'
+ buf[n+2] = '\0'
+ result = n + 2
else:
- writeToBuffer(buf, "inf")
+ result = n
+ # On Windows nice numbers like '1.#INF', '-1.#INF' or '1.#NAN' or 'nan(ind)'
+ # of '-1.#IND' are produced.
+ # We want to get rid of these here:
+ if buf[n-1] in {'n', 'N', 'D', 'd', ')'}:
+ writeToBuffer(buf, "nan")
result = 3
+ elif buf[n-1] == 'F':
+ if buf[0] == '-':
+ writeToBuffer(buf, "-inf")
+ result = 4
+ else:
+ writeToBuffer(buf, "inf")
+ result = 3
proc writeFloatToBuffer*(buf: var array[65, char]; value: BiggestFloat | float32): int {.inline.} =
- when defined(nimPreviewFloatRoundtrip):
+ when defined(nimPreviewFloatRoundtrip) or not defined(writeFloatToBufferSprintf):
writeFloatToBufferRoundtrip(buf, value)
else:
writeFloatToBufferSprintf(buf, value)
@@ -92,7 +94,7 @@ proc addFloatSprintf*(result: var string; x: float) =
doAssert false
else:
var buffer {.noinit.}: array[65, char]
- let n = writeFloatToBufferSprintf(buffer, x)
+ let n = writeFloatToBuffer(buffer, x)
result.addCstringN(cstring(buffer[0].addr), n)
proc nimFloatToString(a: float): cstring =
diff --git a/lib/system/strmantle.nim b/lib/system/strmantle.nim
index 9cf4f9e55..084e1db6b 100644
--- a/lib/system/strmantle.nim
+++ b/lib/system/strmantle.nim
@@ -43,8 +43,9 @@ proc hashString(s: string): int {.compilerproc.} =
h = h + h shl 15
result = cast[int](h)
-proc c_strtod(buf: cstring, endptr: ptr cstring): float64 {.
- importc: "strtod", header: "<stdlib.h>", noSideEffect.}
+when not defined(nimNoLibc):
+ proc c_strtod(buf: cstring, endptr: ptr cstring): float64 {.
+ importc: "strtod", header: "<stdlib.h>", noSideEffect.}
const
IdentChars = {'a'..'z', 'A'..'Z', '0'..'9', '_'}
@@ -180,36 +181,42 @@ proc nimParseBiggestFloat(s: string, number: var BiggestFloat,
number = sign * integer.float * powtens[slop] * powtens[absExponent-slop]
return i - start
- # if failed: slow path with strtod.
- var t: array[500, char] # flaviu says: 325 is the longest reasonable literal
- var ti = 0
- let maxlen = t.high - "e+000".len # reserve enough space for exponent
-
- let endPos = i
- result = endPos - start
- i = start
- # re-parse without error checking, any error should be handled by the code above.
- if i < endPos and s[i] == '.': i.inc
- while i < endPos and s[i] in {'0'..'9','+','-'}:
- if ti < maxlen:
- t[ti] = s[i]; inc(ti)
- inc(i)
- while i < endPos and s[i] in {'.', '_'}: # skip underscore and decimal point
+ when defined(c_strtod):
+ # if failed: slow path with strtod.
+ var t: array[500, char] # flaviu says: 325 is the longest reasonable literal
+ var ti = 0
+ let maxlen = t.high - "e+000".len # reserve enough space for exponent
+
+ let endPos = i
+ result = endPos - start
+ i = start
+ # re-parse without error checking, any error should be handled by the code above.
+ if i < endPos and s[i] == '.': i.inc
+ while i < endPos and s[i] in {'0'..'9','+','-'}:
+ if ti < maxlen:
+ t[ti] = s[i]; inc(ti)
inc(i)
+ while i < endPos and s[i] in {'.', '_'}: # skip underscore and decimal point
+ inc(i)
- # insert exponent
- t[ti] = 'E'
- inc(ti)
- t[ti] = if expNegative: '-' else: '+'
- inc(ti, 4)
-
- # insert adjusted exponent
- t[ti-1] = ('0'.ord + absExponent mod 10).char
- absExponent = absExponent div 10
- t[ti-2] = ('0'.ord + absExponent mod 10).char
- absExponent = absExponent div 10
- t[ti-3] = ('0'.ord + absExponent mod 10).char
- number = c_strtod(addr t, nil)
+ # insert exponent
+ t[ti] = 'E'
+ inc(ti)
+ t[ti] = if expNegative: '-' else: '+'
+ inc(ti, 4)
+
+ # insert adjusted exponent
+ t[ti-1] = ('0'.ord + absExponent mod 10).char
+ absExponent = absExponent div 10
+ t[ti-2] = ('0'.ord + absExponent mod 10).char
+ absExponent = absExponent div 10
+ t[ti-3] = ('0'.ord + absExponent mod 10).char
+ number = c_strtod(addr t, nil)
+ else:
+ number = NaN
+ raise newException(
+ FloatInexactDefect,
+ "insufficent precision in platform-specific parser")
when defined(nimHasInvariant):
{.pop.} # staticBoundChecks
--
2.37.2
From e7d190166a317d5a2d73bdee5777cff826319f61 Mon Sep 17 00:00:00 2001
From: Emery Hemingway <ehmry@posteo.net>
Date: Tue, 15 Feb 2022 16:28:41 -0600
Subject: [PATCH 2/8] Massive stub-out for nimNoLibc
---
lib/pure/asyncfile.nim | 2 +-
lib/pure/dynlib.nim | 16 ++--
lib/pure/os.nim | 183 +++++++++++++++++++++++----------------
lib/pure/osproc.nim | 2 +-
lib/pure/reservedmem.nim | 2 +-
lib/pure/segfaults.nim | 2 +-
lib/pure/selectors.nim | 7 +-
lib/pure/streams.nim | 2 +-
lib/pure/terminal.nim | 2 +-
lib/pure/unittest.nim | 2 +-
lib/std/tempfiles.nim | 2 +-
lib/system/dyncalls.nim | 36 ++++----
lib/system/gc_common.nim | 4 +-
lib/system/mmdisp.nim | 2 +-
lib/system/strmantle.nim | 2 +-
lib/system/syslocks.nim | 2 +-
16 files changed, 153 insertions(+), 115 deletions(-)
diff --git a/lib/pure/asyncfile.nim b/lib/pure/asyncfile.nim
index 222a89b97..6c5fd8a92 100644
--- a/lib/pure/asyncfile.nim
+++ b/lib/pure/asyncfile.nim
@@ -28,7 +28,7 @@ import asyncdispatch, os
when defined(windows) or defined(nimdoc):
import winlean
-else:
+elif defined(posix):
import posix
type
diff --git a/lib/pure/dynlib.nim b/lib/pure/dynlib.nim
index 48fd91b8f..b5567a5af 100644
--- a/lib/pure/dynlib.nim
+++ b/lib/pure/dynlib.nim
@@ -118,7 +118,7 @@ when defined(posix) and not defined(nintendoswitch):
proc unloadLib(lib: LibHandle) = discard dlclose(lib)
proc symAddr(lib: LibHandle, name: cstring): pointer = dlsym(lib, name)
-elif defined(nintendoswitch):
+elif defined(nintendoswitch) or defined(nimNoLibc):
#
# =========================================================================
# Nintendo switch DevkitPro sdk does not have these. Raise an error if called.
@@ -126,19 +126,19 @@ elif defined(nintendoswitch):
#
proc dlclose(lib: LibHandle) =
- raise newException(OSError, "dlclose not implemented on Nintendo Switch!")
+ raise newException(OSError, "dlclose not implemented on this platform!")
proc dlopen(path: cstring, mode: int): LibHandle =
- raise newException(OSError, "dlopen not implemented on Nintendo Switch!")
+ raise newException(OSError, "dlopen not implemented on this platform!")
proc dlsym(lib: LibHandle, name: cstring): pointer =
- raise newException(OSError, "dlsym not implemented on Nintendo Switch!")
+ raise newException(OSError, "dlsym not implemented on this platform!")
proc loadLib(path: string, global_symbols = false): LibHandle =
- raise newException(OSError, "loadLib not implemented on Nintendo Switch!")
+ raise newException(OSError, "loadLib not implemented on this platform!")
proc loadLib(): LibHandle =
- raise newException(OSError, "loadLib not implemented on Nintendo Switch!")
+ raise newException(OSError, "loadLib not implemented on this platform!")
proc unloadLib(lib: LibHandle) =
- raise newException(OSError, "unloadLib not implemented on Nintendo Switch!")
+ raise newException(OSError, "unloadLib not implemented on this platform!")
proc symAddr(lib: LibHandle, name: cstring): pointer =
- raise newException(OSError, "symAddr not implemented on Nintendo Switch!")
+ raise newException(OSError, "symAddr not implemented on this platform!")
elif defined(genode):
#
diff --git a/lib/pure/os.nim b/lib/pure/os.nim
index 24945095f..8674b5e05 100644
--- a/lib/pure/os.nim
+++ b/lib/pure/os.nim
@@ -36,6 +36,9 @@ import strutils, pathnorm
const weirdTarget = defined(nimscript) or defined(js)
+const fileSystemMissing = not defined(nimdoc) and (
+ defined(fileSystemMissing) or (defined(genode) and not defined(posix)))
+
since (1, 1):
const
invalidFilenameChars* = {'/', '\\', ':', '*', '?', '"', '<', '>', '|', '^', '\0'} ## \
@@ -1163,7 +1166,8 @@ proc fileExists*(filename: string): bool {.rtl, extern: "nos$1",
## See also:
## * `dirExists proc <#dirExists,string>`_
## * `symlinkExists proc <#symlinkExists,string>`_
- when defined(windows):
+ when fileSystemMissing: discard
+ elif defined(windows):
when useWinUnicode:
wrapUnary(a, getFileAttributesW, filename)
else:
@@ -1182,7 +1186,8 @@ proc dirExists*(dir: string): bool {.rtl, extern: "nos$1", tags: [ReadDirEffect]
## See also:
## * `fileExists proc <#fileExists,string>`_
## * `symlinkExists proc <#symlinkExists,string>`_
- when defined(windows):
+ when fileSystemMissing: discard
+ elif defined(windows):
when useWinUnicode:
wrapUnary(a, getFileAttributesW, dir)
else:
@@ -1202,7 +1207,8 @@ proc symlinkExists*(link: string): bool {.rtl, extern: "nos$1",
## See also:
## * `fileExists proc <#fileExists,string>`_
## * `dirExists proc <#dirExists,string>`_
- when defined(windows):
+ when fileSystemMissing: discard
+ elif defined(windows):
when useWinUnicode:
wrapUnary(a, getFileAttributesW, link)
else:
@@ -1237,46 +1243,48 @@ proc findExe*(exe: string, followSymlinks: bool = true;
## meets the actual file. This behavior can be disabled if desired
## by setting `followSymlinks = false`.
- if exe.len == 0: return
- template checkCurrentDir() =
- for ext in extensions:
- result = addFileExt(exe, ext)
- if fileExists(result): return
- when defined(posix):
- if '/' in exe: checkCurrentDir()
+ when fileSystemMissing: discard
else:
- checkCurrentDir()
- let path = getEnv("PATH")
- for candidate in split(path, PathSep):
- if candidate.len == 0: continue
- when defined(windows):
- var x = (if candidate[0] == '"' and candidate[^1] == '"':
- substr(candidate, 1, candidate.len-2) else: candidate) /
- exe
+ if exe.len == 0: return
+ template checkCurrentDir() =
+ for ext in extensions:
+ result = addFileExt(exe, ext)
+ if fileExists(result): return
+ when defined(posix):
+ if '/' in exe: checkCurrentDir()
else:
- var x = expandTilde(candidate) / exe
- for ext in extensions:
- var x = addFileExt(x, ext)
- if fileExists(x):
- when not defined(windows):
- while followSymlinks: # doubles as if here
- if x.symlinkExists:
- var r = newString(maxSymlinkLen)
- var len = readlink(x, r, maxSymlinkLen)
- if len < 0:
- raiseOSError(osLastError(), exe)
- if len > maxSymlinkLen:
- r = newString(len+1)
- len = readlink(x, r, len)
- setLen(r, len)
- if isAbsolute(r):
- x = r
+ checkCurrentDir()
+ let path = getEnv("PATH")
+ for candidate in split(path, PathSep):
+ if candidate.len == 0: continue
+ when defined(windows):
+ var x = (if candidate[0] == '"' and candidate[^1] == '"':
+ substr(candidate, 1, candidate.len-2) else: candidate) /
+ exe
+ else:
+ var x = expandTilde(candidate) / exe
+ for ext in extensions:
+ var x = addFileExt(x, ext)
+ if fileExists(x):
+ when not defined(windows):
+ while followSymlinks: # doubles as if here
+ if x.symlinkExists:
+ var r = newString(maxSymlinkLen)
+ var len = readlink(x, r, maxSymlinkLen)
+ if len < 0:
+ raiseOSError(osLastError(), exe)
+ if len > maxSymlinkLen:
+ r = newString(len+1)
+ len = readlink(x, r, len)
+ setLen(r, len)
+ if isAbsolute(r):
+ x = r
+ else:
+ x = parentDir(x) / r
else:
- x = parentDir(x) / r
- else:
- break
- return x
- result = ""
+ break
+ return x
+ result = ""
when weirdTarget:
const times = "fake const"
@@ -1289,7 +1297,8 @@ proc getLastModificationTime*(file: string): times.Time {.rtl, extern: "nos$1",
## * `getLastAccessTime proc <#getLastAccessTime,string>`_
## * `getCreationTime proc <#getCreationTime,string>`_
## * `fileNewer proc <#fileNewer,string,string>`_
- when defined(posix):
+ when fileSystemMissing: discard
+ elif defined(posix):
var res: Stat
if stat(file, res) < 0'i32: raiseOSError(osLastError(), file)
result = res.st_mtim.toTime
@@ -1307,7 +1316,8 @@ proc getLastAccessTime*(file: string): times.Time {.rtl, extern: "nos$1", noWeir
## * `getLastModificationTime proc <#getLastModificationTime,string>`_
## * `getCreationTime proc <#getCreationTime,string>`_
## * `fileNewer proc <#fileNewer,string,string>`_
- when defined(posix):
+ when fileSystemMissing: discard
+ elif defined(posix):
var res: Stat
if stat(file, res) < 0'i32: raiseOSError(osLastError(), file)
result = res.st_atim.toTime
@@ -1329,7 +1339,8 @@ proc getCreationTime*(file: string): times.Time {.rtl, extern: "nos$1", noWeirdT
## * `getLastModificationTime proc <#getLastModificationTime,string>`_
## * `getLastAccessTime proc <#getLastAccessTime,string>`_
## * `fileNewer proc <#fileNewer,string,string>`_
- when defined(posix):
+ when fileSystemMissing: discard
+ elif defined(posix):
var res: Stat
if stat(file, res) < 0'i32: raiseOSError(osLastError(), file)
result = res.st_ctim.toTime
@@ -1348,7 +1359,8 @@ proc fileNewer*(a, b: string): bool {.rtl, extern: "nos$1", noWeirdTarget.} =
## * `getLastModificationTime proc <#getLastModificationTime,string>`_
## * `getLastAccessTime proc <#getLastAccessTime,string>`_
## * `getCreationTime proc <#getCreationTime,string>`_
- when defined(posix):
+ when fileSystemMissing: discard
+ elif defined(posix):
# If we don't have access to nanosecond resolution, use '>='
when not StatHasNanoseconds:
result = getLastModificationTime(a) >= getLastModificationTime(b)
@@ -1371,7 +1383,8 @@ when not defined(nimscript):
## * `setCurrentDir proc <#setCurrentDir,string>`_
## * `currentSourcePath template <system.html#currentSourcePath.t>`_
## * `getProjectPath proc <macros.html#getProjectPath>`_
- when defined(nodejs):
+ when fileSystemMissing: discard
+ elif defined(nodejs):
var ret: cstring
{.emit: "`ret` = process.cwd();".}
return $ret
@@ -1428,7 +1441,8 @@ proc setCurrentDir*(newDir: string) {.inline, tags: [], noWeirdTarget.} =
## * `getConfigDir proc <#getConfigDir>`_
## * `getTempDir proc <#getTempDir>`_
## * `getCurrentDir proc <#getCurrentDir>`_
- when defined(windows):
+ when fileSystemMissing: discard
+ elif defined(windows):
when useWinUnicode:
if setCurrentDirectoryW(newWideCString(newDir)) == 0'i32:
raiseOSError(osLastError(), newDir)
@@ -1565,7 +1579,8 @@ proc sameFile*(path1, path2: string): bool {.rtl, extern: "nos$1",
##
## See also:
## * `sameFileContent proc <#sameFileContent,string,string>`_
- when defined(windows):
+ when fileSystemMissing: discard
+ elif defined(windows):
var success = true
var f1 = openHandle(path1)
var f2 = openHandle(path2)
@@ -1625,7 +1640,8 @@ proc getFilePermissions*(filename: string): set[FilePermission] {.
## See also:
## * `setFilePermissions proc <#setFilePermissions,string,set[FilePermission]>`_
## * `FilePermission enum <#FilePermission>`_
- when defined(posix):
+ when fileSystemMissing: discard
+ elif defined(posix):
var a: Stat
if stat(filename, a) < 0'i32: raiseOSError(osLastError(), filename)
result = {}
@@ -1671,7 +1687,8 @@ proc setFilePermissions*(filename: string, permissions: set[FilePermission],
## See also:
## * `getFilePermissions <#getFilePermissions,string>`_
## * `FilePermission enum <#FilePermission>`_
- when defined(posix):
+ when fileSystemMissing: discard
+ elif defined(posix):
var p = 0.Mode
if fpUserRead in permissions: p = p or S_IRUSR.Mode
if fpUserWrite in permissions: p = p or S_IWUSR.Mode
@@ -1750,7 +1767,8 @@ proc createSymlink*(src, dest: string) {.noWeirdTarget.} =
## * `createHardlink proc <#createHardlink,string,string>`_
## * `expandSymlink proc <#expandSymlink,string>`_
- when defined(windows):
+ when fileSystemMissing: discard
+ elif defined(windows):
const SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE = 2
# allows anyone with developer mode on to create a link
let flag = dirExists(src).int32 or SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE
@@ -1773,7 +1791,8 @@ proc expandSymlink*(symlinkPath: string): string {.noWeirdTarget.} =
##
## See also:
## * `createSymlink proc <#createSymlink,string,string>`_
- when defined(windows):
+ when fileSystemMissing: discard
+ elif defined(windows):
result = symlinkPath
else:
result = newString(maxSymlinkLen)
@@ -1950,7 +1969,8 @@ proc tryRemoveFile*(file: string): bool {.rtl, extern: "nos$1", tags: [WriteDirE
## * `removeFile proc <#removeFile,string>`_
## * `moveFile proc <#moveFile,string,string>`_
result = true
- when defined(windows):
+ when fileSystemMissing: discard
+ elif defined(windows):
when useWinUnicode:
let f = newWideCString(file)
else:
@@ -1991,7 +2011,8 @@ proc tryMoveFSObject(source, dest: string, isDir: bool): bool {.noWeirdTarget.}
## Returns false in case of `EXDEV` error or `AccessDeniedError` on windows (if `isDir` is true).
## In case of other errors `OSError` is raised.
## Returns true in case of success.
- when defined(windows):
+ when fileSystemMissing: discard
+ elif defined(windows):
when useWinUnicode:
let s = newWideCString(source)
let d = newWideCString(dest)
@@ -2001,16 +2022,18 @@ proc tryMoveFSObject(source, dest: string, isDir: bool): bool {.noWeirdTarget.}
else:
result = c_rename(source, dest) == 0'i32
- if not result:
- let err = osLastError()
- let isAccessDeniedError =
- when defined(windows):
- const AccessDeniedError = OSErrorCode(5)
- isDir and err == AccessDeniedError
- else:
- err == EXDEV.OSErrorCode
- if not isAccessDeniedError:
- raiseOSError(err, $(source, dest))
+ when fileSystemMissing: discard
+ else:
+ if not result:
+ let err = osLastError()
+ let isAccessDeniedError =
+ when defined(windows):
+ const AccessDeniedError = OSErrorCode(5)
+ isDir and err == AccessDeniedError
+ else:
+ err == EXDEV.OSErrorCode
+ if not isAccessDeniedError:
+ raiseOSError(err, $(source, dest))
proc moveFile*(source, dest: string) {.rtl, extern: "nos$1",
tags: [ReadDirEffect, ReadIOEffect, WriteIOEffect], noWeirdTarget.} =
@@ -2093,7 +2116,8 @@ template defaultWalkFilter(item): bool =
template walkCommon(pattern: string, filter) =
## Common code for getting the files and directories with the
## specified `pattern`
- when defined(windows):
+ when fileSystemMissing: discard
+ elif defined(windows):
var
f: WIN32_FIND_DATA
res: int
@@ -2189,7 +2213,8 @@ proc expandFilename*(filename: string): string {.rtl, extern: "nos$1",
## Returns the full (`absolute`:idx:) path of an existing file `filename`.
##
## Raises `OSError` in case of an error. Follows symlinks.
- when defined(windows):
+ when fileSystemMissing: discard
+ elif defined(windows):
var bufsize = MAX_PATH.int32
when useWinUnicode:
var unused: WideCString = nil
@@ -2302,7 +2327,8 @@ iterator walkDir*(dir: string; relative = false, checkDir = false):
for k, v in items(staticWalkDir(dir, relative)):
yield (k, v)
else:
- when weirdTarget:
+ when fileSystemMissing: discard
+ elif weirdTarget:
for k, v in items(staticWalkDir(dir, relative)):
yield (k, v)
elif defined(windows):
@@ -2425,7 +2451,8 @@ iterator walkDirRec*(dir: string,
# Future work can provide a way to customize this and do error reporting.
proc rawRemoveDir(dir: string) {.noWeirdTarget.} =
- when defined(windows):
+ when fileSystemMissing: discard
+ elif defined(windows):
when useWinUnicode:
wrapUnary(res, removeDirectoryW, dir)
else:
@@ -2468,7 +2495,8 @@ proc rawCreateDir(dir: string): bool {.noWeirdTarget.} =
#
# This is a thin wrapper over mkDir (or alternatives on other systems),
# so in case of a pre-existing path we don't check that it is a directory.
- when defined(solaris):
+ when fileSystemMissing: discard
+ elif defined(solaris):
let res = mkdir(dir, 0o777)
if res == 0'i32:
result = true
@@ -2618,7 +2646,8 @@ proc createHardlink*(src, dest: string) {.noWeirdTarget.} =
##
## See also:
## * `createSymlink proc <#createSymlink,string,string>`_
- when defined(windows):
+ when fileSystemMissing: discard
+ elif defined(windows):
when useWinUnicode:
var wSrc = newWideCString(src)
var wDst = newWideCString(dest)
@@ -3217,7 +3246,8 @@ proc getFileSize*(file: string): BiggestInt {.rtl, extern: "nos$1",
tags: [ReadIOEffect], noWeirdTarget.} =
## Returns the file size of `file` (in bytes). ``OSError`` is
## raised in case of an error.
- when defined(windows):
+ when fileSystemMissing: discard
+ elif defined(windows):
var a: WIN32_FIND_DATA
var resA = findFirstFile(file, a)
if resA == -1: raiseOSError(osLastError(), file)
@@ -3234,6 +3264,10 @@ when defined(windows) or weirdTarget:
type
DeviceId* = int32
FileId* = int64
+elif fileSystemMissing:
+ type
+ DeviceId* = int
+ FileId* = int
else:
type
DeviceId* = Dev
@@ -3344,7 +3378,8 @@ proc getFileInfo*(handle: FileHandle): FileInfo {.noWeirdTarget.} =
## * `getFileInfo(path) proc <#getFileInfo,string>`_
# Done: ID, Kind, Size, Permissions, Link Count
- when defined(windows):
+ when fileSystemMissing: discard
+ elif defined(windows):
var rawInfo: BY_HANDLE_FILE_INFORMATION
# We have to use the super special '_get_osfhandle' call (wrapped above)
# To transform the C file descriptor to a native file handle.
@@ -3386,7 +3421,8 @@ proc getFileInfo*(path: string, followSymlink = true): FileInfo {.noWeirdTarget.
## See also:
## * `getFileInfo(handle) proc <#getFileInfo,FileHandle>`_
## * `getFileInfo(file) proc <#getFileInfo,File>`_
- when defined(windows):
+ when fileSystemMissing: discard
+ elif defined(windows):
var
handle = openHandle(path, followSymlink)
rawInfo: BY_HANDLE_FILE_INFORMATION
@@ -3484,7 +3520,8 @@ proc getCurrentProcessId*(): int {.noWeirdTarget.} =
proc setLastModificationTime*(file: string, t: times.Time) {.noWeirdTarget.} =
## Sets the `file`'s last modification time. `OSError` is raised in case of
## an error.
- when defined(posix):
+ when fileSystemMissing: discard
+ elif defined(posix):
let unixt = posix.Time(t.toUnix)
let micro = convert(Nanoseconds, Microseconds, t.nanosecond)
var timevals = [Timeval(tv_sec: unixt, tv_usec: micro),
diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim
index 6a0ac9a8b..0c9b5b427 100644
--- a/lib/pure/osproc.nim
+++ b/lib/pure/osproc.nim
@@ -25,7 +25,7 @@ export quoteShell, quoteShellWindows, quoteShellPosix
when defined(windows):
import winlean
-else:
+elif defined(posix):
import posix
when defined(linux) and defined(useClone):
diff --git a/lib/pure/reservedmem.nim b/lib/pure/reservedmem.nim
index 232a2b383..55e194dcb 100644
--- a/lib/pure/reservedmem.nim
+++ b/lib/pure/reservedmem.nim
@@ -79,7 +79,7 @@ when defined(windows):
if r == cast[typeof(r)](0):
raiseOSError(osLastError())
-else:
+elif defined(posix):
import posix
let allocationGranularity = sysconf(SC_PAGESIZE)
diff --git a/lib/pure/segfaults.nim b/lib/pure/segfaults.nim
index b0eac2299..b1aec8265 100644
--- a/lib/pure/segfaults.nim
+++ b/lib/pure/segfaults.nim
@@ -64,7 +64,7 @@ when defined(windows):
{.pop.}
c_signal(SIGSEGV, segfaultHandler)
-else:
+elif defined(posix):
import posix
var sa: Sigaction
diff --git a/lib/pure/selectors.nim b/lib/pure/selectors.nim
index ec441f6da..21bef9187 100644
--- a/lib/pure/selectors.nim
+++ b/lib/pure/selectors.nim
@@ -27,7 +27,8 @@
##
## TODO: `/dev/poll`, `event ports` and filesystem events.
-import os, nativesockets
+when not defined(nimNoLibc):
+ import os, nativesockets
const hasThreadSupport = compileOption("threads") and defined(threadsafe)
@@ -232,7 +233,7 @@ when defined(nimdoc):
##
## For *poll* and *select* selectors `-1` is returned.
-else:
+elif not defined(nimNoLibc):
import strutils
when hasThreadSupport:
import locks
@@ -285,7 +286,7 @@ else:
proc setNonBlocking(fd: cint) {.inline.} =
setBlocking(fd.SocketHandle, false)
- when not defined(windows):
+ when not defined(windows) and defined(posix):
import posix
template setKey(s, pident, pevents, pparam, pdata: untyped) =
diff --git a/lib/pure/streams.nim b/lib/pure/streams.nim
index 99fe7e073..cf60c8f70 100644
--- a/lib/pure/streams.nim
+++ b/lib/pure/streams.nim
@@ -1467,7 +1467,7 @@ when false:
when defined(windows):
# do not import windows as this increases compile times:
discard
- else:
+ elif defined(posix):
import posix
proc hsSetPosition(s: FileHandleStream, pos: int) =
diff --git a/lib/pure/terminal.nim b/lib/pure/terminal.nim
index 25b1a4cdd..03b79bbee 100644
--- a/lib/pure/terminal.nim
+++ b/lib/pure/terminal.nim
@@ -247,7 +247,7 @@ when defined(windows):
let term = getTerminal()
if f == stderr: term.hStderr else: term.hStdout
-else:
+elif defined(posix):
import termios, posix, os, parseutils
proc setRaw(fd: FileHandle, time: cint = TCSAFLUSH) =
diff --git a/lib/pure/unittest.nim b/lib/pure/unittest.nim
index b1c01f8db..c715e12c3 100644
--- a/lib/pure/unittest.nim
+++ b/lib/pure/unittest.nim
@@ -113,7 +113,7 @@ import macros, strutils, streams, times, sets, sequtils
when declared(stdout):
import os
-const useTerminal = not defined(js)
+const useTerminal = not (defined(js) or defined(nimNoLibc))
when useTerminal:
import terminal
diff --git a/lib/std/tempfiles.nim b/lib/std/tempfiles.nim
index ce84c2a37..49b8f766e 100644
--- a/lib/std/tempfiles.nim
+++ b/lib/std/tempfiles.nim
@@ -41,7 +41,7 @@ when defined(windows):
proc close_osfandle(fd: cint): cint {.
importc: "_close", header: "<io.h>".}
-else:
+elif defined(posix):
import posix
proc c_fdopen(
diff --git a/lib/system/dyncalls.nim b/lib/system/dyncalls.nim
index 36c2c5fe1..39eb24fb0 100644
--- a/lib/system/dyncalls.nim
+++ b/lib/system/dyncalls.nim
@@ -20,15 +20,15 @@ const
proc nimLoadLibraryError(path: string) =
# carefully written to avoid memory allocation:
const prefix = "could not load: "
- cstderr.rawWrite(prefix)
- cstderr.rawWrite(path)
+ writeToStdErr(prefix)
+ writeToStdErr(path)
when not defined(nimDebugDlOpen) and not defined(windows):
- cstderr.rawWrite("\n(compile with -d:nimDebugDlOpen for more information)")
+ writeToStdErr("\n(compile with -d:nimDebugDlOpen for more information)")
when defined(windows):
const badExe = "\n(bad format; library may be wrong architecture)"
let loadError = GetLastError()
if loadError == ERROR_BAD_EXE_FORMAT:
- cstderr.rawWrite(badExe)
+ writeToStdErr(badExe)
when defined(guiapp):
# Because console output is not shown in GUI apps, display the error as a
# message box instead:
@@ -46,14 +46,14 @@ proc nimLoadLibraryError(path: string) =
if loadError == ERROR_BAD_EXE_FORMAT and msgLeft >= badExe.len:
copyMem(msg[msgIdx].addr, badExe.cstring, badExe.len)
discard MessageBoxA(nil, msg[0].addr, nil, 0)
- cstderr.rawWrite("\n")
+ writeToStdErr("\n")
quit(1)
proc procAddrError(name: cstring) {.compilerproc, nonReloadable, hcrInline.} =
# carefully written to avoid memory allocation:
- cstderr.rawWrite("could not import: ")
- cstderr.rawWrite(name)
- cstderr.rawWrite("\n")
+ writeToStdErr("could not import: ")
+ writeToStdErr(name)
+ writeToStdErr("\n")
quit(1)
# this code was inspired from Lua's source code:
@@ -98,8 +98,8 @@ when defined(posix):
when defined(nimDebugDlOpen):
let error = dlerror()
if error != nil:
- cstderr.rawWrite(error)
- cstderr.rawWrite("\n")
+ writeToStdErr(error)
+ writeToStdErr("\n")
proc nimGetProcAddr(lib: LibHandle, name: cstring): ProcAddr =
result = dlsym(lib, name)
@@ -176,22 +176,22 @@ elif defined(genode):
proc nimGetProcAddr(lib: LibHandle, name: cstring): ProcAddr =
raiseAssert("nimGetProcAddr not implemented")
-elif defined(nintendoswitch) or defined(freertos) or defined(zephyr):
+elif defined(nintendoswitch) or defined(freertos) or defined(zephyr) or defined(nimNoLibc):
proc nimUnloadLibrary(lib: LibHandle) =
- cstderr.rawWrite("nimUnLoadLibrary not implemented")
- cstderr.rawWrite("\n")
+ writeToStdErr("nimUnLoadLibrary not implemented")
+ writeToStdErr("\n")
quit(1)
proc nimLoadLibrary(path: string): LibHandle =
- cstderr.rawWrite("nimLoadLibrary not implemented")
- cstderr.rawWrite("\n")
+ writeToStdErr("nimLoadLibrary not implemented")
+ writeToStdErr("\n")
quit(1)
proc nimGetProcAddr(lib: LibHandle, name: cstring): ProcAddr =
- cstderr.rawWrite("nimGetProAddr not implemented")
- cstderr.rawWrite(name)
- cstderr.rawWrite("\n")
+ writeToStdErr("nimGetProAddr not implemented")
+ writeToStdErr(name)
+ writeToStdErr("\n")
quit(1)
else:
diff --git a/lib/system/gc_common.nim b/lib/system/gc_common.nim
index ea8857ece..4fff6d50f 100644
--- a/lib/system/gc_common.nim
+++ b/lib/system/gc_common.nim
@@ -471,7 +471,7 @@ proc nimRegisterGlobalMarker(markerProc: GlobalMarkerProc) {.compilerproc.} =
globalMarkers[globalMarkersLen] = markerProc
inc globalMarkersLen
else:
- cstderr.rawWrite("[GC] cannot register global variable; too many global variables")
+ writeToStdErr("[GC] cannot register global variable; too many global variables")
quit 1
proc nimRegisterThreadLocalMarker(markerProc: GlobalMarkerProc) {.compilerproc.} =
@@ -479,5 +479,5 @@ proc nimRegisterThreadLocalMarker(markerProc: GlobalMarkerProc) {.compilerproc.}
threadLocalMarkers[threadLocalMarkersLen] = markerProc
inc threadLocalMarkersLen
else:
- cstderr.rawWrite("[GC] cannot register thread local variable; too many thread local variables")
+ writeToStdErr("[GC] cannot register thread local variable; too many thread local variables")
quit 1
diff --git a/lib/system/mmdisp.nim b/lib/system/mmdisp.nim
index 3317ba627..422618c0f 100644
--- a/lib/system/mmdisp.nim
+++ b/lib/system/mmdisp.nim
@@ -45,7 +45,7 @@ else:
proc raiseOutOfMem() {.noinline.} =
if outOfMemHook != nil: outOfMemHook()
- cstderr.rawWrite("out of memory\n")
+ writeToStdErr("out of memory\n")
quit(1)
when defined(boehmgc):
diff --git a/lib/system/strmantle.nim b/lib/system/strmantle.nim
index 084e1db6b..2521fab9f 100644
--- a/lib/system/strmantle.nim
+++ b/lib/system/strmantle.nim
@@ -181,7 +181,7 @@ proc nimParseBiggestFloat(s: string, number: var BiggestFloat,
number = sign * integer.float * powtens[slop] * powtens[absExponent-slop]
return i - start
- when defined(c_strtod):
+ when not defined(nimNoLibc):
# if failed: slow path with strtod.
var t: array[500, char] # flaviu says: 325 is the longest reasonable literal
var ti = 0
diff --git a/lib/system/syslocks.nim b/lib/system/syslocks.nim
index 2f0c8b0ba..0b0324def 100644
--- a/lib/system/syslocks.nim
+++ b/lib/system/syslocks.nim
@@ -97,7 +97,7 @@ elif defined(genode):
proc broadcastSysCond(cond: var SysCond) {.
noSideEffect, importcpp.}
-else:
+elif defined(posix):
type
SysLockObj {.importc: "pthread_mutex_t", pure, final,
header: """#include <sys/types.h>
--
2.37.2
From 11e259afe417e39fda794c83304cfd815da2dfd6 Mon Sep 17 00:00:00 2001
From: Emery Hemingway <ehmry@posteo.net>
Date: Thu, 26 May 2022 12:52:54 -0500
Subject: [PATCH 3/8] times: Genode native time
---
lib/genode/constructibles.nim | 9 +++++++++
lib/genode/env.nim | 4 ++--
lib/genode/rtc.nim | 21 +++++++++++++++++++++
lib/pure/os.nim | 2 ++
lib/pure/times.nim | 34 +++++++++++++++++++++++++++++++---
5 files changed, 65 insertions(+), 5 deletions(-)
create mode 100644 lib/genode/constructibles.nim
create mode 100644 lib/genode/rtc.nim
diff --git a/lib/genode/constructibles.nim b/lib/genode/constructibles.nim
new file mode 100644
index 000000000..8bf6f5162
--- /dev/null
+++ b/lib/genode/constructibles.nim
@@ -0,0 +1,9 @@
+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.
diff --git a/lib/genode/env.nim b/lib/genode/env.nim
index ef4a25883..0c2f35a22 100644
--- a/lib/genode/env.nim
+++ b/lib/genode/env.nim
@@ -21,9 +21,9 @@ when not defined(genode):
type
GenodeEnvObj {.importcpp: "Genode::Env", header: "<base/env.h>", pure.} = object
- GenodeEnvPtr = ptr GenodeEnvObj
+ GenodeEnvPtr* = ptr GenodeEnvObj
const runtimeEnvSym = "nim_runtime_env"
when not defined(nimscript):
- var runtimeEnv {.importcpp: runtimeEnvSym.}: GenodeEnvPtr
+ var runtimeEnv* {.importcpp: runtimeEnvSym.}: GenodeEnvPtr
diff --git a/lib/genode/rtc.nim b/lib/genode/rtc.nim
new file mode 100644
index 000000000..d2f03454f
--- /dev/null
+++ b/lib/genode/rtc.nim
@@ -0,0 +1,21 @@
+import ./env, ./constructibles
+
+const rtcHeader = "<rtc_session/connection.h>"
+
+type
+ ConnectionBase {.importcpp: "Rtc::Connection", header: rtcHeader.} = object
+ Connection = Constructible[ConnectionBase]
+
+ Timestamp* {.importcpp: "Rtc::Timestamp", header: rtcHeader.} = object
+ microsecond*, second*, minute*, hour*, day*, month*, year*: cuint
+
+proc construct(c: Connection; env: GenodeEnvPtr; label: cstring) {.
+ importcpp: "#.construct(*#, @)", tags: [IOEffect].}
+
+proc timestamp*(c: Connection): Timestamp {.
+ importcpp: "#->current_time()".}
+ # There is an RPC call here but its not listed effect.
+
+proc initRtcConnection*(env: GenodeEnvPtr; label = ""): Connection =
+ ## Open a new **RTC** connection.
+ result.construct(env, label)
diff --git a/lib/pure/os.nim b/lib/pure/os.nim
index 8674b5e05..a1f9c8451 100644
--- a/lib/pure/os.nim
+++ b/lib/pure/os.nim
@@ -62,6 +62,8 @@ elif defined(posix):
proc toTime(ts: Timespec): times.Time {.inline.} =
result = initTime(ts.tv_sec.int64, ts.tv_nsec.int)
+elif defined(genode):
+ import times
else:
{.error: "OS module not ported to your operating system!".}
diff --git a/lib/pure/times.nim b/lib/pure/times.nim
index 113f73d2a..a18c9be4f 100644
--- a/lib/pure/times.nim
+++ b/lib/pure/times.nim
@@ -252,6 +252,11 @@ elif defined(windows):
proc localtime(a1: var CTime): ptr Tm {.importc, header: "<time.h>", sideEffect.}
+elif defined(genode):
+ # Genode without POSIX
+ import ../genode/[env, rtc]
+ var rtcConn = initRtcConnection(runtimeEnv, "times")
+
type
Month* = enum ## Represents a month. Note that the enum starts at `1`,
## so `ord(month)` will give the month number in the
@@ -894,6 +899,9 @@ proc toWinTime*(t: Time): int64 =
## since `1601-01-01T00:00:00Z`).
result = t.seconds * rateDiff + epochDiff + t.nanosecond div 100
+when defined(genode):
+ proc `+=`*(t: var Time, b: TimeInterval) {.tags: [], gcsafe.}
+
proc getTime*(): Time {.tags: [TimeEffect], benign.} =
## Gets the current time as a `Time` with up to nanosecond resolution.
when defined(js):
@@ -915,6 +923,16 @@ proc getTime*(): Time {.tags: [TimeEffect], benign.} =
var f {.noinit.}: FILETIME
getSystemTimeAsFileTime(f)
result = fromWinTime(rdFileTime(f))
+ elif defined(genode):
+ var ts = rtcConn.timestamp()
+ result += TimeInterval(
+ microseconds: int ts.microsecond,
+ seconds: int ts.second,
+ minutes: int ts.minute,
+ hours: int ts.hour,
+ days: int ts.day,
+ months: int ts.month,
+ years: int ts.year)
proc `-`*(a, b: Time): Duration {.operator, extern: "ntDiffTime".} =
## Computes the duration between two points in time.
@@ -1222,7 +1240,7 @@ when defined(js):
result.time = adjTime + initDuration(seconds = result.utcOffset)
result.isDst = false
-else:
+elif defined(posix):
proc toAdjUnix(tm: Tm): int64 =
let epochDay = toEpochDay(tm.tm_mday, (tm.tm_mon + 1).Month,
tm.tm_year.int + 1900)
@@ -1284,6 +1302,13 @@ else:
result.utcOffset = finalOffset
result.isDst = dst
+elif defined(genode):
+ proc localZonedTimeFromTime(time: Time): ZonedTime {.benign.} =
+ result.time = time
+
+ proc localZonedTimeFromAdjTime(adjTime: Time): ZonedTime {.benign.} =
+ result.time = adjTime
+
proc utcTzInfo(time: Time): ZonedTime =
ZonedTime(utcOffset: 0, isDst: false, time: time)
@@ -1877,9 +1902,9 @@ proc parsePattern(input: string, pattern: FormatPattern, i: var int,
else:
result = false
of yy:
- # Assumes current century
+ # Assumes 21st century
var year = takeInt(2..2)
- var thisCen = now().year div 100
+ var thisCen = 20
parsed.year = some(thisCen*100 + year)
result = year > 0
of yyyy:
@@ -2608,6 +2633,9 @@ proc epochTime*(): float {.tags: [TimeEffect].} =
var secs = i64 div rateDiff
var subsecs = i64 mod rateDiff
result = toFloat(int(secs)) + toFloat(int(subsecs)) * 0.0000001
+ elif defined(genode):
+ var t = now().toTime
+ result = t.seconds.toBiggestFloat + t.nanosecond.toBiggestFloat * 0.000_000_000_1
elif defined(js):
result = newDate().getTime() / 1000
else:
--
2.37.2
From 7f3934aba501406c95d69ce271e5456c426f0ba6 Mon Sep 17 00:00:00 2001
From: Emery Hemingway <ehmry@posteo.net>
Date: Thu, 26 May 2022 13:05:06 -0500
Subject: [PATCH 4/8] os: add support for Genode without POSIX
---
lib/pure/os.nim | 20 ++++++++++++++------
1 file changed, 14 insertions(+), 6 deletions(-)
diff --git a/lib/pure/os.nim b/lib/pure/os.nim
index a1f9c8451..67d481e13 100644
--- a/lib/pure/os.nim
+++ b/lib/pure/os.nim
@@ -1755,6 +1755,8 @@ proc isAdmin*: bool {.noWeirdTarget.} =
if freeSid(administratorsGroup) != nil:
raiseOSError(osLastError(), "failed to free SID for Administrators group")
+ elif defined(genode): # non-POSIX
+ discard
else:
result = geteuid() == 0
@@ -3238,11 +3240,15 @@ proc sleep*(milsecs: int) {.rtl, extern: "nos$1", tags: [TimeEffect], noWeirdTar
## Sleeps `milsecs` milliseconds.
when defined(windows):
winlean.sleep(int32(milsecs))
- else:
+ elif defined(posix):
var a, b: Timespec
a.tv_sec = posix.Time(milsecs div 1000)
a.tv_nsec = (milsecs mod 1000) * 1000 * 1000
discard posix.nanosleep(a, b)
+ elif defined(genode):
+ raise newException(Defect, "blocking sleep not available on Genode")
+ else:
+ {.error: "sleep not ported to your operating system!".}
proc getFileSize*(file: string): BiggestInt {.rtl, extern: "nos$1",
tags: [ReadIOEffect], noWeirdTarget.} =
@@ -3266,14 +3272,14 @@ when defined(windows) or weirdTarget:
type
DeviceId* = int32
FileId* = int64
-elif fileSystemMissing:
- type
- DeviceId* = int
- FileId* = int
-else:
+elif defined(posix):
type
DeviceId* = Dev
FileId* = Ino
+else:
+ type
+ DeviceId* = int
+ FileId* = int
type
FileInfo* = object
@@ -3516,6 +3522,8 @@ proc getCurrentProcessId*(): int {.noWeirdTarget.} =
proc GetCurrentProcessId(): DWORD {.stdcall, dynlib: "kernel32",
importc: "GetCurrentProcessId".}
result = GetCurrentProcessId().int
+ elif defined(genode): # non-POSIX
+ discard
else:
result = getpid()
--
2.37.2
From df882bfdc80e35a09c6e5c8517eda9d54e91a0c4 Mon Sep 17 00:00:00 2001
From: Emery Hemingway <ehmry@posteo.net>
Date: Tue, 11 Oct 2022 14:31:21 -0500
Subject: [PATCH 5/8] Genode native modules
---
lib/genode/entrypoints.nim | 10 ++++
lib/genode/rtc.nim | 4 +-
lib/genode/signals.nim | 93 ++++++++++++++++++++++++++++++++++++++
lib/genode/timers.nim | 13 ++++++
lib/pure/times.nim | 2 +-
5 files changed, 119 insertions(+), 3 deletions(-)
create mode 100644 lib/genode/entrypoints.nim
create mode 100644 lib/genode/signals.nim
create mode 100644 lib/genode/timers.nim
diff --git a/lib/genode/entrypoints.nim b/lib/genode/entrypoints.nim
new file mode 100644
index 000000000..360aa274d
--- /dev/null
+++ b/lib/genode/entrypoints.nim
@@ -0,0 +1,10 @@
+type
+ EntrypointObj {.
+ importcpp: "Genode::Entrypoint",
+ header: "<base/entrypoint.h>",
+ pure.} = object
+ Entrypoint* = ptr EntrypointObj
+ ## Opaque Entrypoint object.
+
+proc ep*(env: GenodeEnvPtr): Entrypoint {.
+ importcpp: "(&#->ep())".}
diff --git a/lib/genode/rtc.nim b/lib/genode/rtc.nim
index d2f03454f..00a13049b 100644
--- a/lib/genode/rtc.nim
+++ b/lib/genode/rtc.nim
@@ -1,4 +1,4 @@
-import ./env, ./constructibles
+import ./constructibles
const rtcHeader = "<rtc_session/connection.h>"
@@ -9,7 +9,7 @@ type
Timestamp* {.importcpp: "Rtc::Timestamp", header: rtcHeader.} = object
microsecond*, second*, minute*, hour*, day*, month*, year*: cuint
-proc construct(c: Connection; env: GenodeEnvPtr; label: cstring) {.
+proc construct(c: Connection; env: GenodeEnv; label: cstring) {.
importcpp: "#.construct(*#, @)", tags: [IOEffect].}
proc timestamp*(c: Connection): Timestamp {.
diff --git a/lib/genode/signals.nim b/lib/genode/signals.nim
new file mode 100644
index 000000000..0e59db59c
--- /dev/null
+++ b/lib/genode/signals.nim
@@ -0,0 +1,93 @@
+import ./entrypoints, ./constructibles
+
+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()", tags: [IOEffect].}
+
+{.emit: """
+#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 SignalDispatcher; }
+
+struct Nim::SignalDispatcher
+{
+ /**
+ * Pointer to a Nim type
+ */
+ void *arg;
+
+ /**
+ * Call Nim with dispatcher argument
+ */
+ void handle_signal() {
+ Libc::with_libc([this] () { nimHandleSignal(arg); }); }
+
+ Genode::Signal_handler<SignalDispatcher> handler;
+
+ SignalDispatcher(Genode::Entrypoint *ep, void *arg)
+ : arg(arg), handler(*ep, *this, &SignalDispatcher::handle_signal) { }
+
+ Genode::Signal_context_capability cap() {
+ return handler; }
+};
+
+""".}
+
+
+type
+ HandlerProc = proc () {.closure, gcsafe.}
+
+ SignalDispatcherBase {.
+ importcpp: "Nim::SignalDispatcher", pure.} = object
+
+ SignalDispatcherCpp = Constructible[SignalDispatcherBase]
+
+ SignalDispatcherObj = object
+ cpp: SignalDispatcherCpp
+ cb: HandlerProc
+ ## Signal handling procedure called during dispatch.
+
+ SignalHandler* = ref SignalDispatcherObj
+ ## Nim object enclosing a Genode signal handler.
+
+{.deprecated: [SignalDispatcher: SignalHandler].}
+
+proc construct(cpp: SignalDispatcherCpp; ep: Entrypoint; sh: SignalHandler) {.importcpp.}
+
+proc cap(cpp: SignalDispatcherCpp): 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.
+ assert(not cb.isNil)
+ result = SignalHandler(cb: cb)
+ result.cpp.construct(ep, result)
+ GCref result
+ assert(not result.cb.isNil)
+
+proc dissolve*(sig: SignalHandler) =
+ ## Dissolve signal dispatcher from entrypoint.
+ destruct sig.cpp
+ GCunref sig
+
+proc cap*(sig: SignalHandler): SignalContextCapability =
+ ## Signal context capability. Can be delegated to external components.
+ assert(not sig.cb.isNil)
+ sig.cpp.cap
+
+proc nimHandleSignal(p: pointer) {.exportc.} =
+ ## C symbol invoked by entrypoint during signal dispatch.
+ let dispatch = cast[SignalDispatcher](p)
+ doAssert(not dispatch.cb.isNil)
+ dispatch.cb()
diff --git a/lib/genode/timers.nim b/lib/genode/timers.nim
new file mode 100644
index 000000000..d74643dde
--- /dev/null
+++ b/lib/genode/timers.nim
@@ -0,0 +1,13 @@
+import ./constructibles, ./signals
+
+const timerHeader = "<timer_session/connection.h>"
+
+type
+ ConnectionBase {.importcpp: "Timer::Connection", header: timerHeader.} = object
+ TimerConnection* = Constructible[ConnectionBase]
+
+proc construct*(conn: TimerConnection; env: GenodeEnvPtr; label: cstring) {.
+ importcpp: "#.construct(*#, @)", tags: [IOEffect].}
+
+proc sigh*(conn: TimerConnection; sigCap: SignalContextCapability) {.
+ importcpp: "#->sigh(#)", tags: [IOEffect].}
diff --git a/lib/pure/times.nim b/lib/pure/times.nim
index a18c9be4f..33074cf0c 100644
--- a/lib/pure/times.nim
+++ b/lib/pure/times.nim
@@ -254,7 +254,7 @@ elif defined(windows):
elif defined(genode):
# Genode without POSIX
- import ../genode/[env, rtc]
+ import ../genode/rtc
var rtcConn = initRtcConnection(runtimeEnv, "times")
type
--
2.37.2
From 8a6ded41fffe06befdd7236b8c87ebb770172239 Mon Sep 17 00:00:00 2001
From: Emery Hemingway <ehmry@posteo.net>
Date: Wed, 12 Oct 2022 16:35:57 -0500
Subject: [PATCH 6/8] asyncdispatch: milliseconds to Duration
Use the Duration type to avoid mismatches with schedulers that use
units of time other than the millisecond.
---
lib/pure/asyncdispatch.nim | 70 ++++++++++++++++++++++----------------
1 file changed, 40 insertions(+), 30 deletions(-)
diff --git a/lib/pure/asyncdispatch.nim b/lib/pure/asyncdispatch.nim
index 04dbf4e4a..3d0e374ff 100644
--- a/lib/pure/asyncdispatch.nim
+++ b/lib/pure/asyncdispatch.nim
@@ -213,7 +213,7 @@ type
proc processTimers(
p: PDispatcherBase, didSomeWork: var bool
-): Option[int] {.inline.} =
+): Option[Duration] {.inline.} =
# Pop the timers in the order in which they will expire (smaller `finishAt`).
var count = p.timers.len
let t = getMonoTime()
@@ -222,11 +222,11 @@ proc processTimers(
dec count
didSomeWork = true
- # Return the number of milliseconds in which the next timer will expire.
+ # Return the duration in which the next timer will expire.
if p.timers.len == 0: return
- let millisecs = (p.timers[0].finishAt - getMonoTime()).inMilliseconds
- return some(millisecs.int + 1)
+ let timeout = p.timers[0].finishAt - getMonoTime()
+ return some(timeout)
proc processPendingCallbacks(p: PDispatcherBase; didSomeWork: var bool) =
while p.callbacks.len > 0:
@@ -235,16 +235,17 @@ proc processPendingCallbacks(p: PDispatcherBase; didSomeWork: var bool) =
didSomeWork = true
proc adjustTimeout(
- p: PDispatcherBase, pollTimeout: int, nextTimer: Option[int]
-): int {.inline.} =
- if p.callbacks.len != 0:
- return 0
+ p: PDispatcherBase, pollTimeout: Duration, nextTimer: Option[Duration]
+): Duration {.inline.} =
+ if p.callbacks.len == 0:
+ if nextTimer.isNone() or pollTimeout < result:
+ return pollTimeout
- if nextTimer.isNone() or pollTimeout == -1:
- return pollTimeout
+ result = max(nextTimer.get(), Duration())
+ result = min(pollTimeout, result)
- result = max(nextTimer.get(), 0)
- result = min(pollTimeout, result)
+proc roundToMilliseconds(dur: Duration): int64 {.used.} =
+ inMilliseconds(dur + initDuration(microseconds = 500))
proc callSoon*(cbproc: proc () {.gcsafe.}) {.gcsafe.}
## Schedule `cbproc` to be called as soon as possible.
@@ -355,7 +356,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: Duration): bool =
let p = getGlobalDispatcher()
if p.handles.len == 0 and p.timers.len == 0 and p.callbacks.len == 0:
raise newException(ValueError,
@@ -365,8 +366,8 @@ when defined(windows) or defined(nimdoc):
let nextTimer = processTimers(p, result)
let at = adjustTimeout(p, timeout, nextTimer)
var llTimeout =
- if at == -1: winlean.INFINITE
- else: at.int32
+ if at < Duration(): winlean.INFINITE
+ else: at.roundToMilliseconds.int32
var lpNumberOfBytesTransferred: DWORD
var lpCompletionKey: ULONG_PTR
@@ -1337,7 +1338,7 @@ else:
)
- proc runOnce(timeout = 500): bool =
+ proc runOnce(timeout: Duration): bool =
let p = getGlobalDispatcher()
if p.selector.isEmpty() and p.timers.len == 0 and p.callbacks.len == 0:
raise newException(ValueError,
@@ -1347,7 +1348,7 @@ else:
var keys: array[64, ReadyKey]
let nextTimer = processTimers(p, result)
var count =
- p.selector.selectInto(adjustTimeout(p, timeout, nextTimer), keys)
+ p.selector.selectInto(adjustTimeout(p, timeout, nextTimer).roundToMilliseconds.int, keys)
for i in 0..<count:
let fd = keys[i].fd.AsyncFD
let events = keys[i].events
@@ -1634,7 +1635,7 @@ else:
data.readList.add(cb)
p.selector.registerEvent(SelectEvent(ev), data)
-proc drain*(timeout = 500) =
+proc drain*(timeout: Duration) =
## Waits for completion of **all** events and processes them. Raises `ValueError`
## if there are no pending operations. In contrast to `poll` this
## processes as many events as are available until the timeout has elapsed.
@@ -1642,16 +1643,20 @@ proc drain*(timeout = 500) =
let start = now()
while hasPendingOperations():
discard runOnce(curTimeout)
- curTimeout -= (now() - start).inMilliseconds.int
- if curTimeout < 0:
+ curTimeout -= now() - start
+ if curTimeout < Duration():
break
-proc poll*(timeout = 500) =
+proc drain*(timeoutMs = 500) {.inline.} = drain(initDuration(milliseconds = timeoutMs))
+
+proc poll*(timeout: Duration) =
## Waits for completion events and processes them. Raises `ValueError`
## if there are no pending operations. This runs the underlying OS
## `epoll`:idx: or `kqueue`:idx: primitive only once.
discard runOnce(timeout)
+proc poll*(timeoutMs = 500) {.inline.} = poll(initDuration(milliseconds = timeoutMs))
+
template createAsyncNativeSocketImpl(domain, sockType, protocol: untyped,
inheritable = defined(nimInheritHandles)) =
let handle = createNativeSocket(domain, sockType, protocol, inheritable)
@@ -1856,19 +1861,24 @@ proc connect*(socket: AsyncFD, address: string, port: Port,
socket.SocketHandle.bindToDomain(domain)
asyncAddrInfoLoop(aiList, socket)
-proc sleepAsync*(ms: int | float): owned(Future[void]) =
- ## Suspends the execution of the current async procedure for the next
- ## `ms` milliseconds.
+proc sleepAsync*(timeout: Duration): owned(Future[void]) =
+ ## Suspends the execution of the current async procedure for the given
+ ## duration.
var retFuture = newFuture[void]("sleepAsync")
let p = getGlobalDispatcher()
+ let deadline: Monotime = getMonoTime() + timeout
+ p.timers.push((deadline, retFuture))
+ return retFuture
+
+proc sleepAsync*(ms: int | float): owned(Future[void]) {.inline.} =
+ ## Suspends the execution of the current async procedure for the next
+ ## `ms` milliseconds.
when ms is int:
- p.timers.push((getMonoTime() + initDuration(milliseconds = ms), retFuture))
+ sleepAsync(initDuration(milliseconds = ms))
elif ms is float:
- let ns = (ms * 1_000_000).int64
- p.timers.push((getMonoTime() + initDuration(nanoseconds = ns), retFuture))
- return retFuture
+ sleepAsync(initDuration(nanoseconds = (ms * 1_000_000).int64))
-proc withTimeout*[T](fut: Future[T], timeout: int): owned(Future[bool]) =
+proc withTimeout*[T](fut: Future[T], timeoutMs: int): owned(Future[bool]) =
## Returns a future which will complete once `fut` completes or after
## `timeout` milliseconds has elapsed.
##
@@ -1877,7 +1887,7 @@ proc withTimeout*[T](fut: Future[T], timeout: int): owned(Future[bool]) =
## future will hold false.
var retFuture = newFuture[bool]("asyncdispatch.`withTimeout`")
- var timeoutFuture = sleepAsync(timeout)
+ var timeoutFuture = sleepAsync(initDuration(milliseconds = timeoutMs))
fut.callback =
proc () =
if not retFuture.finished:
--
2.37.2
From dd9fea0775173ccc35c093bb307e18141ec93f45 Mon Sep 17 00:00:00 2001
From: Emery Hemingway <ehmry@posteo.net>
Date: Wed, 12 Oct 2022 15:42:31 -0500
Subject: [PATCH 7/8] Genode: native (non-POSIX) asyndispatch
---
lib/genode/entrypoints.nim | 4 +
lib/genode/timers.nim | 21 +++
lib/genode_cpp/timeouts.h | 44 +++++
lib/pure/asyncdispatch.nim | 366 ++++++++++++++++++++++---------------
lib/pure/selectors.nim | 1 +
5 files changed, 292 insertions(+), 144 deletions(-)
create mode 100644 lib/genode_cpp/timeouts.h
diff --git a/lib/genode/entrypoints.nim b/lib/genode/entrypoints.nim
index 360aa274d..cdd90d083 100644
--- a/lib/genode/entrypoints.nim
+++ b/lib/genode/entrypoints.nim
@@ -8,3 +8,7 @@ type
proc ep*(env: GenodeEnvPtr): Entrypoint {.
importcpp: "(&#->ep())".}
+
+proc wait_and_dispatch_one_io_signal*(ep: Entrypoint) {.importcpp.}
+
+proc dispatch_pending_io_signal*(ep: Entrypoint): bool {.importcpp.}
diff --git a/lib/genode/timers.nim b/lib/genode/timers.nim
index d74643dde..0922a4d2b 100644
--- a/lib/genode/timers.nim
+++ b/lib/genode/timers.nim
@@ -1,4 +1,5 @@
import ./constructibles, ./signals
+from times import Duration
const timerHeader = "<timer_session/connection.h>"
@@ -11,3 +12,23 @@ proc construct*(conn: TimerConnection; env: GenodeEnvPtr; label: cstring) {.
proc sigh*(conn: TimerConnection; sigCap: SignalContextCapability) {.
importcpp: "#->sigh(#)", tags: [IOEffect].}
+
+type
+ TimeoutCallback*[T] = proc (state: T; us: uint64) {.cdecl.}
+ ## Callback type that accepts at state parameter and the elapsed timout duration in microseconds.
+ TimeoutHandler*[T] {.
+ importcpp: "Nim::Constructible_timeout_handler<T>",
+ header: "genode_cpp/timeouts.h"
+ .} = object
+
+proc construct*[T](handler: TimeoutHandler[T]; timer: TimerConnection; state: T; cb: TimeoutCallback) {.
+ importcpp: "#.construct(*#, @)".}
+ ## Construct a `TimeoutHandler` with a `TimerConnection` and a callback.
+
+proc schedule_us[T](handler: TimeoutHandler[T]; timeout: uint64) {.importcpp.}
+
+proc schedule*[T](handler: TimeoutHandler[T]; timeout: Duration) {.inline.} =
+ ## Schedule the handler to be invoked after a timeout.
+ let us = timeout.inMicroseconds
+ assert(us > 0, "Timeout cannot be less than 1 µs")
+ schedule_us(handler, uint64 us)
diff --git a/lib/genode_cpp/timeouts.h b/lib/genode_cpp/timeouts.h
new file mode 100644
index 000000000..1af4ca48d
--- /dev/null
+++ b/lib/genode_cpp/timeouts.h
@@ -0,0 +1,44 @@
+/*
+ *
+ * Nim's Runtime Library
+ * (c) Copyright 2022 Emery Hemingway
+ *
+ * See the file "copying.txt", included in this
+ * distribution, for details about the copyright.
+ *
+ */
+
+#ifndef _GENODE_CPP__TIMEOUTS_H_
+#define _GENODE_CPP__TIMEOUTS_H_
+
+/* Genode includes */
+#include <timer_session/connection.h>
+
+namespace Nim {
+
+ /**
+ * Class for calling a Nim callback from a Genode timer.
+ */
+ template <typename STATE>
+ struct Timeout_handler
+ {
+ Timer::One_shot_timeout<Timeout_handler> _timeout;
+ STATE _state;
+ void(*)(STATE, uint64_t) _callback;
+
+ Timeout_handler(Timer::Connection *timer, STATE _state, void(*callback)(STATE, uint64_t))
+ : _timeout(*timer, *this, &Timeout_handler::_handle_timeout)
+ , _state(state), _callback(callback)
+ { }
+
+ void _handle_timeout(Duration dur) { _callback(_state, dur.trunc_to_plain_us().value); }
+
+ void schedule_us(uint64_t us) { schedule(Duration(Microseconds(us))); }
+ };
+
+ template <typename STATE>
+ typedef Constructible<Nim::Timeout_handler<STATE>> Nim::Constructible_timeout_handler
+
+}
+
+#endif
diff --git a/lib/pure/asyncdispatch.nim b/lib/pure/asyncdispatch.nim
index 3d0e374ff..2e1d7b4f8 100644
--- a/lib/pure/asyncdispatch.nim
+++ b/lib/pure/asyncdispatch.nim
@@ -194,13 +194,18 @@
## ``none`` can be used when a library supports both a synchronous and
## asynchronous API, to disable the latter.
-import os, tables, strutils, times, heapqueue, options, asyncstreams
+import os, tables, strutils, times, heapqueue, options, asyncstreams, deques
import options, math, std/monotimes
import asyncfutures except callSoon
-import nativesockets, net, deques
+const socketsMissing = defined(genode) and not defined(posix)
+when not socketsMissing:
+ import nativesockets, net
+
+when not socketsMissing:
+ export Port, SocketFlag
+
-export Port, SocketFlag
export asyncfutures except callSoon
export asyncstreams
@@ -1129,6 +1134,75 @@ when defined(windows) or defined(nimdoc):
ev.hWaiter = pcd.waitFd
initAll()
+elif defined(genode) and not defined(posix):
+ import genode/[entrypoints, timers]
+ type PDispatcher* = ref object of PDispatcherBase
+ timer: TimerConnection
+ timeoutHandler: TimeoutHandler[PDispatcher]
+ progress: bool
+
+ proc asyncdispatch_timeout_callback(disp: PDispatcher; us: uint64) {.cdecl.} =
+ let nextTimer = processTimers(disp, disp.progress)
+ if nextTimer.isSome:
+ disp.timeoutHandler.schedule(get(nextTimer))
+ processPendingCallbacks(disp, disp.progress)
+
+ var gDisp: owned PDispatcher ## Global dispatcher
+
+ proc getGlobalDispatcher*(): PDispatcher =
+ if gDisp.isNil:
+ new gDisp
+ construct(gDisp.timer, runtimeEnv, "asyncdispatch")
+ construct(gDisp.timeoutHandler, gDisp.timer, gDisp, asyncdispatch_timeout_callback)
+ gDisp
+
+ proc hasPendingOperations*(): bool =
+ var p = getGlobalDispatcher()
+ p.timers.len != 0 or p.callbacks.len != 0
+
+ proc runOnce(timeout: Duration): bool =
+ var disp = getGlobalDispatcher()
+ if disp.timers.len == 0 and disp.callbacks.len == 0:
+ raise newException(Defect, "No timers registered in dispatcher.")
+ let nextTimeout = processTimers(disp, result)
+ processPendingCallbacks(disp, result)
+ if nextTimeout.isSome:
+ if result:
+ # schedule the next pending timer
+ disp.timeoutHandler.schedule(get nextTimeout)
+ else:
+ # schedule the next pender timer or timeout, whichever is earlier
+ disp.timeoutHandler.schedule(min(timeout, get nextTimeout))
+ elif not result:
+ # schedule the run timeout and dispatch I/O signals until something happens
+ disp.timeoutHandler.schedule(timeout)
+ let ioDeadline = getMonoTime() + timeout
+ disp.progress = false
+ while true:
+ runtimeEnv.ep.wait_and_dispatch_one_io_signal()
+ # wait for asyncdispatch_timeout_callback to be trigger by a signal
+ if disp.progress or getMonoTime() >= ioDeadline:
+ result = disp.progress
+ break
+
+ proc runOnce(timeout: int): bool {.inline} = runOnce(initDuration(milliseconds = timeout))
+
+ proc addTimer*(timeout: Duration, oneshot: bool, cb: proc() {.gcsafe.}) =
+ var
+ disp = getGlobalDispatcher()
+ fut = newFuture[void]("addTimer")
+ if oneshot:
+ fut.addCallback(cb)
+ else:
+ fut.addCallback:
+ cb()
+ addTimer(timeout, oneshot, cb)
+ # TODO: reuse the future?
+ disp.timers.push((getMonoTime() + timeout, fut))
+
+ proc addTimer*(timeout: int, oneshot: bool, cb: proc() {.gcsafe.}) {.inline.} =
+ addTimer(initDuration(milliseconds = timeout), oneshot, cb)
+
else:
import selectors
from posix import EINTR, EAGAIN, EINPROGRESS, EWOULDBLOCK, MSG_PEEK,
@@ -1668,16 +1742,17 @@ template createAsyncNativeSocketImpl(domain, sockType, protocol: untyped,
result = handle.AsyncFD
register(result)
-proc createAsyncNativeSocket*(domain: cint, sockType: cint,
- protocol: cint,
- inheritable = defined(nimInheritHandles)): AsyncFD =
- createAsyncNativeSocketImpl(domain, sockType, protocol, inheritable)
+when not socketsMissing:
+ proc createAsyncNativeSocket*(domain: cint, sockType: cint,
+ protocol: cint,
+ inheritable = defined(nimInheritHandles)): AsyncFD =
+ createAsyncNativeSocketImpl(domain, sockType, protocol, inheritable)
-proc createAsyncNativeSocket*(domain: Domain = Domain.AF_INET,
- sockType: SockType = SOCK_STREAM,
- protocol: Protocol = IPPROTO_TCP,
- inheritable = defined(nimInheritHandles)): AsyncFD =
- createAsyncNativeSocketImpl(domain, sockType, protocol, inheritable)
+ proc createAsyncNativeSocket*(domain: Domain = Domain.AF_INET,
+ sockType: SockType = SOCK_STREAM,
+ protocol: Protocol = IPPROTO_TCP,
+ inheritable = defined(nimInheritHandles)): AsyncFD =
+ createAsyncNativeSocketImpl(domain, sockType, protocol, inheritable)
when defined(windows) or defined(nimdoc):
proc bindToDomain(handle: SocketHandle, domain: Domain) =
@@ -1727,7 +1802,7 @@ when defined(windows) or defined(nimdoc):
# and the future will be completed/failed there, too.
GC_unref(ol)
retFuture.fail(newException(OSError, osErrorMsg(lastError)))
-else:
+elif not socketsMissing:
proc doConnect(socket: AsyncFD, addrInfo: ptr AddrInfo): owned(Future[void]) =
let retFuture = newFuture[void]("doConnect")
result = retFuture
@@ -1759,107 +1834,108 @@ else:
else:
retFuture.fail(newException(OSError, osErrorMsg(lastError)))
-template asyncAddrInfoLoop(addrInfo: ptr AddrInfo, fd: untyped,
- protocol: Protocol = IPPROTO_RAW) =
- ## Iterates through the AddrInfo linked list asynchronously
- ## until the connection can be established.
- const shouldCreateFd = not declared(fd)
-
- when shouldCreateFd:
- let sockType = protocol.toSockType()
+when not socketsMissing:
+ template asyncAddrInfoLoop(addrInfo: ptr AddrInfo, fd: untyped,
+ protocol: Protocol = IPPROTO_RAW) =
+ ## Iterates through the AddrInfo linked list asynchronously
+ ## until the connection can be established.
+ const shouldCreateFd = not declared(fd)
+
+ when shouldCreateFd:
+ let sockType = protocol.toSockType()
+
+ var fdPerDomain: array[low(Domain).ord..high(Domain).ord, AsyncFD]
+ for i in low(fdPerDomain)..high(fdPerDomain):
+ fdPerDomain[i] = osInvalidSocket.AsyncFD
+ template closeUnusedFds(domainToKeep = -1) {.dirty.} =
+ for i, fd in fdPerDomain:
+ if fd != osInvalidSocket.AsyncFD and i != domainToKeep:
+ fd.closeSocket()
+
+ var lastException: ref Exception
+ var curAddrInfo = addrInfo
+ var domain: Domain
+ when shouldCreateFd:
+ var curFd: AsyncFD
+ else:
+ var curFd = fd
+ proc tryNextAddrInfo(fut: Future[void]) {.gcsafe.} =
+ if fut == nil or fut.failed:
+ if fut != nil:
+ lastException = fut.readError()
+
+ while curAddrInfo != nil:
+ let domainOpt = curAddrInfo.ai_family.toKnownDomain()
+ if domainOpt.isSome:
+ domain = domainOpt.unsafeGet()
+ break
+ curAddrInfo = curAddrInfo.ai_next
+
+ if curAddrInfo == nil:
+ freeAddrInfo(addrInfo)
+ when shouldCreateFd:
+ closeUnusedFds()
+ if lastException != nil:
+ retFuture.fail(lastException)
+ else:
+ retFuture.fail(newException(
+ IOError, "Couldn't resolve address: " & address))
+ return
- var fdPerDomain: array[low(Domain).ord..high(Domain).ord, AsyncFD]
- for i in low(fdPerDomain)..high(fdPerDomain):
- fdPerDomain[i] = osInvalidSocket.AsyncFD
- template closeUnusedFds(domainToKeep = -1) {.dirty.} =
- for i, fd in fdPerDomain:
- if fd != osInvalidSocket.AsyncFD and i != domainToKeep:
- fd.closeSocket()
-
- var lastException: ref Exception
- var curAddrInfo = addrInfo
- var domain: Domain
- when shouldCreateFd:
- var curFd: AsyncFD
- else:
- var curFd = fd
- proc tryNextAddrInfo(fut: Future[void]) {.gcsafe.} =
- if fut == nil or fut.failed:
- if fut != nil:
- lastException = fut.readError()
-
- while curAddrInfo != nil:
- let domainOpt = curAddrInfo.ai_family.toKnownDomain()
- if domainOpt.isSome:
- domain = domainOpt.unsafeGet()
- break
+ when shouldCreateFd:
+ curFd = fdPerDomain[ord(domain)]
+ if curFd == osInvalidSocket.AsyncFD:
+ try:
+ curFd = createAsyncNativeSocket(domain, sockType, protocol)
+ except:
+ freeAddrInfo(addrInfo)
+ closeUnusedFds()
+ raise getCurrentException()
+ when defined(windows):
+ curFd.SocketHandle.bindToDomain(domain)
+ fdPerDomain[ord(domain)] = curFd
+
+ doConnect(curFd, curAddrInfo).callback = tryNextAddrInfo
curAddrInfo = curAddrInfo.ai_next
-
- if curAddrInfo == nil:
+ else:
freeAddrInfo(addrInfo)
when shouldCreateFd:
- closeUnusedFds()
- if lastException != nil:
- retFuture.fail(lastException)
+ closeUnusedFds(ord(domain))
+ retFuture.complete(curFd)
else:
- retFuture.fail(newException(
- IOError, "Couldn't resolve address: " & address))
- return
-
- when shouldCreateFd:
- curFd = fdPerDomain[ord(domain)]
- if curFd == osInvalidSocket.AsyncFD:
- try:
- curFd = createAsyncNativeSocket(domain, sockType, protocol)
- except:
- freeAddrInfo(addrInfo)
- closeUnusedFds()
- raise getCurrentException()
- when defined(windows):
- curFd.SocketHandle.bindToDomain(domain)
- fdPerDomain[ord(domain)] = curFd
-
- doConnect(curFd, curAddrInfo).callback = tryNextAddrInfo
- curAddrInfo = curAddrInfo.ai_next
- else:
- freeAddrInfo(addrInfo)
- when shouldCreateFd:
- closeUnusedFds(ord(domain))
- retFuture.complete(curFd)
- else:
- retFuture.complete()
-
- tryNextAddrInfo(nil)
+ retFuture.complete()
-proc dial*(address: string, port: Port,
- protocol: Protocol = IPPROTO_TCP): owned(Future[AsyncFD]) =
- ## Establishes connection to the specified `address`:`port` pair via the
- ## specified protocol. The procedure iterates through possible
- ## resolutions of the `address` until it succeeds, meaning that it
- ## seamlessly works with both IPv4 and IPv6.
- ## Returns the async file descriptor, registered in the dispatcher of
- ## the current thread, ready to send or receive data.
- let retFuture = newFuture[AsyncFD]("dial")
- result = retFuture
- let sockType = protocol.toSockType()
+ tryNextAddrInfo(nil)
+
+ proc dial*(address: string, port: Port,
+ protocol: Protocol = IPPROTO_TCP): owned(Future[AsyncFD]) =
+ ## Establishes connection to the specified `address`:`port` pair via the
+ ## specified protocol. The procedure iterates through possible
+ ## resolutions of the `address` until it succeeds, meaning that it
+ ## seamlessly works with both IPv4 and IPv6.
+ ## Returns the async file descriptor, registered in the dispatcher of
+ ## the current thread, ready to send or receive data.
+ let retFuture = newFuture[AsyncFD]("dial")
+ result = retFuture
+ let sockType = protocol.toSockType()
- let aiList = getAddrInfo(address, port, Domain.AF_UNSPEC, sockType, protocol)
- asyncAddrInfoLoop(aiList, noFD, protocol)
+ let aiList = getAddrInfo(address, port, Domain.AF_UNSPEC, sockType, protocol)
+ asyncAddrInfoLoop(aiList, noFD, protocol)
-proc connect*(socket: AsyncFD, address: string, port: Port,
- domain = Domain.AF_INET): owned(Future[void]) =
- let retFuture = newFuture[void]("connect")
- result = retFuture
+ proc connect*(socket: AsyncFD, address: string, port: Port,
+ domain = Domain.AF_INET): owned(Future[void]) =
+ let retFuture = newFuture[void]("connect")
+ result = retFuture
- when defined(windows):
- verifyPresence(socket)
- else:
- assert getSockDomain(socket.SocketHandle) == domain
+ when defined(windows):
+ verifyPresence(socket)
+ else:
+ assert getSockDomain(socket.SocketHandle) == domain
- let aiList = getAddrInfo(address, port, domain)
- when defined(windows):
- socket.SocketHandle.bindToDomain(domain)
- asyncAddrInfoLoop(aiList, socket)
+ let aiList = getAddrInfo(address, port, domain)
+ when defined(windows):
+ socket.SocketHandle.bindToDomain(domain)
+ asyncAddrInfoLoop(aiList, socket)
proc sleepAsync*(timeout: Duration): owned(Future[void]) =
## Suspends the execution of the current async procedure for the given
@@ -1900,48 +1976,49 @@ proc withTimeout*[T](fut: Future[T], timeoutMs: int): owned(Future[bool]) =
if not retFuture.finished: retFuture.complete(false)
return retFuture
-proc accept*(socket: AsyncFD,
- flags = {SocketFlag.SafeDisconn},
- inheritable = defined(nimInheritHandles)): owned(Future[AsyncFD]) =
- ## Accepts a new connection. Returns a future containing the client socket
- ## corresponding to that connection.
- ##
- ## If `inheritable` is false (the default), the resulting client socket
- ## will not be inheritable by child processes.
- ##
- ## The future will complete when the connection is successfully accepted.
- var retFut = newFuture[AsyncFD]("accept")
- var fut = acceptAddr(socket, flags, inheritable)
- fut.callback =
- proc (future: Future[tuple[address: string, client: AsyncFD]]) =
- assert future.finished
- if future.failed:
- retFut.fail(future.error)
- else:
- retFut.complete(future.read.client)
- return retFut
-
-proc keepAlive(x: string) =
- discard "mark 'x' as escaping so that it is put into a closure for us to keep the data alive"
-
-proc send*(socket: AsyncFD, data: string,
- flags = {SocketFlag.SafeDisconn}): owned(Future[void]) =
- ## Sends `data` to `socket`. The returned future will complete once all
- ## data has been sent.
- var retFuture = newFuture[void]("send")
- if data.len > 0:
- let sendFut = socket.send(unsafeAddr data[0], data.len, flags)
- sendFut.callback =
- proc () =
- keepAlive(data)
- if sendFut.failed:
- retFuture.fail(sendFut.error)
+when not socketsMissing:
+ proc accept*(socket: AsyncFD,
+ flags = {SocketFlag.SafeDisconn},
+ inheritable = defined(nimInheritHandles)): owned(Future[AsyncFD]) =
+ ## Accepts a new connection. Returns a future containing the client socket
+ ## corresponding to that connection.
+ ##
+ ## If `inheritable` is false (the default), the resulting client socket
+ ## will not be inheritable by child processes.
+ ##
+ ## The future will complete when the connection is successfully accepted.
+ var retFut = newFuture[AsyncFD]("accept")
+ var fut = acceptAddr(socket, flags, inheritable)
+ fut.callback =
+ proc (future: Future[tuple[address: string, client: AsyncFD]]) =
+ assert future.finished
+ if future.failed:
+ retFut.fail(future.error)
else:
- retFuture.complete()
- else:
- retFuture.complete()
+ retFut.complete(future.read.client)
+ return retFut
- return retFuture
+ proc keepAlive(x: string) =
+ discard "mark 'x' as escaping so that it is put into a closure for us to keep the data alive"
+
+ proc send*(socket: AsyncFD, data: string,
+ flags = {SocketFlag.SafeDisconn}): owned(Future[void]) =
+ ## Sends `data` to `socket`. The returned future will complete once all
+ ## data has been sent.
+ var retFuture = newFuture[void]("send")
+ if data.len > 0:
+ let sendFut = socket.send(unsafeAddr data[0], data.len, flags)
+ sendFut.callback =
+ proc () =
+ keepAlive(data)
+ if sendFut.failed:
+ retFuture.fail(sendFut.error)
+ else:
+ retFuture.complete()
+ else:
+ retFuture.complete()
+
+ return retFuture
# -- Await Macro
include asyncmacro
@@ -1977,6 +2054,7 @@ proc activeDescriptors*(): int {.inline.} =
## event loop. This is a cheap operation that does not involve a system call.
when defined(windows):
result = getGlobalDispatcher().handles.len
+ elif defined(genode): -1
elif not defined(nimdoc):
result = getGlobalDispatcher().selector.count
diff --git a/lib/pure/selectors.nim b/lib/pure/selectors.nim
index 21bef9187..b1edff636 100644
--- a/lib/pure/selectors.nim
+++ b/lib/pure/selectors.nim
@@ -350,6 +350,7 @@ elif not defined(nimNoLibc):
elif defined(solaris):
include ioselects/ioselectors_poll # need to replace it with event ports
elif defined(genode):
+ when not defined(posix): {.error: "do not import this module without POSIX support".}
include ioselects/ioselectors_select # TODO: use the native VFS layer
elif defined(nintendoswitch):
include ioselects/ioselectors_select
--
2.37.2
From 0e45ef5201ba41bfa971d28f859ad7efcdd58549 Mon Sep 17 00:00:00 2001
From: Emery Hemingway <ehmry@posteo.net>
Date: Wed, 12 Oct 2022 18:49:08 -0500
Subject: [PATCH 8/8] Genode: do no enter libc when posix not defined
---
compiler/ccgexprs.nim | 2 +-
compiler/cgen.nim | 23 ++++++++++++++++++++---
2 files changed, 21 insertions(+), 4 deletions(-)
diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim
index d6057fcfb..cb90c8e85 100644
--- a/compiler/ccgexprs.nim
+++ b/compiler/ccgexprs.nim
@@ -1155,7 +1155,7 @@ proc genEcho(p: BProc, n: PNode) =
# this unusual way of implementing it ensures that e.g. ``echo("hallo", 45)``
# is threadsafe.
internalAssert p.config, n.kind == nkBracket
- if p.config.target.targetOS == osGenode:
+ if p.config.target.targetOS == osGenode and not p.config.isDefined("posix"):
# echo directly to the Genode LOG session
var args: Rope = nil
var a: TLoc
diff --git a/compiler/cgen.nim b/compiler/cgen.nim
index 03999db62..03dbaa30b 100644
--- a/compiler/cgen.nim
+++ b/compiler/cgen.nim
@@ -1464,6 +1464,16 @@ proc genMainProc(m: BModule) =
NimMainBody
ComponentConstruct =
+ "void Component::construct(Genode::Env &env) {$N" &
+ "\t// Set Env used during runtime initialization$N" &
+ "\tnim_runtime_env = &env;$N" &
+ "\t// Initialize runtime and globals$N" &
+ MainProcs &
+ "\t// Call application construct$N" &
+ "\tnim_component_construct(&env);$N" &
+ "}$N$N"
+
+ LibcComponentConstruct =
"void Libc::Component::construct(Libc::Env &env) {$N" &
"\t// Set Env used during runtime initialization$N" &
"\tnim_runtime_env = &env;$N" &
@@ -1479,7 +1489,10 @@ proc genMainProc(m: BModule) =
m.config.globalOptions * {optGenGuiApp, optGenDynLib} != {}:
m.includeHeader("<windows.h>")
elif m.config.target.targetOS == osGenode:
- m.includeHeader("<libc/component.h>")
+ if m.config.isDefined("posix"):
+ m.includeHeader("<libc/component.h>")
+ else:
+ m.includeHeader("<base/component.h>")
let initStackBottomCall =
if m.config.target.targetOS == osStandalone or m.config.selectedGC in {gcNone, gcArc, gcOrc}: "".rope
@@ -1535,8 +1548,12 @@ proc genMainProc(m: BModule) =
const otherMain = WinCDllMain
appcg(m, m.s[cfsProcs], otherMain, [m.config.nimMainPrefix])
elif m.config.target.targetOS == osGenode:
- const otherMain = ComponentConstruct
- appcg(m, m.s[cfsProcs], otherMain, [m.config.nimMainPrefix])
+ if m.config.isDefined("posix"):
+ const otherMain = LibcComponentConstruct
+ appcg(m, m.s[cfsProcs], otherMain, [m.config.nimMainPrefix])
+ else:
+ const otherMain = ComponentConstruct
+ appcg(m, m.s[cfsProcs], otherMain, [m.config.nimMainPrefix])
elif optGenDynLib in m.config.globalOptions:
const otherMain = PosixCDllMain
appcg(m, m.s[cfsProcs], otherMain, [m.config.nimMainPrefix])
--
2.37.2