diff options
Diffstat (limited to 'lib/std')
-rw-r--r-- | lib/std/cmdline.nim | 8 | ||||
-rw-r--r-- | lib/std/enumutils.nim | 8 | ||||
-rw-r--r-- | lib/std/exitprocs.nim | 16 | ||||
-rw-r--r-- | lib/std/formatfloat.nim | 16 | ||||
-rw-r--r-- | lib/std/paths.nim | 12 | ||||
-rw-r--r-- | lib/std/private/gitutils.nim | 13 | ||||
-rw-r--r-- | lib/std/private/jsutils.nim | 6 | ||||
-rw-r--r-- | lib/std/private/osdirs.nim | 21 | ||||
-rw-r--r-- | lib/std/private/osfiles.nim | 2 | ||||
-rw-r--r-- | lib/std/private/ospaths2.nim | 16 | ||||
-rw-r--r-- | lib/std/private/ossymlinks.nim | 18 | ||||
-rw-r--r-- | lib/std/private/syslocks.nim | 28 | ||||
-rw-r--r-- | lib/std/syncio.nim | 13 | ||||
-rw-r--r-- | lib/std/tasks.nim | 27 | ||||
-rw-r--r-- | lib/std/time_t.nim | 2 | ||||
-rw-r--r-- | lib/std/typedthreads.nim | 94 | ||||
-rw-r--r-- | lib/std/varints.nim | 12 | ||||
-rw-r--r-- | lib/std/widestrs.nim | 4 |
18 files changed, 199 insertions, 117 deletions
diff --git a/lib/std/cmdline.nim b/lib/std/cmdline.nim index a57fb76a4..0ba4619e5 100644 --- a/lib/std/cmdline.nim +++ b/lib/std/cmdline.nim @@ -181,7 +181,7 @@ when defined(nimdoc): ## Similarly to `argv`:idx: in C, ## it is possible to call `paramStr(0)` but this will return OS specific ## contents (usually the name of the invoked executable). You should avoid - ## this and call `getAppFilename()`_ instead. + ## this and call `getAppFilename() <os.html#getAppFilename>`_ instead. ## ## **Availability**: When generating a dynamic library (see `--app:lib`) on ## Posix this proc is not defined. @@ -192,7 +192,7 @@ when defined(nimdoc): ## * `parseCmdLine proc`_ ## * `paramCount proc`_ ## * `commandLineParams proc`_ - ## * `getAppFilename proc`_ + ## * `getAppFilename proc <os.html#getAppFilename>`_ ## ## **Examples:** ## @@ -282,7 +282,7 @@ when declared(paramCount) or defined(nimdoc): ## Convenience proc which returns the command line parameters. ## ## This returns **only** the parameters. If you want to get the application - ## executable filename, call `getAppFilename()`_. + ## executable filename, call `getAppFilename() <os.html#getAppFilename>`_. ## ## **Availability**: On Posix there is no portable way to get the command ## line from a DLL and thus the proc isn't defined in this environment. You @@ -294,7 +294,7 @@ when declared(paramCount) or defined(nimdoc): ## * `parseCmdLine proc`_ ## * `paramCount proc`_ ## * `paramStr proc`_ - ## * `getAppFilename proc`_ + ## * `getAppFilename proc <os.html#getAppFilename>`_ ## ## **Examples:** ## diff --git a/lib/std/enumutils.nim b/lib/std/enumutils.nim index bcfb2d5d7..9c338817d 100644 --- a/lib/std/enumutils.nim +++ b/lib/std/enumutils.nim @@ -174,6 +174,9 @@ template symbolRank*[T: enum](a: T): int = when T is Ordinal: ord(a) - T.low.ord.static else: symbolRankImpl(a) +proc rangeBase(T: typedesc): typedesc {.magic: "TypeTrait".} + # skip one level of range; return the base type of a range type + func symbolName*[T: enum](a: T): string = ## Returns the symbol name of an enum. ## @@ -192,5 +195,8 @@ func symbolName*[T: enum](a: T): string = c1 = 4 c2 = 20 assert c1.symbolName == "c1" - const names = enumNames(T) + when T is range: + const names = enumNames(rangeBase T) + else: + const names = enumNames(T) names[a.symbolRank] diff --git a/lib/std/exitprocs.nim b/lib/std/exitprocs.nim index 52e9653df..f26368f42 100644 --- a/lib/std/exitprocs.nim +++ b/lib/std/exitprocs.nim @@ -29,13 +29,13 @@ initLock(gFunsLock) when defined(js): proc addAtExit(quitProc: proc() {.noconv.}) = when defined(nodejs): - asm """ + {.emit: """ process.on('exit', `quitProc`); - """ + """.} elif defined(js): - asm """ + {.emit: """ window.onbeforeunload = `quitProc`; - """ + """.} else: proc addAtExit(quitProc: proc() {.noconv.}) {. importc: "atexit", header: "<stdlib.h>".} @@ -72,16 +72,16 @@ proc addExitProc*(cl: proc() {.noconv.}) = when not defined(nimscript) and (not defined(js) or defined(nodejs)): proc getProgramResult*(): int = when defined(js) and defined(nodejs): - asm """ + {.emit: """ `result` = process.exitCode; -""" +""".} else: result = programResult proc setProgramResult*(a: int) = when defined(js) and defined(nodejs): - asm """ + {.emit: """ process.exitCode = `a`; -""" +""".} else: programResult = a diff --git a/lib/std/formatfloat.nim b/lib/std/formatfloat.nim index 872549c3b..9258245f6 100644 --- a/lib/std/formatfloat.nim +++ b/lib/std/formatfloat.nim @@ -35,8 +35,8 @@ proc writeFloatToBufferRoundtrip*(buf: var array[65, char]; value: float32): int result = float32ToChars(buf, value, forceTrailingDotZero=true).int buf[result] = '\0' -proc c_sprintf(buf, frmt: cstring): cint {.header: "<stdio.h>", - importc: "sprintf", varargs, noSideEffect.} +proc c_snprintf(buf: cstring, n: csize_t, frmt: cstring): cint {.header: "<stdio.h>", + importc: "snprintf", varargs, noSideEffect.} proc writeToBuffer(buf: var array[65, char]; value: cstring) = var i = 0 @@ -49,7 +49,7 @@ proc writeFloatToBufferSprintf*(buf: var array[65, char]; value: BiggestFloat): ## ## returns the amount of bytes written to `buf` not counting the ## terminating '\0' character. - var n = c_sprintf(cast[cstring](addr buf), "%.16g", value).int + var n = c_snprintf(cast[cstring](addr buf), 65, "%.16g", value).int var hasDot = false for i in 0..n-1: if buf[i] == ',': @@ -104,19 +104,19 @@ when defined(js): proc nimFloatToString(a: float): cstring = ## ensures the result doesn't print like an integer, i.e. return 2.0, not 2 # print `-0.0` properly - asm """ + {.emit: """ function nimOnlyDigitsOrMinus(n) { return n.toString().match(/^-?\d+$/); } if (Number.isSafeInteger(`a`)) - `result` = `a` === 0 && 1 / `a` < 0 ? "-0.0" : `a`+".0" + `result` = `a` === 0 && 1 / `a` < 0 ? "-0.0" : `a`+".0"; else { - `result` = `a`+"" + `result` = `a`+""; if(nimOnlyDigitsOrMinus(`result`)){ - `result` = `a`+".0" + `result` = `a`+".0"; } } - """ + """.} proc addFloat*(result: var string; x: float | float32) {.inline.} = ## Converts float to its string representation and appends it to `result`. diff --git a/lib/std/paths.nim b/lib/std/paths.nim index b488d2fea..664dedd31 100644 --- a/lib/std/paths.nim +++ b/lib/std/paths.nim @@ -9,7 +9,7 @@ export osseps import std/envvars import std/private/osappdirs -import std/pathnorm +import std/[pathnorm, hashes, sugar, strutils] from std/private/ospaths2 import joinPath, splitPath, ReadDirEffect, WriteDirEffect, @@ -25,6 +25,16 @@ export ReadDirEffect, WriteDirEffect type Path* = distinct string +func hash*(x: Path): Hash = + let x = x.string.dup(normalizePath) + if FileSystemCaseSensitive: + result = x.hash + else: + result = x.toLowerAscii.hash + +template `$`*(x: Path): string = + string(x) + func `==`*(x, y: Path): bool {.inline.} = ## Compares two paths. ## diff --git a/lib/std/private/gitutils.nim b/lib/std/private/gitutils.nim index db323bee1..6dc9c8f3b 100644 --- a/lib/std/private/gitutils.nim +++ b/lib/std/private/gitutils.nim @@ -4,7 +4,7 @@ internal API for now, API subject to change # xxx move other git utilities here; candidate for stdlib. -import std/[os, osproc, strutils, tempfiles] +import std/[os, paths, osproc, strutils, tempfiles] when defined(nimPreviewSlimSystem): import std/[assertions, syncio] @@ -32,15 +32,8 @@ template retryCall*(maxRetry = 3, backoffDuration = 1.0, call: untyped): bool = result proc isGitRepo*(dir: string): bool = - ## This command is used to get the relative path to the root of the repository. - ## Using this, we can verify whether a folder is a git repository by checking - ## whether the command success and if the output is empty. - let (output, status) = execCmdEx("git rev-parse --show-cdup", workingDir = dir) - # On Windows there will be a trailing newline on success, remove it. - # The value of a successful call typically won't have a whitespace (it's - # usually a series of ../), so we know that it's safe to unconditionally - # remove trailing whitespaces from the result. - result = status == 0 and output.strip() == "" + ## Avoid calling git since it depends on /bin/sh existing and fails in Nix. + return fileExists(dir/".git/HEAD") proc diffFiles*(path1, path2: string): tuple[output: string, same: bool] = ## Returns a human readable diff of files `path1`, `path2`, the exact form of diff --git a/lib/std/private/jsutils.nim b/lib/std/private/jsutils.nim index fd1f395f3..5f79eab27 100644 --- a/lib/std/private/jsutils.nim +++ b/lib/std/private/jsutils.nim @@ -37,13 +37,13 @@ when defined(js): let a = array[2, float64].default assert jsConstructorName(a) == "Float64Array" assert jsConstructorName(a.toJs) == "Float64Array" - asm """`result` = `a`.constructor.name""" + {.emit: """`result` = `a`.constructor.name;""".} proc hasJsBigInt*(): bool = - asm """`result` = typeof BigInt != 'undefined'""" + {.emit: """`result` = typeof BigInt != 'undefined';""".} proc hasBigUint64Array*(): bool = - asm """`result` = typeof BigUint64Array != 'undefined'""" + {.emit: """`result` = typeof BigUint64Array != 'undefined';""".} proc getProtoName*[T](a: T): cstring {.importjs: "Object.prototype.toString.call(#)".} = runnableExamples: diff --git a/lib/std/private/osdirs.nim b/lib/std/private/osdirs.nim index b89a59c8d..a44cad7d9 100644 --- a/lib/std/private/osdirs.nim +++ b/lib/std/private/osdirs.nim @@ -446,13 +446,17 @@ proc createDir*(dir: string) {.rtl, extern: "nos$1", else: discard existsOrCreateDir(p) -proc copyDir*(source, dest: string) {.rtl, extern: "nos$1", +proc copyDir*(source, dest: string, skipSpecial = false) {.rtl, extern: "nos$1", tags: [ReadDirEffect, WriteIOEffect, ReadIOEffect], benign, noWeirdTarget.} = ## Copies a directory from `source` to `dest`. ## ## On non-Windows OSes, symlinks are copied as symlinks. On Windows, symlinks ## are skipped. ## + ## If `skipSpecial` is true, then (besides all directories) only *regular* + ## files (**without** special "file" objects like FIFOs, device files, + ## etc) will be copied on Unix. + ## ## If this fails, `OSError` is raised. ## ## On the Windows platform this proc will copy the attributes from @@ -472,16 +476,17 @@ proc copyDir*(source, dest: string) {.rtl, extern: "nos$1", ## * `createDir proc`_ ## * `moveDir proc`_ createDir(dest) - for kind, path in walkDir(source): + for kind, path in walkDir(source, skipSpecial = skipSpecial): var noSource = splitPath(path).tail if kind == pcDir: - copyDir(path, dest / noSource) + copyDir(path, dest / noSource, skipSpecial = skipSpecial) else: copyFile(path, dest / noSource, {cfSymlinkAsIs}) proc copyDirWithPermissions*(source, dest: string, - ignorePermissionErrors = true) + ignorePermissionErrors = true, + skipSpecial = false) {.rtl, extern: "nos$1", tags: [ReadDirEffect, WriteIOEffect, ReadIOEffect], benign, noWeirdTarget.} = ## Copies a directory from `source` to `dest` preserving file permissions. @@ -489,6 +494,10 @@ proc copyDirWithPermissions*(source, dest: string, ## On non-Windows OSes, symlinks are copied as symlinks. On Windows, symlinks ## are skipped. ## + ## If `skipSpecial` is true, then (besides all directories) only *regular* + ## files (**without** special "file" objects like FIFOs, device files, + ## etc) will be copied on Unix. + ## ## If this fails, `OSError` is raised. This is a wrapper proc around ## `copyDir`_ and `copyFileWithPermissions`_ procs ## on non-Windows platforms. @@ -518,10 +527,10 @@ proc copyDirWithPermissions*(source, dest: string, except: if not ignorePermissionErrors: raise - for kind, path in walkDir(source): + for kind, path in walkDir(source, skipSpecial = skipSpecial): var noSource = splitPath(path).tail if kind == pcDir: - copyDirWithPermissions(path, dest / noSource, ignorePermissionErrors) + copyDirWithPermissions(path, dest / noSource, ignorePermissionErrors, skipSpecial = skipSpecial) else: copyFileWithPermissions(path, dest / noSource, ignorePermissionErrors, {cfSymlinkAsIs}) diff --git a/lib/std/private/osfiles.nim b/lib/std/private/osfiles.nim index a1d7079c5..37d8eabca 100644 --- a/lib/std/private/osfiles.nim +++ b/lib/std/private/osfiles.nim @@ -240,7 +240,7 @@ proc copyFile*(source, dest: string, options = {cfSymlinkFollow}; bufferSize = 1 else: # generic version of copyFile which works for any platform: var d, s: File - if not open(s, source):raiseOSError(osLastError(), source) + if not open(s, source): raiseOSError(osLastError(), source) if not open(d, dest, fmWrite): close(s) raiseOSError(osLastError(), dest) diff --git a/lib/std/private/ospaths2.nim b/lib/std/private/ospaths2.nim index 37fae3ccd..bc69ff725 100644 --- a/lib/std/private/ospaths2.nim +++ b/lib/std/private/ospaths2.nim @@ -254,10 +254,10 @@ proc isAbsolute*(path: string): bool {.rtl, noSideEffect, extern: "nos$1", raise result = path[0] != ':' elif defined(RISCOS): result = path[0] == '$' - elif defined(posix) or defined(js): - # `or defined(js)` wouldn't be needed pending https://github.com/nim-lang/Nim/issues/13469 - # This works around the problem for posix, but Windows is still broken with nim js -d:nodejs + elif defined(posix): result = path[0] == '/' + elif defined(nodejs): + {.emit: [result," = require(\"path\").isAbsolute(",path.cstring,");"].} else: raiseAssert "unreachable" # if ever hits here, adapt as needed @@ -763,9 +763,9 @@ proc cmpPaths*(pathA, pathB: string): int {. ## On a case-sensitive filesystem this is done ## case-sensitively otherwise case-insensitively. Returns: ## - ## | 0 if pathA == pathB - ## | < 0 if pathA < pathB - ## | > 0 if pathA > pathB + ## | `0` if pathA == pathB + ## | `< 0` if pathA < pathB + ## | `> 0` if pathA > pathB runnableExamples: when defined(macosx): assert cmpPaths("foo", "Foo") == 0 @@ -862,13 +862,13 @@ when not defined(nimscript): raiseAssert "use -d:nodejs to have `getCurrentDir` defined" elif defined(windows): var bufsize = MAX_PATH.int32 - var res = newWideCString("", bufsize) + var res = newWideCString(bufsize) while true: var L = getCurrentDirectoryW(bufsize, res) if L == 0'i32: raiseOSError(osLastError()) elif L > bufsize: - res = newWideCString("", L) + res = newWideCString(L) bufsize = L else: result = res$L diff --git a/lib/std/private/ossymlinks.nim b/lib/std/private/ossymlinks.nim index c0774b573..c1760c42e 100644 --- a/lib/std/private/ossymlinks.nim +++ b/lib/std/private/ossymlinks.nim @@ -66,11 +66,13 @@ proc expandSymlink*(symlinkPath: string): string {.noWeirdTarget.} = when defined(windows) or defined(nintendoswitch): result = symlinkPath else: - result = newString(maxSymlinkLen) - var len = readlink(symlinkPath, result.cstring, maxSymlinkLen) - if len < 0: - raiseOSError(osLastError(), symlinkPath) - if len > maxSymlinkLen: - result = newString(len+1) - len = readlink(symlinkPath, result.cstring, len) - setLen(result, len) + var bufLen = 1024 + while true: + result = newString(bufLen) + let len = readlink(symlinkPath.cstring, result.cstring, bufLen) + if len < 0: + raiseOSError(osLastError(), symlinkPath) + if len < bufLen: + result.setLen(len) + break + bufLen = bufLen shl 1 diff --git a/lib/std/private/syslocks.nim b/lib/std/private/syslocks.nim index ca8897dc2..e19ec2c04 100644 --- a/lib/std/private/syslocks.nim +++ b/lib/std/private/syslocks.nim @@ -16,7 +16,7 @@ when defined(windows): Handle = int SysLock* {.importc: "CRITICAL_SECTION", - header: "<windows.h>", final, pure.} = object # CRITICAL_SECTION in WinApi + header: "<windows.h>", final, pure, byref.} = object # CRITICAL_SECTION in WinApi DebugInfo: pointer LockCount: int32 RecursionCount: int32 @@ -24,7 +24,7 @@ when defined(windows): LockSemaphore: int SpinCount: int - SysCond* {.importc: "RTL_CONDITION_VARIABLE", header: "<windows.h>".} = object + SysCond* {.importc: "RTL_CONDITION_VARIABLE", header: "<windows.h>", byref.} = object thePtr {.importc: "Ptr".} : Handle proc initSysLock*(L: var SysLock) {.importc: "InitializeCriticalSection", @@ -46,7 +46,7 @@ when defined(windows): header: "<windows.h>".} ## Releases the lock `L`. - proc deinitSys*(L: var SysLock) {.importc: "DeleteCriticalSection", + proc deinitSys*(L: SysLock) {.importc: "DeleteCriticalSection", header: "<windows.h>".} proc initializeConditionVariable( @@ -68,7 +68,7 @@ when defined(windows): proc initSysCond*(cond: var SysCond) {.inline.} = initializeConditionVariable(cond) - proc deinitSysCond*(cond: var SysCond) {.inline.} = + proc deinitSysCond*(cond: SysCond) {.inline.} = discard proc waitSysCond*(cond: var SysCond, lock: var SysLock) = discard sleepConditionVariableCS(cond, lock, -1'i32) @@ -83,13 +83,13 @@ elif defined(genode): header: Header.} = object proc initSysLock*(L: var SysLock) = discard - proc deinitSys*(L: var SysLock) = discard + proc deinitSys*(L: SysLock) = discard proc acquireSys*(L: var SysLock) {.noSideEffect, importcpp.} proc tryAcquireSys*(L: var SysLock): bool {.noSideEffect, importcpp.} proc releaseSys*(L: var SysLock) {.noSideEffect, importcpp.} proc initSysCond*(L: var SysCond) = discard - proc deinitSysCond*(L: var SysCond) = discard + proc deinitSysCond*(L: SysCond) = discard proc waitSysCond*(cond: var SysCond, lock: var SysLock) {. noSideEffect, importcpp.} proc signalSysCond*(cond: var SysCond) {. @@ -101,7 +101,7 @@ else: type SysLockObj {.importc: "pthread_mutex_t", pure, final, header: """#include <sys/types.h> - #include <pthread.h>""".} = object + #include <pthread.h>""", byref.} = object when defined(linux) and defined(amd64): abi: array[40 div sizeof(clong), clong] @@ -113,7 +113,7 @@ else: SysCondObj {.importc: "pthread_cond_t", pure, final, header: """#include <sys/types.h> - #include <pthread.h>""".} = object + #include <pthread.h>""", byref.} = object when defined(linux) and defined(amd64): abi: array[48 div sizeof(clonglong), clonglong] @@ -127,7 +127,7 @@ else: proc initSysLockAux(L: var SysLockObj, attr: ptr SysLockAttr) {. importc: "pthread_mutex_init", header: "<pthread.h>", noSideEffect.} - proc deinitSysAux(L: var SysLockObj) {.noSideEffect, + proc deinitSysAux(L: SysLockObj) {.noSideEffect, importc: "pthread_mutex_destroy", header: "<pthread.h>".} proc acquireSysAux(L: var SysLockObj) {.noSideEffect, @@ -156,7 +156,7 @@ else: L = cast[SysLock](c_malloc(csize_t(sizeof(SysLockObj)))) initSysLockAux(L[], attr) - proc deinitSys*(L: var SysLock) = + proc deinitSys*(L: SysLock) = deinitSysAux(L[]) c_free(L) @@ -173,7 +173,7 @@ else: template initSysLock*(L: var SysLock, attr: ptr SysLockAttr = nil) = initSysLockAux(L, attr) - template deinitSys*(L: var SysLock) = + template deinitSys*(L: SysLock) = deinitSysAux(L) template acquireSys*(L: var SysLock) = acquireSysAux(L) @@ -193,7 +193,7 @@ else: # locks proc initSysCondAux(cond: var SysCondObj, cond_attr: ptr SysCondAttr = nil) {. importc: "pthread_cond_init", header: "<pthread.h>", noSideEffect.} - proc deinitSysCondAux(cond: var SysCondObj) {.noSideEffect, + proc deinitSysCondAux(cond: SysCondObj) {.noSideEffect, importc: "pthread_cond_destroy", header: "<pthread.h>".} proc waitSysCondAux(cond: var SysCondObj, lock: var SysLockObj): cint {. @@ -208,7 +208,7 @@ else: cond = cast[SysCond](c_malloc(csize_t(sizeof(SysCondObj)))) initSysCondAux(cond[], cond_attr) - proc deinitSysCond*(cond: var SysCond) = + proc deinitSysCond*(cond: SysCond) = deinitSysCondAux(cond[]) c_free(cond) @@ -221,7 +221,7 @@ else: else: template initSysCond*(cond: var SysCond, cond_attr: ptr SysCondAttr = nil) = initSysCondAux(cond, cond_attr) - template deinitSysCond*(cond: var SysCond) = + template deinitSysCond*(cond: SysCond) = deinitSysCondAux(cond) template waitSysCond*(cond: var SysCond, lock: var SysLock) = diff --git a/lib/std/syncio.nim b/lib/std/syncio.nim index b664c3b60..c34a025af 100644 --- a/lib/std/syncio.nim +++ b/lib/std/syncio.nim @@ -320,7 +320,7 @@ elif defined(windows): const BufSize = 4000 -proc close*(f: File) {.tags: [], gcsafe.} = +proc close*(f: File) {.tags: [], gcsafe, sideEffect.} = ## Closes the file. if not f.isNil: discard c_fclose(f) @@ -419,7 +419,7 @@ proc readLine*(f: File, line: var string): bool {.tags: [ReadIOEffect], if f.isatty: const numberOfCharsToRead = 2048 var numberOfCharsRead = 0'i32 - var buffer = newWideCString("", numberOfCharsToRead) + var buffer = newWideCString(numberOfCharsToRead) if readConsole(getOsFileHandle(f), addr(buffer[0]), numberOfCharsToRead, addr(numberOfCharsRead), nil) == 0: var error = getLastError() @@ -648,6 +648,9 @@ const "" else: "" + RawFormatOpen: array[FileMode, cstring] = [ + # used for open by FileHandle, which calls `fdopen` + cstring("rb"), "wb", "w+b", "r+b", "ab"] FormatOpen: array[FileMode, cstring] = [ cstring("rb" & NoInheritFlag), "wb" & NoInheritFlag, "w+b" & NoInheritFlag, "r+b" & NoInheritFlag, "ab" & NoInheritFlag @@ -749,7 +752,7 @@ proc open*(f: var File, filehandle: FileHandle, filehandle) else: filehandle if not setInheritable(oshandle, false): return false - f = c_fdopen(filehandle, FormatOpen[mode]) + f = c_fdopen(filehandle, RawFormatOpen[mode]) result = f != nil proc open*(filename: string, @@ -763,7 +766,7 @@ proc open*(filename: string, if not open(result, filename, mode, bufSize): raise newException(IOError, "cannot open: " & filename) -proc setFilePos*(f: File, pos: int64, relativeTo: FileSeekPos = fspSet) {.benign.} = +proc setFilePos*(f: File, pos: int64, relativeTo: FileSeekPos = fspSet) {.benign, sideEffect.} = ## Sets the position of the file pointer that is used for read/write ## operations. The file's first byte has the index zero. if c_fseek(f, pos, cint(relativeTo)) != 0: @@ -871,7 +874,7 @@ proc writeFile*(filename: string, content: openArray[byte]) {.since: (1, 1).} = var f: File = nil if open(f, filename, fmWrite): try: - f.writeBuffer(unsafeAddr content[0], content.len) + discard f.writeBuffer(unsafeAddr content[0], content.len) finally: close(f) else: diff --git a/lib/std/tasks.nim b/lib/std/tasks.nim index 9eb7c97c4..7e59747f5 100644 --- a/lib/std/tasks.nim +++ b/lib/std/tasks.nim @@ -68,7 +68,8 @@ type proc `=copy`*(x: var Task, y: Task) {.error.} -when defined(nimAllowNonVarDestructor): +const arcLike = defined(gcArc) or defined(gcAtomicArc) or defined(gcOrc) +when defined(nimAllowNonVarDestructor) and arcLike: proc `=destroy`*(t: Task) {.inline, gcsafe.} = ## Frees the resources allocated for a `Task`. if t.args != nil: @@ -109,6 +110,19 @@ template addAllNode(assignParam: NimNode, procParam: NimNode) = tempAssignList.add newLetStmt(tempNode, newDotExpr(objTemp, formalParams[i][0])) scratchRecList.add newIdentDefs(newIdentNode(formalParams[i][0].strVal), assignParam) +proc analyseRootSym(s: NimNode): NimNode = + result = s + while true: + case result.kind + of nnkBracketExpr, nnkDerefExpr, nnkHiddenDeref, + nnkAddr, nnkHiddenAddr, + nnkObjDownConv, nnkObjUpConv: + result = result[0] + of nnkDotExpr, nnkCheckedFieldExpr, nnkHiddenStdConv, nnkHiddenSubConv: + result = result[1] + else: + break + macro toTask*(e: typed{nkCall | nkInfix | nkPrefix | nkPostfix | nkCommand | nkCallStrLit}): Task = ## Converts the call and its arguments to `Task`. runnableExamples: @@ -120,11 +134,14 @@ macro toTask*(e: typed{nkCall | nkInfix | nkPrefix | nkPostfix | nkCommand | nkC let retType = getTypeInst(e) let returnsVoid = retType.typeKind == ntyVoid + let rootSym = analyseRootSym(e[0]) + expectKind rootSym, nnkSym + when compileOption("threads"): - if not isGcSafe(e[0]): + if not isGcSafe(rootSym): error("'toTask' takes a GC safe call expression", e) - if hasClosure(e[0]): + if hasClosure(rootSym): error("closure call is not allowed", e) if e.len > 1: @@ -208,7 +225,7 @@ macro toTask*(e: typed{nkCall | nkInfix | nkPrefix | nkPostfix | nkCommand | nkC let funcCall = newCall(e[0], callNode) functionStmtList.add tempAssignList - let funcName = genSym(nskProc, e[0].strVal) + let funcName = genSym(nskProc, rootSym.strVal) let destroyName = genSym(nskProc, "destroyScratch") let objTemp2 = genSym(ident = "obj") let tempNode = quote("@") do: @@ -240,7 +257,7 @@ macro toTask*(e: typed{nkCall | nkInfix | nkPrefix | nkPostfix | nkCommand | nkC Task(callback: `funcName`, args: `scratchIdent`, destroy: `destroyName`) else: let funcCall = newCall(e[0]) - let funcName = genSym(nskProc, e[0].strVal) + let funcName = genSym(nskProc, rootSym.strVal) if returnsVoid: result = quote do: diff --git a/lib/std/time_t.nim b/lib/std/time_t.nim index 5fa95fff3..de051b135 100644 --- a/lib/std/time_t.nim +++ b/lib/std/time_t.nim @@ -14,7 +14,7 @@ when defined(nimdoc): ## Wrapper for `time_t`. On posix, this is an alias to `posix.Time`. elif defined(windows): when defined(i386) and defined(gcc): - type Time* {.importc: "time_t", header: "<time.h>".} = distinct int32 + type Time* {.importc: "time_t", header: "<time.h>".} = distinct clong else: # newest version of Visual C++ defines time_t to be of 64 bits type Time* {.importc: "time_t", header: "<time.h>".} = distinct int64 diff --git a/lib/std/typedthreads.nim b/lib/std/typedthreads.nim index 501a0d0fa..7b0b81968 100644 --- a/lib/std/typedthreads.nim +++ b/lib/std/typedthreads.nim @@ -7,33 +7,73 @@ # distribution, for details about the copyright. # -## Thread support for Nim. -## -## Examples -## ======== -## -## ```Nim -## import std/locks -## -## var -## thr: array[0..4, Thread[tuple[a,b: int]]] -## L: Lock -## -## proc threadFunc(interval: tuple[a,b: int]) {.thread.} = -## for i in interval.a..interval.b: -## acquire(L) # lock stdout -## echo i -## release(L) -## -## initLock(L) -## -## for i in 0..high(thr): -## createThread(thr[i], threadFunc, (i*10, i*10+5)) -## joinThreads(thr) -## -## deinitLock(L) -## ``` - +##[ +Thread support for Nim. Threads allow multiple functions to execute concurrently. + +In Nim, threads are a low-level construct and using a library like `malebolgia`, `taskpools` or `weave` is recommended. + +When creating a thread, you can pass arguments to it. As Nim's garbage collector does not use atomic references, sharing +`ref` and other variables managed by the garbage collector between threads is not supported. +Use global variables to do so, or pointers. + +Memory allocated using [`sharedAlloc`](./system.html#allocShared.t%2CNatural) can be used and shared between threads. + +To communicate between threads, consider using [channels](./system.html#Channel) + +Examples +======== + +```Nim +import std/locks + +var + thr: array[0..4, Thread[tuple[a,b: int]]] + L: Lock + +proc threadFunc(interval: tuple[a,b: int]) {.thread.} = + for i in interval.a..interval.b: + acquire(L) # lock stdout + echo i + release(L) + +initLock(L) + +for i in 0..high(thr): + createThread(thr[i], threadFunc, (i*10, i*10+5)) +joinThreads(thr) + +deinitLock(L) +``` + +When using a memory management strategy that supports shared heaps like `arc` or `boehm`, +you can pass pointer to threads and share memory between them, but the memory must outlive the thread. +The default memory management strategy, `orc`, supports this. +The example below is **not valid** for memory management strategies that use local heaps like `refc`! + +```Nim +import locks + +var l: Lock + +proc threadFunc(obj: ptr seq[int]) {.thread.} = + withLock l: + for i in 0..<100: + obj[].add(obj[].len * obj[].len) + +proc threadHandler() = + var thr: array[0..4, Thread[ptr seq[int]]] + var s = newSeq[int]() + + for i in 0..high(thr): + createThread(thr[i], threadFunc, s.addr) + joinThreads(thr) + echo s + +initLock(l) +threadHandler() +deinitLock(l) +``` +]## import std/private/[threadtypes] diff --git a/lib/std/varints.nim b/lib/std/varints.nim index 0d18b9069..32fe2fffb 100644 --- a/lib/std/varints.nim +++ b/lib/std/varints.nim @@ -82,29 +82,29 @@ proc writeVu64*(z: var openArray[byte], x: uint64): int = z[3] = cast[uint8](y) return 4 z[0] = 251 - varintWrite32(toOpenArray(z, 1, z.high-1), y) + varintWrite32(toOpenArray(z, 1, 4), y) return 5 if w <= 255: z[0] = 252 z[1] = cast[uint8](w) - varintWrite32(toOpenArray(z, 2, z.high-2), y) + varintWrite32(toOpenArray(z, 2, 5), y) return 6 if w <= 65535: z[0] = 253 z[1] = cast[uint8](w shr 8) z[2] = cast[uint8](w) - varintWrite32(toOpenArray(z, 3, z.high-3), y) + varintWrite32(toOpenArray(z, 3, 6), y) return 7 if w <= 16777215: z[0] = 254 z[1] = cast[uint8](w shr 16) z[2] = cast[uint8](w shr 8) z[3] = cast[uint8](w) - varintWrite32(toOpenArray(z, 4, z.high-4), y) + varintWrite32(toOpenArray(z, 4, 7), y) return 8 z[0] = 255 - varintWrite32(toOpenArray(z, 1, z.high-1), w) - varintWrite32(toOpenArray(z, 5, z.high-5), y) + varintWrite32(toOpenArray(z, 1, 4), w) + varintWrite32(toOpenArray(z, 5, 8), y) return 9 proc sar(a, b: int64): int64 = diff --git a/lib/std/widestrs.nim b/lib/std/widestrs.nim index 0bf50be45..2ddf80d14 100644 --- a/lib/std/widestrs.nim +++ b/lib/std/widestrs.nim @@ -25,7 +25,8 @@ when not (defined(cpu16) or defined(cpu8)): bytes: int data: WideCString - when defined(nimAllowNonVarDestructor): + const arcLike = defined(gcArc) or defined(gcAtomicArc) or defined(gcOrc) + when defined(nimAllowNonVarDestructor) and arcLike: proc `=destroy`(a: WideCStringObj) = if a.data != nil: when compileOption("threads"): @@ -154,6 +155,7 @@ when not (defined(cpu16) or defined(cpu8)): createWide(result, size * 2 + 2) proc newWideCString*(source: cstring, L: int): WideCStringObj = + ## Warning:: `source` needs to be preallocated with the length `L` createWide(result, L * 2 + 2) var d = 0 for ch in runes(source, L): |