diff options
Diffstat (limited to 'lib/pure')
-rw-r--r-- | lib/pure/concurrency/threadpool.nim | 10 | ||||
-rw-r--r-- | lib/pure/coro.nim | 145 | ||||
-rw-r--r-- | lib/pure/os.nim | 6 | ||||
-rw-r--r-- | lib/pure/osproc.nim | 12 | ||||
-rw-r--r-- | lib/pure/uri.nim | 27 |
5 files changed, 188 insertions, 12 deletions
diff --git a/lib/pure/concurrency/threadpool.nim b/lib/pure/concurrency/threadpool.nim index 7c9d8adfd..0079cf302 100644 --- a/lib/pure/concurrency/threadpool.nim +++ b/lib/pure/concurrency/threadpool.nim @@ -322,6 +322,9 @@ var distinguished: array[MaxDistinguishedThread, TThread[ptr Worker]] distinguishedData: array[MaxDistinguishedThread, Worker] +when defined(nimPinToCpu): + var gCpus: Natural + proc setMinPoolSize*(size: range[1..MaxThreadPoolSize]) = ## sets the minimal thread pool size. The default value of this is 4. minPoolSize = size @@ -342,6 +345,8 @@ proc activateWorkerThread(i: int) {.noinline.} = workersData[i].q.empty = createSemaphore() initLock(workersData[i].q.lock) createThread(workers[i], slave, addr(workersData[i])) + when defined(nimPinToCpu): + if gCpus > 0: pinToCpu(workers[i], i mod gCpus) proc activateDistinguishedThread(i: int) {.noinline.} = distinguishedData[i].taskArrived = createSemaphore() @@ -353,7 +358,10 @@ proc activateDistinguishedThread(i: int) {.noinline.} = createThread(distinguished[i], distinguishedSlave, addr(distinguishedData[i])) proc setup() = - currentPoolSize = min(countProcessors(), MaxThreadPoolSize) + let p = countProcessors() + when defined(nimPinToCpu): + gCpus = p + currentPoolSize = min(p, MaxThreadPoolSize) readyWorker = addr(workersData[0]) for i in 0.. <currentPoolSize: activateWorkerThread(i) diff --git a/lib/pure/coro.nim b/lib/pure/coro.nim new file mode 100644 index 000000000..6ef5f6f54 --- /dev/null +++ b/lib/pure/coro.nim @@ -0,0 +1,145 @@ +# +# +# Nim's Runtime Library +# (c) Copyright 2015 Rokas Kupstys +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +when not defined(nimCoroutines): + {.error: "Coroutines require -d:nimCoroutines".} + +import os, times +import macros +import arch +import lists + +const coroDefaultStackSize = 512 * 1024 + + +type Coroutine = ref object + # prev: ptr Coroutine + # next: ptr Coroutine + ctx: JmpBuf + fn: proc() + started: bool + lastRun: float + sleepTime: float + stack: pointer + stacksize: int + +var coroutines = initDoublyLinkedList[Coroutine]() +var current: Coroutine +var mainCtx: JmpBuf + + +proc GC_addStack(starts: pointer) {.cdecl, importc.} +proc GC_removeStack(starts: pointer) {.cdecl, importc.} +proc GC_setCurrentStack(starts, pos: pointer) {.cdecl, importc.} + + +proc coroStart*(c: proc(), stacksize: int=coroDefaultStackSize) = + ## Adds coroutine to event loop. It does not run immediately. + var coro = Coroutine() + coro.fn = c + while coro.stack == nil: + coro.stack = alloc0(stacksize) + coro.stacksize = stacksize + coroutines.append(coro) + +{.push stackTrace: off.} +proc coroYield*(sleepTime: float=0) = + ## Stops coroutine execution and resumes no sooner than after ``sleeptime`` seconds. + ## Until then other coroutines are executed. + var oldFrame = getFrame() + var sp {.volatile.}: pointer + GC_setCurrentStack(current.stack, cast[pointer](addr sp)) + current.sleepTime = sleep_time + current.lastRun = epochTime() + if setjmp(current.ctx) == 0: + longjmp(mainCtx, 1) + setFrame(oldFrame) +{.pop.} + +proc coroRun*() = + ## Starts main event loop which exits when all coroutines exit. Calling this proc + ## starts execution of first coroutine. + var node = coroutines.head + var minDelay: float = 0 + var frame: PFrame + while node != nil: + var coro = node.value + current = coro + os.sleep(int(minDelay * 1000)) + + var remaining = coro.sleepTime - (epochTime() - coro.lastRun); + if remaining <= 0: + remaining = 0 + let res = setjmp(mainCtx) + if res == 0: + frame = getFrame() + if coro.started: # coroutine resumes + longjmp(coro.ctx, 1) + else: + coro.started = true # coroutine starts + var stackEnd = cast[pointer](cast[ByteAddress](coro.stack) + coro.stacksize) + GC_addStack(coro.stack) + coroSwitchStack(stackEnd) + coro.fn() + coroRestoreStack() + GC_removeStack(coro.stack) + var next = node.prev + coroutines.remove(node) + dealloc(coro.stack) + node = next + setFrame(frame) + else: + setFrame(frame) + + elif remaining > 0: + if minDelay > 0 and remaining > 0: + minDelay = min(remaining, minDelay) + else: + minDelay = remaining + + if node == nil or node.next == nil: + node = coroutines.head + else: + node = node.next + + +proc coroAlive*(c: proc()): bool = + ## Returns ``true`` if coroutine has not returned, ``false`` otherwise. + for coro in items(coroutines): + if coro.fn == c: + return true + +proc coroWait*(c: proc(), interval=0.01) = + ## Returns only after coroutine ``c`` has returned. ``interval`` is time in seconds how often. + while coroAlive(c): + coroYield interval + + +when isMainModule: + var stackCheckValue = 1100220033 + proc c2() + + proc c1() = + for i in 0 .. 3: + echo "c1" + coroYield 0.05 + echo "c1 exits" + + + proc c2() = + for i in 0 .. 3: + echo "c2" + coroYield 0.025 + coroWait(c1) + echo "c2 exits" + + coroStart(c1) + coroStart(c2) + coroRun() + echo "done ", stackCheckValue diff --git a/lib/pure/os.nim b/lib/pure/os.nim index c2c28c2b1..3d592a526 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -461,8 +461,10 @@ proc getLastAccessTime*(file: string): Time {.rtl, extern: "nos$1".} = proc getCreationTime*(file: string): Time {.rtl, extern: "nos$1".} = ## Returns the `file`'s creation time. - ## Note that under posix OS's, the returned time may actually be the time at - ## which the file's attribute's were last modified. + ## + ## **Note:** Under POSIX OS's, the returned time may actually be the time at + ## which the file's attribute's were last modified. See + ## `here <https://github.com/nim-lang/Nim/issues/1058>`_ for details. when defined(posix): var res: Stat if stat(file, res) < 0'i32: raiseOSError(osLastError()) diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim index add4bc0a8..81befccff 100644 --- a/lib/pure/osproc.nim +++ b/lib/pure/osproc.nim @@ -468,7 +468,14 @@ when defined(Windows) and not defined(useNimRtl): fileClose(si.hStdError) if e != nil: dealloc(e) - if success == 0: raiseOSError(lastError, command) + if success == 0: + const errInvalidParameter = 87.int + const errFileNotFound = 2.int + if lastError.int in {errInvalidParameter, errFileNotFound}: + raiseOSError(lastError, + "Requested command not found: '$1'. OS error:" % command) + else: + raiseOSError(lastError, command) # Close the handle now so anyone waiting is woken: discard closeHandle(procInfo.hThread) result.fProcessHandle = procInfo.hProcess @@ -778,7 +785,8 @@ elif not defined(useNimRtl): var error: cint let sizeRead = read(data.pErrorPipe[readIdx], addr error, sizeof(error)) if sizeRead == sizeof(error): - raiseOSError($strerror(error)) + raiseOSError("Could not find command: '$1'. OS error: $2" % + [$data.sysCommand, $strerror(error)]) return pid diff --git a/lib/pure/uri.nim b/lib/pure/uri.nim index 1890a9bf4..492de3b46 100644 --- a/lib/pure/uri.nim +++ b/lib/pure/uri.nim @@ -14,7 +14,7 @@ type Url* = distinct string Uri* = object - scheme*, username*, password*: string + scheme*, username*, password*: string hostname*, port*, path*, query*, anchor*: string opaque*: bool @@ -69,7 +69,7 @@ proc parseAuthority(authority: string, result: var Uri) = i.inc proc parsePath(uri: string, i: var int, result: var Uri) = - + i.inc parseUntil(uri, result.path, {'?', '#'}, i) # The 'mailto' scheme's PATH actually contains the hostname/username @@ -104,19 +104,23 @@ proc parseUri*(uri: string, result: var Uri) = var i = 0 # Check if this is a reference URI (relative URI) + let doubleSlash = uri.len > 1 and uri[1] == '/' if uri[i] == '/': - parsePath(uri, i, result) - return + # Make sure ``uri`` doesn't begin with '//'. + if not doubleSlash: + parsePath(uri, i, result) + return # Scheme i.inc parseWhile(uri, result.scheme, Letters + Digits + {'+', '-', '.'}, i) - if uri[i] != ':': + if uri[i] != ':' and not doubleSlash: # Assume this is a reference URI (relative URI) i = 0 result.scheme.setLen(0) parsePath(uri, i, result) return - i.inc # Skip ':' + if not doubleSlash: + i.inc # Skip ':' # Authority if uri[i] == '/' and uri[i+1] == '/': @@ -201,7 +205,7 @@ proc combine*(base: Uri, reference: Uri): Uri = ## ## let bar = combine(parseUri("http://example.com/foo/bar/"), parseUri("baz")) ## assert bar.path == "/foo/bar/baz" - + template setAuthority(dest, src: expr): stmt = dest.hostname = src.hostname dest.username = src.username @@ -369,6 +373,15 @@ when isMainModule: doAssert test.path == "test/no/slash" doAssert($test == str) + block: + let str = "//git@github.com:dom96/packages" + let test = parseUri(str) + doAssert test.scheme == "" + doAssert test.username == "git" + doAssert test.hostname == "github.com" + doAssert test.port == "dom96" + doAssert test.path == "/packages" + # Remove dot segments tests block: doAssert removeDotSegments("/foo/bar/baz") == "/foo/bar/baz" |