diff options
Diffstat (limited to 'lib/system')
-rw-r--r-- | lib/system/alloc.nim | 6 | ||||
-rw-r--r-- | lib/system/ansi_c.nim | 2 | ||||
-rw-r--r-- | lib/system/embedded.nim | 1 | ||||
-rw-r--r-- | lib/system/excpt.nim | 5 | ||||
-rw-r--r-- | lib/system/gc.nim | 18 | ||||
-rw-r--r-- | lib/system/gc_common.nim | 2 | ||||
-rw-r--r-- | lib/system/gc_ms.nim | 3 | ||||
-rw-r--r-- | lib/system/gc_regions.nim | 29 | ||||
-rw-r--r-- | lib/system/helpers.nim | 23 | ||||
-rw-r--r-- | lib/system/helpers2.nim | 2 | ||||
-rw-r--r-- | lib/system/jssys.nim | 2 | ||||
-rw-r--r-- | lib/system/mmdisp.nim | 3 | ||||
-rw-r--r-- | lib/system/nimscript.nim | 35 | ||||
-rw-r--r-- | lib/system/profiler.nim | 14 | ||||
-rw-r--r-- | lib/system/strmantle.nim | 2 | ||||
-rw-r--r-- | lib/system/sysio.nim | 17 | ||||
-rw-r--r-- | lib/system/timers.nim | 2 |
17 files changed, 129 insertions, 37 deletions
diff --git a/lib/system/alloc.nim b/lib/system/alloc.nim index b090117a9..e938dc475 100644 --- a/lib/system/alloc.nim +++ b/lib/system/alloc.nim @@ -20,7 +20,7 @@ template track(op, address, size) = # Each chunk starts at an address that is divisible by the page size. const - InitialMemoryRequest = 128 * PageSize # 0.5 MB + nimMinHeapPages {.intdefine.} = 128 # 0.5 MB SmallChunkSize = PageSize MaxFli = 30 MaxLog2Sli = 5 # 32, this cannot be increased without changing 'uint32' @@ -588,8 +588,8 @@ proc getBigChunk(a: var MemRegion, size: int): PBigChunk = sysAssert((size and PageMask) == 0, "getBigChunk: unaligned chunk") result = findSuitableBlock(a, fl, sl) if result == nil: - if size < InitialMemoryRequest: - result = requestOsChunks(a, InitialMemoryRequest) + if size < nimMinHeapPages * PageSize: + result = requestOsChunks(a, nimMinHeapPages * PageSize) splitChunk(a, result, size) else: result = requestOsChunks(a, size) diff --git a/lib/system/ansi_c.nim b/lib/system/ansi_c.nim index 38910dbd6..af34060d8 100644 --- a/lib/system/ansi_c.nim +++ b/lib/system/ansi_c.nim @@ -51,7 +51,7 @@ when defined(windows): elif defined(macosx) or defined(linux) or defined(freebsd) or defined(openbsd) or defined(netbsd) or defined(solaris) or defined(dragonfly) or defined(nintendoswitch) or defined(genode) or - defined(aix): + defined(aix) or hostOS == "standalone": const SIGABRT = cint(6) SIGFPE = cint(8) diff --git a/lib/system/embedded.nim b/lib/system/embedded.nim index 4d453fcca..fb89e7f0f 100644 --- a/lib/system/embedded.nim +++ b/lib/system/embedded.nim @@ -40,6 +40,7 @@ proc reraiseException() {.compilerRtl.} = proc writeStackTrace() = discard +proc unsetControlCHook() = discard proc setControlCHook(hook: proc () {.noconv.}) = discard proc closureIterSetupExc(e: ref Exception) {.compilerproc, inline.} = diff --git a/lib/system/excpt.nim b/lib/system/excpt.nim index 84a1da343..3efefead4 100644 --- a/lib/system/excpt.nim +++ b/lib/system/excpt.nim @@ -536,3 +536,8 @@ proc setControlCHook(hook: proc () {.noconv.}) = # ugly cast, but should work on all architectures: type SignalHandler = proc (sign: cint) {.noconv, benign.} c_signal(SIGINT, cast[SignalHandler](hook)) + +when not defined(useNimRtl): + proc unsetControlCHook() = + # proc to unset a hook set by setControlCHook + c_signal(SIGINT, signalHandler) diff --git a/lib/system/gc.nim b/lib/system/gc.nim index fb20edbbb..9ae388aa9 100644 --- a/lib/system/gc.nim +++ b/lib/system/gc.nim @@ -17,9 +17,9 @@ const CycleIncrease = 2 # is a multiplicative increase InitialCycleThreshold = 4*1024*1024 # X MB because cycle checking is slow - ZctThreshold = 500 # we collect garbage if the ZCT's size - # reaches this threshold - # this seems to be a good value + InitialZctThreshold = 500 # we collect garbage if the ZCT's size + # reaches this threshold + # this seems to be a good value withRealTime = defined(useRealtimeGC) when withRealTime and not declared(getTicks): @@ -78,6 +78,7 @@ type when nimCoroutines: activeStack: ptr GcStack # current executing coroutine stack. cycleThreshold: int + zctThreshold: int when useCellIds: idGenerator: int zct: CellSeq # the zero count table @@ -253,6 +254,7 @@ proc initGC() = when traceGC: for i in low(CellState)..high(CellState): init(states[i]) gch.cycleThreshold = InitialCycleThreshold + gch.zctThreshold = InitialZctThreshold gch.stat.stackScans = 0 gch.stat.cycleCollections = 0 gch.stat.maxThreshold = 0 @@ -771,11 +773,7 @@ proc collectCTBody(gch: var GcHeap) = c_fprintf(stdout, "[GC] missed deadline: %ld\n", duration) proc collectCT(gch: var GcHeap) = - # stackMarkCosts prevents some pathological behaviour: Stack marking - # becomes more expensive with large stacks and large stacks mean that - # cells with RC=0 are more likely to be kept alive by the stack. - let stackMarkCosts = max(stackSize() div (16*sizeof(int)), ZctThreshold) - if (gch.zct.len >= stackMarkCosts or (cycleGC and + if (gch.zct.len >= gch.zctThreshold or (cycleGC and getOccupiedMem(gch.region)>=gch.cycleThreshold) or alwaysGC) and gch.recGcLock == 0: when false: @@ -783,6 +781,7 @@ proc collectCT(gch: var GcHeap) = cellsetReset(gch.marked) markForDebug(gch) collectCTBody(gch) + gch.zctThreshold = max(InitialZctThreshold, gch.zct.len * CycleIncrease) when withRealTime: proc toNano(x: int): Nanos {.inline.} = @@ -793,10 +792,11 @@ when withRealTime: proc GC_step(gch: var GcHeap, us: int, strongAdvice: bool) = gch.maxPause = us.toNano - if (gch.zct.len >= ZctThreshold or (cycleGC and + if (gch.zct.len >= gch.zctThreshold or (cycleGC and getOccupiedMem(gch.region)>=gch.cycleThreshold) or alwaysGC) or strongAdvice: collectCTBody(gch) + gch.zctThreshold = max(InitialZctThreshold, gch.zct.len * CycleIncrease) proc GC_step*(us: int, strongAdvice = false, stackSize = -1) {.noinline.} = if stackSize >= 0: diff --git a/lib/system/gc_common.nim b/lib/system/gc_common.nim index 565453298..ebd3dada2 100644 --- a/lib/system/gc_common.nim +++ b/lib/system/gc_common.nim @@ -418,7 +418,7 @@ proc prepareDealloc(cell: PCell) = decTypeSize(cell, t) proc deallocHeap*(runFinalizers = true; allowGcAfterwards = true) = - ## Frees the thread local heap. Runs every finalizer if ``runFinalizers``` + ## Frees the thread local heap. Runs every finalizer if ``runFinalizers`` ## is true. If ``allowGcAfterwards`` is true, a minimal amount of allocation ## happens to ensure the GC can continue to work after the call ## to ``deallocHeap``. diff --git a/lib/system/gc_ms.nim b/lib/system/gc_ms.nim index d8cb3e2d0..aa5fb6aea 100644 --- a/lib/system/gc_ms.nim +++ b/lib/system/gc_ms.nim @@ -485,8 +485,9 @@ proc collectCTBody(gch: var GcHeap) = sysAssert(allocInv(gch.region), "collectCT: end") proc collectCT(gch: var GcHeap; size: int) = + let fmem = getFreeMem(gch.region) if (getOccupiedMem(gch.region) >= gch.cycleThreshold or - size > getFreeMem(gch.region)) and gch.recGcLock == 0: + size > fmem and fmem > InitialThreshold) and gch.recGcLock == 0: collectCTBody(gch) when not defined(useNimRtl): diff --git a/lib/system/gc_regions.nim b/lib/system/gc_regions.nim index 8a1446944..3b908fb08 100644 --- a/lib/system/gc_regions.nim +++ b/lib/system/gc_regions.nim @@ -23,6 +23,21 @@ when defined(nimphpext): proc osDeallocPages(p: pointer, size: int) {.inline.} = efree(p) +elif defined(useMalloc): + proc roundup(x, v: int): int {.inline.} = + result = (x + (v-1)) and not (v-1) + proc emalloc(size: int): pointer {.importc: "malloc", header: "<stdlib.h>".} + proc efree(mem: pointer) {.importc: "free", header: "<stdlib.h>".} + + proc osAllocPages(size: int): pointer {.inline.} = + emalloc(size) + + proc osTryAllocPages(size: int): pointer {.inline.} = + emalloc(size) + + proc osDeallocPages(p: pointer, size: int) {.inline.} = + efree(p) + else: include osalloc @@ -108,6 +123,8 @@ template `+!`(p: pointer, s: int): pointer = template `-!`(p: pointer, s: int): pointer = cast[pointer](cast[int](p) -% s) +const nimMinHeapPages {.intdefine.} = 4 + proc allocSlowPath(r: var MemRegion; size: int) = # we need to ensure that the underlying linked list # stays small. Say we want to grab 16GB of RAM with some @@ -116,9 +133,8 @@ proc allocSlowPath(r: var MemRegion; size: int) = # 8MB, 16MB, 32MB, 64MB, 128MB, 512MB, 1GB, 2GB, 4GB, 8GB, # 16GB --> list contains only 20 elements! That's reasonable. if (r.totalSize and 1) == 0: - r.nextChunkSize = - if r.totalSize < 64 * 1024: PageSize*4 - else: r.nextChunkSize*2 + r.nextChunkSize = if r.totalSize < 64 * 1024: PageSize*nimMinHeapPages + else: r.nextChunkSize*2 var s = roundup(size+sizeof(BaseChunk), PageSize) var fresh: Chunk if s > r.nextChunkSize: @@ -242,6 +258,13 @@ proc deallocAll*() = tlRegion.deallocAll() proc deallocOsPages(r: var MemRegion) = r.deallocAll() template withScratchRegion*(body: untyped) = + let obs = obstackPtr() + try: + body + finally: + setObstackPtr(obs) + +when false: var scratch: MemRegion let oldRegion = tlRegion tlRegion = scratch diff --git a/lib/system/helpers.nim b/lib/system/helpers.nim index fb1218684..a7e47915e 100644 --- a/lib/system/helpers.nim +++ b/lib/system/helpers.nim @@ -1,11 +1,28 @@ -## helpers used system.nim and other modules, avoids code duplication while -## also minimizing symbols exposed in system.nim +# helpers used system.nim and other modules, avoids code duplication while +# also minimizing symbols exposed in system.nim # # TODO: move other things here that should not be exposed in system.nim proc lineInfoToString(file: string, line, column: int): string = file & "(" & $line & ", " & $column & ")" -proc `$`(info: type(instantiationInfo(0))): string = +type InstantiationInfo = tuple[filename: string, line: int, column: int] + +proc `$`(info: InstantiationInfo): string = # The +1 is needed here + # instead of overriding `$` (and changing its meaning), consider explicit name. lineInfoToString(info.fileName, info.line, info.column+1) + +proc isNamedTuple(T: type): bool = + ## return true for named tuples, false for any other type. + when T isnot tuple: result = false + else: + var t: T + for name, _ in t.fieldPairs: + when name == "Field0": + return compiles(t.Field0) + else: + return true + # empty tuple should be un-named, + # see https://github.com/nim-lang/Nim/issues/8861#issue-356631191 + return false diff --git a/lib/system/helpers2.nim b/lib/system/helpers2.nim index 1c9e7c068..c67a2c278 100644 --- a/lib/system/helpers2.nim +++ b/lib/system/helpers2.nim @@ -1,3 +1,5 @@ +# imported by other modules, unlike helpers.nim which is included + template formatErrorIndexBound*[T](i, a, b: T): string = "index out of bounds: (a:" & $a & ") <= (i:" & $i & ") <= (b:" & $b & ") " diff --git a/lib/system/jssys.nim b/lib/system/jssys.nim index 8be19e5b8..d7718e4f4 100644 --- a/lib/system/jssys.nim +++ b/lib/system/jssys.nim @@ -46,7 +46,7 @@ proc nimCharToStr(x: char): string {.compilerproc.} = result[0] = x proc isNimException(): bool {.asmNoStackFrame.} = - asm "return `lastJSError`.m_type;" + asm "return `lastJSError` && `lastJSError`.m_type;" proc getCurrentException*(): ref Exception {.compilerRtl, benign.} = if isNimException(): result = cast[ref Exception](lastJSError) diff --git a/lib/system/mmdisp.nim b/lib/system/mmdisp.nim index 89bc11d2c..9cc7ab323 100644 --- a/lib/system/mmdisp.nim +++ b/lib/system/mmdisp.nim @@ -72,6 +72,8 @@ when defined(boehmgc): proc boehmGCincremental {. importc: "GC_enable_incremental", boehmGC.} proc boehmGCfullCollect {.importc: "GC_gcollect", boehmGC.} + proc boehmGC_set_all_interior_pointers(flag: cint) {. + importc: "GC_set_all_interior_pointers", boehmGC.} proc boehmAlloc(size: int): pointer {.importc: "GC_malloc", boehmGC.} proc boehmAllocAtomic(size: int): pointer {. importc: "GC_malloc_atomic", boehmGC.} @@ -148,6 +150,7 @@ when defined(boehmgc): proc nimGC_setStackBottom(theStackBottom: pointer) = discard proc initGC() = + boehmGC_set_all_interior_pointers(0) boehmGCinit() when hasThreadSupport: boehmGC_allow_register_threads() diff --git a/lib/system/nimscript.nim b/lib/system/nimscript.nim index fc4b574e4..e879fda83 100644 --- a/lib/system/nimscript.nim +++ b/lib/system/nimscript.nim @@ -46,7 +46,7 @@ proc copyDir(src, dest: string) {. tags: [ReadIOEffect, WriteIOEffect], raises: [OSError].} = builtin proc createDir(dir: string) {.tags: [WriteIOEffect], raises: [OSError].} = builtin -proc getOsError: string = builtin +proc getError: string = builtin proc setCurrentDir(dir: string) = builtin proc getCurrentDir*(): string = ## Retrieves the current working directory. @@ -143,6 +143,7 @@ proc existsDir*(dir: string): bool = proc selfExe*(): string = ## Returns the currently running nim or nimble executable. + # TODO: consider making this as deprecated alias of `getCurrentCompilerExe` builtin proc toExe*(filename: string): string = @@ -177,9 +178,12 @@ var mode*: ScriptMode ## Set this to influence how mkDir, rmDir, rmFile etc. ## behave +template checkError(exc: untyped): untyped = + let err = getError() + if err.len > 0: raise newException(exc, err) + template checkOsError = - let err = getOsError() - if err.len > 0: raise newException(OSError, err) + checkError(OSError) template log(msg: string, body: untyped) = if mode in {ScriptMode.Verbose, ScriptMode.Whatif}: @@ -331,6 +335,26 @@ proc cppDefine*(define: string) = ## needs to be mangled. builtin +proc stdinReadLine(): TaintedString {. + tags: [ReadIOEffect], raises: [IOError].} = + builtin + +proc stdinReadAll(): TaintedString {. + tags: [ReadIOEffect], raises: [IOError].} = + builtin + +proc readLineFromStdin*(): TaintedString {.raises: [IOError].} = + ## Reads a line of data from stdin - blocks until \n or EOF which happens when stdin is closed + log "readLineFromStdin": + result = stdinReadLine() + checkError(EOFError) + +proc readAllFromStdin*(): TaintedString {.raises: [IOError].} = + ## Reads all data from stdin - blocks until EOF which happens when stdin is closed + log "readAllFromStdin": + result = stdinReadAll() + checkError(EOFError) + when not defined(nimble): template `==?`(a, b: string): bool = cmpIgnoreStyle(a, b) == 0 template task*(name: untyped; description: string; body: untyped): untyped = @@ -340,14 +364,15 @@ when not defined(nimble): ## .. code-block:: nim ## task build, "default build is via the C backend": ## setCommand "c" - proc `name Task`*() = body + proc `name Task`*() = + setCommand "nop" + body let cmd = getCommand() if cmd.len == 0 or cmd ==? "help": setCommand "help" writeTask(astToStr(name), description) elif cmd ==? astToStr(name): - setCommand "nop" `name Task`() # nimble has its own implementation for these things. diff --git a/lib/system/profiler.nim b/lib/system/profiler.nim index ffd6fd0c5..0649f1176 100644 --- a/lib/system/profiler.nim +++ b/lib/system/profiler.nim @@ -13,6 +13,9 @@ # (except perhaps loops that have no side-effects). At every Nth call a # stack trace is taken. A stack tace is a list of cstrings. +when defined(profiler) and defined(memProfiler): + {.error: "profiler and memProfiler cannot be defined at the same time (See Embedded Stack Trace Profiler (ESTP) User Guide) for more details".} + {.push profiler: off.} const @@ -57,13 +60,13 @@ proc captureStackTrace(f: PFrame, st: var StackTrace) = b = b.prev var - profilingRequestedHook*: proc (): bool {.nimcall, benign.} + profilingRequestedHook*: proc (): bool {.nimcall, locks: 0, gcsafe.} ## set this variable to provide a procedure that implements a profiler in ## user space. See the `nimprof` module for a reference implementation. when defined(memProfiler): type - MemProfilerHook* = proc (st: StackTrace, requestedSize: int) {.nimcall, benign.} + MemProfilerHook* = proc (st: StackTrace, requestedSize: int) {.nimcall, locks: 0, gcsafe.} var profilerHook*: MemProfilerHook @@ -87,9 +90,10 @@ else: proc callProfilerHook(hook: ProfilerHook) {.noinline.} = # 'noinline' so that 'nimProfile' does not perform the stack allocation # in the common case. - var st: StackTrace - captureStackTrace(framePtr, st) - hook(st) + when not defined(nimdoc): + var st: StackTrace + captureStackTrace(framePtr, st) + hook(st) proc nimProfile() = ## This is invoked by the compiler in every loop and on every proc entry! diff --git a/lib/system/strmantle.nim b/lib/system/strmantle.nim index ceaecb4f9..3c681fc53 100644 --- a/lib/system/strmantle.nim +++ b/lib/system/strmantle.nim @@ -7,7 +7,7 @@ # distribution, for details about the copyright. # -## Compilerprocs for strings that do not depend on the string implementation. +# Compilerprocs for strings that do not depend on the string implementation. proc cmpStrings(a, b: string): int {.inline, compilerProc.} = let alen = a.len diff --git a/lib/system/sysio.nim b/lib/system/sysio.nim index 20964b166..5b0278d74 100644 --- a/lib/system/sysio.nim +++ b/lib/system/sysio.nim @@ -74,10 +74,21 @@ proc raiseEIO(msg: string) {.noinline, noreturn.} = proc raiseEOF() {.noinline, noreturn.} = sysFatal(EOFError, "EOF reached") +proc strerror(errnum: cint): cstring {.importc, header: "<string.h>".} + +when not defined(NimScript): + var + errno {.importc, header: "<errno.h>".}: cint ## error variable + proc checkErr(f: File) = - if c_ferror(f) != 0: - c_clearerr(f) - raiseEIO("Unknown IO Error") + when not defined(NimScript): + if c_ferror(f) != 0: + let msg = "errno: " & $errno & " `" & $strerror(errno) & "`" + c_clearerr(f) + raiseEIO(msg) + else: + # shouldn't happen + quit(1) {.push stackTrace:off, profiler:off.} proc readBuffer(f: File, buffer: pointer, len: Natural): int = diff --git a/lib/system/timers.nim b/lib/system/timers.nim index dd8350e2d..4cd2fe840 100644 --- a/lib/system/timers.nim +++ b/lib/system/timers.nim @@ -49,7 +49,7 @@ elif defined(macosx): mach_timebase_info(timeBaseInfo) proc `-`(a, b: Ticks): Nanos = - result = (a.int64 - b.int64) * timeBaseInfo.numer div timeBaseInfo.denom + result = (a.int64 - b.int64) * timeBaseInfo.numer div timeBaseInfo.denom elif defined(posixRealtime): type |