From b50326bf9b53f02d7a1244dbf12a00b35a0ff71e Mon Sep 17 00:00:00 2001 From: Emery Hemingway 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: "".} 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: "", 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: "", 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: "", - importc: "sprintf", varargs, noSideEffect.} +when not defined(nimNoLibc): + proc c_sprintf(buf, frmt: cstring): cint {.header: "", + 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: "", noSideEffect.} +when not defined(nimNoLibc): + proc c_strtod(buf: cstring, endptr: ptr cstring): float64 {. + importc: "strtod", header: "", 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 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 `_ ## * `getProjectPath proc `_ - 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: "".} -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 -- 2.37.2 From 11e259afe417e39fda794c83304cfd815da2dfd6 Mon Sep 17 00:00:00 2001 From: Emery Hemingway 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: "", 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: "", 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 = "" + +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: "", 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 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 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: "", + 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 = "" @@ -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: "", pure.} = object + ## Capability to an asynchronous signal context. + +proc isValid*(cap: SignalContextCapability): bool {. + importcpp: "#.valid()", tags: [IOEffect].} + +{.emit: """ +#include +#include +#include + +/* 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 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 = "" + +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 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.. 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 = "" @@ -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", + 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 + +namespace Nim { + + /** + * Class for calling a Nim callback from a Genode timer. + */ + template + struct Timeout_handler + { + Timer::One_shot_timeout _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 + typedef Constructible> 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 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("") elif m.config.target.targetOS == osGenode: - m.includeHeader("") + if m.config.isDefined("posix"): + m.includeHeader("") + else: + m.includeHeader("") 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