diff options
author | Araq <rumpf_a@web.de> | 2011-06-15 02:09:02 +0200 |
---|---|---|
committer | Araq <rumpf_a@web.de> | 2011-06-15 02:09:02 +0200 |
commit | 4fa80956b89c2ee06d8940018101240efec7dc02 (patch) | |
tree | 5e3d03d41122f0ad8def8e7d9be8b2eff2a2ea81 | |
parent | adbb48fbcecd82e60c3a25ee006baa0aa5dd7b10 (diff) | |
download | Nim-4fa80956b89c2ee06d8940018101240efec7dc02.tar.gz |
compiler can emulate thread local variables
-rwxr-xr-x | compiler/ccgexprs.nim | 15 | ||||
-rw-r--r-- | compiler/ccgthreadvars.nim | 54 | ||||
-rwxr-xr-x | compiler/cgen.nim | 106 | ||||
-rwxr-xr-x | compiler/msgs.nim | 13 | ||||
-rw-r--r-- | compiler/semthreads.nim | 13 | ||||
-rwxr-xr-x | lib/system.nim | 9 | ||||
-rwxr-xr-x | lib/system/excpt.nim | 185 | ||||
-rwxr-xr-x | lib/system/gc.nim | 3 | ||||
-rwxr-xr-x | lib/system/threads.nim | 66 | ||||
-rwxr-xr-x | todo.txt | 49 |
10 files changed, 259 insertions, 254 deletions
diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index faf37247e..d054e5bac 100755 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -1482,7 +1482,6 @@ proc genSetConstr(p: BProc, e: PNode, d: var TLoc) = # incl(tmp, d); incl(tmp, e); inclRange(tmp, f, g); var a, b, idx: TLoc - ts: string if nfAllConst in e.flags: putIntoDest(p, d, e.typ, genSetNode(p, e)) else: @@ -1504,7 +1503,7 @@ proc genSetConstr(p: BProc, e: PNode, d: var TLoc) = [rdLoc(d), rdSetElemLoc(a, e.typ)]) else: # small set - ts = "NI" & $(getSize(e.typ) * 8) + var ts = "NI" & $(getSize(e.typ) * 8) appf(p.s[cpsStmts], "$1 = 0;$n", [rdLoc(d)]) for i in countup(0, sonsLen(e) - 1): if e.sons[i].kind == nkRange: @@ -1645,10 +1644,17 @@ proc expr(p: BProc, e: PNode, d: var TLoc) = of skEnumField: putIntoDest(p, d, e.typ, toRope(sym.position)) of skVar: - if (sfGlobal in sym.flags): genVarPrototype(p.module, sym) + if sfGlobal in sym.flags: genVarPrototype(p.module, sym) if ((sym.loc.r == nil) or (sym.loc.t == nil)): InternalError(e.info, "expr: var not init " & sym.name.s) - putLocIntoDest(p, d, sym.loc) + if sfThreadVar in sym.flags: + AccessThreadLocalVar(p, sym) + if emulatedThreadVars(): + putIntoDest(p, d, sym.loc.t, con("NimTV->", sym.loc.r)) + else: + putLocIntoDest(p, d, sym.loc) + else: + putLocIntoDest(p, d, sym.loc) of skForVar, skTemp: if ((sym.loc.r == nil) or (sym.loc.t == nil)): InternalError(e.info, "expr: temp not init " & sym.name.s) @@ -1727,7 +1733,6 @@ proc genConstExpr(p: BProc, n: PNode): PRope = # XXX: tySequence! result = genConstSimpleList(p, n) else: - # result := genLiteral(p, n) var d: TLoc initLocExpr(p, n, d) result = rdLoc(d) diff --git a/compiler/ccgthreadvars.nim b/compiler/ccgthreadvars.nim new file mode 100644 index 000000000..e92e955c5 --- /dev/null +++ b/compiler/ccgthreadvars.nim @@ -0,0 +1,54 @@ +# +# +# The Nimrod Compiler +# (c) Copyright 2011 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## Thread var support for crappy architectures that lack native support for +## thread local storage. + +proc AccessThreadLocalVar(p: BProc, s: PSym) = + if optThreads in gGlobalOptions: + if platform.OS[targetOS].props.contains(ospLacksThreadVars): + if not p.ThreadVarAccessed: + p.ThreadVarAccessed = true + p.module.usesThreadVars = true + appf(p.s[cpsLocals], "NimThreadVars* NimTV;$n") + appcg(p, cpsInit, "NimTV=(NimThreadVars*)#GetThreadLocalVars();$n") + +var + nimtv: PRope # nimrod thread vars + nimtvDeps: seq[PType] = @[] + nimtvDeclared = initIntSet() + +proc emulatedThreadVars(): bool {.inline.} = + result = optThreads in gGlobalOptions and + platform.OS[targetOS].props.contains(ospLacksThreadVars) + +proc declareThreadVar(m: BModule, s: PSym, isExtern: bool) = + if emulatedThreadVars(): + # we gather all thread locals var into a struct; we need to allocate + # storage for that somehow, can't use the thread local storage + # allocator for it :-( + if not containsOrIncl(nimtvDeclared, s.id): + nimtvDeps.add(s.loc.t) + appf(nimtv, "$1 $2;$n", [getTypeDesc(m, s.loc.t), s.loc.r]) + else: + if isExtern: app(m.s[cfsVars], "extern ") + if optThreads in gGlobalOptions: app(m.s[cfsVars], "NIM_THREADVAR ") + app(m.s[cfsVars], getTypeDesc(m, s.loc.t)) + appf(m.s[cfsVars], " $1;$n", [s.loc.r]) + +proc generateThreadLocalStorage(m: BModule) = + if nimtv != nil and (m.usesThreadVars or sfMainModule in m.module.flags): + for t in items(nimtvDeps): discard getTypeDesc(m, t) + appf(m.s[cfsSeqTypes], "typedef struct {$1} NimThreadVars;$n", [nimtv]) + +proc GenerateThreadVarsSize(m: BModule) = + if nimtv != nil: + app(m.s[cfsProcs], + "NI NimThreadVarsSize(){return (NI)sizeof(NimThreadVars);}" & tnl) + diff --git a/compiler/cgen.nim b/compiler/cgen.nim index 449814d01..1ecadbec4 100755 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -66,6 +66,7 @@ type s: TCProcSections # the procs sections; short name for readability prc: PSym # the Nimrod proc that this C proc belongs to BeforeRetNeeded: bool # true iff 'BeforeRet' label for proc is needed + ThreadVarAccessed: bool # true if the proc already accessed some threadvar nestedTryStmts: seq[PNode] # in how many nested try statements we are # (the vars must be volatile then) labels: Natural # for generating unique labels in the C proc @@ -85,6 +86,7 @@ type filename*: string s*: TCFileSections # sections of the C file PreventStackTrace: bool # true if stack traces need to be prevented + usesThreadVars: bool # true if the module uses a thread var cfilename*: string # filename of the module (including path, # without extension) typeCache*: TIdTable # cache the generated types @@ -386,29 +388,12 @@ proc localDebugInfo(p: BProc, s: PSym) = if {optStackTrace, optEndb} * p.options != {optStackTrace, optEndb}: return # XXX work around a bug: No type information for open arrays possible: if skipTypes(s.typ, abstractVar).kind == tyOpenArray: return - if gCmd == cmdCompileToLLVM: - # "address" is the 0th field - # "typ" is the 1rst field - # "name" is the 2nd field - var name = cstringLit(p, p.s[cpsInit], normalize(s.name.s)) - if (s.kind == skParam) and not ccgIntroducedPtr(s): allocParam(p, s) - inc(p.labels, 3) - appf(p.s[cpsInit], "%LOC$6 = getelementptr %TF* %F, %NI 0, $1, %NI 0$n" & - "%LOC$7 = getelementptr %TF* %F, %NI 0, $1, %NI 1$n" & - "%LOC$8 = getelementptr %TF* %F, %NI 0, $1, %NI 2$n" & - "store i8* $2, i8** %LOC$6$n" & "store $3* $4, $3** %LOC$7$n" & - "store i8* $5, i8** %LOC$8$n", [toRope(p.frameLen), s.loc.r, - getTypeDesc(p.module, "TNimType"), - genTypeInfo(p.module, s.loc.t), name, - toRope(p.labels), toRope(p.labels - 1), - toRope(p.labels - 2)]) - else: - var a = con("&", s.loc.r) - if (s.kind == skParam) and ccgIntroducedPtr(s): a = s.loc.r - appf(p.s[cpsInit], - "F.s[$1].address = (void*)$3; F.s[$1].typ = $4; F.s[$1].name = $2;$n", - [toRope(p.frameLen), makeCString(normalize(s.name.s)), a, - genTypeInfo(p.module, s.loc.t)]) + var a = con("&", s.loc.r) + if (s.kind == skParam) and ccgIntroducedPtr(s): a = s.loc.r + appf(p.s[cpsInit], + "F.s[$1].address = (void*)$3; F.s[$1].typ = $4; F.s[$1].name = $2;$n", + [toRope(p.frameLen), makeCString(normalize(s.name.s)), a, + genTypeInfo(p.module, s.loc.t)]) inc(p.frameLen) proc assignLocalVar(p: BProc, s: PSym) = @@ -417,55 +402,35 @@ proc assignLocalVar(p: BProc, s: PSym) = # for each module that uses them! if s.loc.k == locNone: fillLoc(s.loc, locLocalVar, s.typ, mangleName(s), OnStack) - if gCmd == cmdCompileToLLVM: - appf(p.s[cpsLocals], "$1 = alloca $2$n", - [s.loc.r, getTypeDesc(p.module, s.loc.t)]) - incl(s.loc.flags, lfIndirect) - else: - app(p.s[cpsLocals], getTypeDesc(p.module, s.loc.t)) - if sfRegister in s.flags: app(p.s[cpsLocals], " register") - if (sfVolatile in s.flags) or (p.nestedTryStmts.len > 0): - app(p.s[cpsLocals], " volatile") - appf(p.s[cpsLocals], " $1;$n", [s.loc.r]) + app(p.s[cpsLocals], getTypeDesc(p.module, s.loc.t)) + if sfRegister in s.flags: app(p.s[cpsLocals], " register") + if (sfVolatile in s.flags) or (p.nestedTryStmts.len > 0): + app(p.s[cpsLocals], " volatile") + appf(p.s[cpsLocals], " $1;$n", [s.loc.r]) localDebugInfo(p, s) -proc declareThreadVar(m: BModule, s: PSym) = - if optThreads in gGlobalOptions: - if platform.OS[targetOS].props.contains(ospLacksThreadVars): - # we gather all thread locals var into a struct and put that into - # nim__dat.c; we need to allocate storage for that somehow, can't use - # the thread local storage allocator for it :-( - # XXX we need to adapt expr() too, every reference to a thread local var - # generates quite some code ... - InternalError("no workaround for lack of thread local vars implemented") - else: - app(m.s[cfsVars], "NIM_THREADVAR ") - app(m.s[cfsVars], getTypeDesc(m, s.loc.t)) - else: - app(m.s[cfsVars], getTypeDesc(m, s.loc.t)) +include ccgthreadvars proc assignGlobalVar(p: BProc, s: PSym) = if s.loc.k == locNone: fillLoc(s.loc, locGlobalVar, s.typ, mangleName(s), OnHeap) useHeader(p.module, s) - if lfNoDecl in s.loc.flags: return - if sfImportc in s.flags: app(p.module.s[cfsVars], "extern ") - if sfThreadVar in s.flags: declareThreadVar(p.module, s) - else: app(p.module.s[cfsVars], getTypeDesc(p.module, s.loc.t)) - if sfRegister in s.flags: app(p.module.s[cfsVars], " register") - if sfVolatile in s.flags: app(p.module.s[cfsVars], " volatile") - appf(p.module.s[cfsVars], " $1;$n", [s.loc.r]) - if {optStackTrace, optEndb} * p.module.module.options == - {optStackTrace, optEndb}: + if lfNoDecl in s.loc.flags: return + if sfThreadVar in s.flags: + declareThreadVar(p.module, s, sfImportc in s.flags) + else: + if sfImportc in s.flags: app(p.module.s[cfsVars], "extern ") + app(p.module.s[cfsVars], getTypeDesc(p.module, s.loc.t)) + if sfRegister in s.flags: app(p.module.s[cfsVars], " register") + if sfVolatile in s.flags: app(p.module.s[cfsVars], " volatile") + appf(p.module.s[cfsVars], " $1;$n", [s.loc.r]) + if p.module.module.options * {optStackTrace, optEndb} == + {optStackTrace, optEndb}: appcg(p.module, p.module.s[cfsDebugInit], "#dbgRegisterGlobal($1, &$2, $3);$n", [cstringLit(p, p.module.s[cfsDebugInit], normalize(s.owner.name.s & '.' & s.name.s)), s.loc.r, genTypeInfo(p.module, s.typ)]) - -proc iff(cond: bool, the, els: PRope): PRope = - if cond: result = the - else: result = els proc assignParam(p: BProc, s: PSym) = assert(s.loc.r != nil) @@ -691,9 +656,8 @@ proc genProcPrototype(m: BModule, sym: PSym) = if lfDynamicLib in sym.loc.Flags: if sym.owner.id != m.module.id and not ContainsOrIncl(m.declaredThings, sym.id): - appff(m.s[cfsVars], "extern $1 $2;$n", - "@$2 = linkonce global $1 zeroinitializer$n", - [getTypeDesc(m, sym.loc.t), mangleDynLibProc(sym)]) + appf(m.s[cfsVars], "extern $1 $2;$n", + [getTypeDesc(m, sym.loc.t), mangleDynLibProc(sym)]) if gCmd == cmdCompileToLLVM: incl(sym.loc.flags, lfIndirect) elif not ContainsOrIncl(m.declaredProtos, sym.id): appf(m.s[cfsProcHeaders], "$1;$n", [genProcHeader(m, sym)]) @@ -725,20 +689,16 @@ proc genVarPrototype(m: BModule, sym: PSym) = assert(sfGlobal in sym.flags) useHeader(m, sym) fillLoc(sym.loc, locGlobalVar, sym.typ, mangleName(sym), OnHeap) - if (lfNoDecl in sym.loc.Flags) or - ContainsOrIncl(m.declaredThings, sym.id): + if (lfNoDecl in sym.loc.Flags) or ContainsOrIncl(m.declaredThings, sym.id): return if sym.owner.id != m.module.id: # else we already have the symbol generated! assert(sym.loc.r != nil) - if gCmd == cmdCompileToLLVM: - incl(sym.loc.flags, lfIndirect) - appf(m.s[cfsVars], "$1 = linkonce global $2 zeroinitializer$n", - [sym.loc.r, getTypeDesc(m, sym.loc.t)]) - else: + if sfThreadVar in sym.flags: + declareThreadVar(m, sym, true) + else: app(m.s[cfsVars], "extern ") - if sfThreadVar in sym.flags: declareThreadVar(m, sym) - else: app(m.s[cfsVars], getTypeDesc(m, sym.loc.t)) + app(m.s[cfsVars], getTypeDesc(m, sym.loc.t)) if sfRegister in sym.flags: app(m.s[cfsVars], " register") if sfVolatile in sym.flags: app(m.s[cfsVars], " volatile") appf(m.s[cfsVars], " $1;$n", [sym.loc.r]) @@ -898,6 +858,7 @@ proc genInitCode(m: BModule) = proc genModule(m: BModule, cfilenoext: string): PRope = result = getFileHeader(cfilenoext) generateHeaders(m) + generateThreadLocalStorage(m) for i in countup(low(TCFileSection), cfsProcs): app(result, m.s[i]) proc rawNewModule(module: PSym, filename: string): BModule = @@ -990,6 +951,7 @@ proc writeModule(m: BModule) = if sfMainModule in m.module.flags: # generate main file: app(m.s[cfsProcHeaders], mainModProcs) + GenerateThreadVarsSize(m) var code = genModule(m, cfilenoext) when hasTinyCBackend: diff --git a/compiler/msgs.nim b/compiler/msgs.nim index ebc646483..ed88ff551 100755 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -86,14 +86,15 @@ type errCannotInterpretNodeX, errFieldXNotFound, errInvalidConversionFromTypeX, errAssertionFailed, errCannotGenerateCodeForX, errXRequiresOneArgument, errUnhandledExceptionX, errCyclicTree, errXisNoMacroOrTemplate, - errXhasSideEffects, errIteratorExpected, errDifferentHeaps, - errUser, + errXhasSideEffects, errIteratorExpected, + errUser, warnCannotOpenFile, warnOctalEscape, warnXIsNeverRead, warnXmightNotBeenInit, warnCannotWriteMO2, warnCannotReadMO2, warnDeprecated, warnSmallLshouldNotBeUsed, warnUnknownMagic, warnRedefinitionOfLabel, warnUnknownSubstitutionX, warnLanguageXNotSupported, warnCommentXIgnored, warnXisPassedToProcVar, warnDerefDeprecated, warnAnalysisLoophole, + warnDifferentHeaps, warnUser, hintSuccess, hintSuccessX, hintLineTooLong, hintXDeclaredButNotUsed, hintConvToBaseNotNeeded, @@ -310,7 +311,6 @@ const errXisNoMacroOrTemplate: "\'$1\' is no macro or template", errXhasSideEffects: "\'$1\' can have side effects", errIteratorExpected: "iterator within for loop context expected", - errDifferentHeaps: "possible inconsistency of thread local heaps", errUser: "$1", warnCannotOpenFile: "cannot open \'$1\' [CannotOpenFile]", warnOctalEscape: "octal escape sequences do not exist; leading zero is ignored [OctalEscape]", @@ -327,7 +327,8 @@ const warnCommentXIgnored: "comment \'$1\' ignored [CommentXIgnored]", warnXisPassedToProcVar: "\'$1\' is passed to a procvar; deprecated [XisPassedToProcVar]", warnDerefDeprecated: "p^ is deprecated; use p[] instead [DerefDeprecated]", - warnAnalysisLoophole: "thread analysis incomplete due to indirect call '$1' [AnalysisLoophole]", + warnAnalysisLoophole: "thread analysis incomplete due to unkown call '$1' [AnalysisLoophole]", + warnDifferentHeaps: "possible inconsistency of thread local heaps", warnUser: "$1 [User]", hintSuccess: "operation successful [Success]", hintSuccessX: "operation successful ($1 lines compiled; $2 sec total) [SuccessX]", @@ -345,12 +346,12 @@ const hintUser: "$1 [User]"] const - WarningsToStr*: array[0..16, string] = ["CannotOpenFile", "OctalEscape", + WarningsToStr*: array[0..17, string] = ["CannotOpenFile", "OctalEscape", "XIsNeverRead", "XmightNotBeenInit", "CannotWriteMO2", "CannotReadMO2", "Deprecated", "SmallLshouldNotBeUsed", "UnknownMagic", "RedefinitionOfLabel", "UnknownSubstitutionX", "LanguageXNotSupported", "CommentXIgnored", "XisPassedToProcVar", "DerefDeprecated", - "AnalysisLoophole", "User"] + "AnalysisLoophole", "DifferentHeaps", "User"] HintsToStr*: array[0..13, string] = ["Success", "SuccessX", "LineTooLong", "XDeclaredButNotUsed", "ConvToBaseNotNeeded", "ConvFromXtoItselfNotNeeded", diff --git a/compiler/semthreads.nim b/compiler/semthreads.nim index 70ae3d3d3..3ca52fc30 100644 --- a/compiler/semthreads.nim +++ b/compiler/semthreads.nim @@ -138,7 +138,7 @@ proc writeAccess(c: PProcCtx, n: PNode, owner: TThreadOwner) = of toVoid, toUndefined: InternalError(n.info, "writeAccess") of toTheirs, toMine: if lastOwner != owner and owner != toNil: - LocalError(n.info, errDifferentHeaps) + Message(n.info, warnDifferentHeaps) else: # we could not backtrack to a concrete symbol, but that's fine: var lastOwner = analyseSym(c, n) @@ -147,7 +147,7 @@ proc writeAccess(c: PProcCtx, n: PNode, owner: TThreadOwner) = of toVoid, toUndefined: InternalError(n.info, "writeAccess") of toTheirs, toMine: if lastOwner != owner and owner != toNil: - LocalError(n.info, errDifferentHeaps) + Message(n.info, warnDifferentHeaps) proc analyseAssign(c: PProcCtx, le, ri: PNode) = var y = analyse(c, ri) # read access; ok @@ -171,7 +171,7 @@ proc analyseCall(c: PProcCtx, n: PNode): TThreadOwner = newCtx.mapping[formal.id] = call.args[i-1] pushInfoContext(n.info) result = analyse(newCtx, prc.ast.sons[codePos]) - if prc.ast.sons[codePos].kind == nkEmpty: + if prc.ast.sons[codePos].kind == nkEmpty and sfNoSideEffect notin prc.flags: Message(n.info, warnAnalysisLoophole, renderTree(n)) if prc.typ.sons[0] != nil: if prc.ast.len > resultPos: @@ -221,14 +221,15 @@ template aggregateOwner(result, ana: expr) = var a = ana # eval once if result != a: if result == toNil: result = a - else: localError(n.info, errDifferentHeaps) + else: Message(n.info, warnDifferentHeaps) proc analyseArgs(c: PProcCtx, n: PNode, start = 1) = for i in start..n.len-1: discard analyse(c, n[i]) proc analyseOp(c: PProcCtx, n: PNode): TThreadOwner = if n[0].kind != nkSym or n[0].sym.kind != skProc: - Message(n.info, warnAnalysisLoophole, renderTree(n)) + if tfNoSideEffect notin n[0].typ.flags: + Message(n.info, warnAnalysisLoophole, renderTree(n)) result = toNil else: var prc = n[0].sym @@ -316,7 +317,7 @@ proc analyse(c: PProcCtx, n: PNode): TThreadOwner = result = analyse(c, n.sons[0]) of nkRaiseStmt: var a = analyse(c, n.sons[0]) - if a != toMine: LocalError(n.info, errDifferentHeaps) + if a != toMine: Message(n.info, warnDifferentHeaps) result = toVoid of nkVarSection: result = analyseVarSection(c, n) of nkConstSection: result = analyseConstSection(c, n) diff --git a/lib/system.nim b/lib/system.nim index 1c8bf3ae9..7670288fc 100755 --- a/lib/system.nim +++ b/lib/system.nim @@ -1479,8 +1479,6 @@ when not defined(EcmaScript) and not defined(NimrodVM): strDesc.size = sizeof(string) strDesc.kind = tyString strDesc.flags = {ntfAcyclic} - initStackBottom() - initGC() # BUGFIX: need to be called here! include "system/ansi_c" @@ -1692,6 +1690,10 @@ when not defined(EcmaScript) and not defined(NimrodVM): when hasThreadSupport: include "system/threads" + else: + initStackBottom() + initGC() + include "system/excpt" # we cannot compile this with stack tracing on # as it would recurse endlessly! @@ -1755,8 +1757,7 @@ when not defined(EcmaScript) and not defined(NimrodVM): proc getCurrentException*(): ref E_Base {.compilerRtl, inl.} = ## retrieves the current exception; if there is none, nil is returned. - ThreadGlobals() - result = ||currException + result = currException proc getCurrentExceptionMsg*(): string {.inline.} = ## retrieves the error message that was attached to the current diff --git a/lib/system/excpt.nim b/lib/system/excpt.nim index 75cac97ba..ac4ec2f0b 100755 --- a/lib/system/excpt.nim +++ b/lib/system/excpt.nim @@ -10,6 +10,9 @@ # Exception handling code. This is difficult because it has # to work if there is no more memory (but it doesn't yet!). +# XXX assertions are unnecessarily complex; system should use their own +# assertion mechanism instead! + var stackTraceNewLine* = "\n" ## undocumented feature; it is replaced by ``<br>`` ## for CGI applications @@ -32,64 +35,51 @@ proc chckRange(i, a, b: int): int {.inline, compilerproc.} proc chckRangeF(x, a, b: float): float {.inline, compilerproc.} proc chckNil(p: pointer) {.inline, compilerproc.} -when hasThreadSupport: - template ThreadGlobals = - var currentThread = ThisThread() - template `||`(varname: expr): expr = currentThread.g.varname - -else: - template ThreadGlobals = nil # nothing - template `||`(varname: expr): expr = varname - - var - framePtr: PFrame - excHandler: PSafePoint = nil - # list of exception handlers - # a global variable for the root of all try blocks - currException: ref E_Base - - buf: string # cannot be allocated on the stack! - assertBuf: string # we need a different buffer for - # assert, as it raises an exception and - # exception handler needs the buffer too - tempFrames: array [0..127, PFrame] # cannot be allocated on the stack! - gAssertionFailed: ref EAssertionFailed - - new(||gAssertionFailed) - ||buf = newStringOfCap(2000) - ||assertBuf = newStringOfCap(2000) - +var + framePtr {.rtlThreadVar.}: PFrame + excHandler {.rtlThreadVar.}: PSafePoint + # list of exception handlers + # a global variable for the root of all try blocks + currException {.rtlThreadVar.}: ref E_Base + + buf {.rtlThreadVar.}: string # cannot be allocated on the stack! + assertBuf {.rtlThreadVar.}: string + # we need a different buffer for + # assert, as it raises an exception and + # exception handler needs the buffer too + gAssertionFailed {.rtlThreadVar.}: ref EAssertionFailed + +proc initGlobals() = + new(gAssertionFailed) + buf = newStringOfCap(2000) + assertBuf = newStringOfCap(2000) + +when not hasThreadSupport: + initGlobals() proc pushFrame(s: PFrame) {.compilerRtl, inl.} = - ThreadGlobals() - s.prev = ||framePtr - ||framePtr = s + s.prev = framePtr + framePtr = s proc popFrame {.compilerRtl, inl.} = - ThreadGlobals() - ||framePtr = (||framePtr).prev + framePtr = framePtr.prev proc setFrame(s: PFrame) {.compilerRtl, inl.} = - ThreadGlobals() - ||framePtr = s + framePtr = s proc pushSafePoint(s: PSafePoint) {.compilerRtl, inl.} = - ThreadGlobals() - s.prev = ||excHandler - ||excHandler = s + s.prev = excHandler + excHandler = s proc popSafePoint {.compilerRtl, inl.} = - ThreadGlobals() - ||excHandler = (||excHandler).prev + excHandler = excHandler.prev proc pushCurrentException(e: ref E_Base) {.compilerRtl, inl.} = - ThreadGlobals() - e.parent = ||currException - ||currException = e + e.parent = currException + currException = e proc popCurrentException {.compilerRtl, inl.} = - ThreadGlobals() - ||currException = (||currException).parent + currException = currException.parent # some platforms have native support for stack traces: const @@ -143,18 +133,24 @@ when defined(nativeStacktrace) and nativeStackTraceSupported: # Once we're past signalHandler, we're at what the user is # interested in enabled = true + +when not hasThreadSupport: + var + tempFrames: array [0..127, PFrame] # should not be alloc'd on stack proc auxWriteStackTrace(f: PFrame, s: var string) = - const + when hasThreadSupport: + var + tempFrames: array [0..127, PFrame] # but better than a threadvar + const firstCalls = 32 - ThreadGlobals() var it = f i = 0 total = 0 - while it != nil and i <= high(||tempFrames)-(firstCalls-1): + while it != nil and i <= high(tempFrames)-(firstCalls-1): # the (-1) is for a nil entry that marks where the '...' should occur - (||tempFrames)[i] = it + tempFrames[i] = it inc(i) inc(total) it = it.prev @@ -165,38 +161,37 @@ proc auxWriteStackTrace(f: PFrame, s: var string) = for j in 1..total-i-(firstCalls-1): if b != nil: b = b.prev if total != i: - (||tempFrames)[i] = nil + tempFrames[i] = nil inc(i) - while b != nil and i <= high(||tempFrames): - (||tempFrames)[i] = b + while b != nil and i <= high(tempFrames): + tempFrames[i] = b inc(i) b = b.prev for j in countdown(i-1, 0): - if (||tempFrames)[j] == nil: + if tempFrames[j] == nil: add(s, "(") add(s, $(total-i-1)) add(s, " calls omitted) ...") else: var oldLen = s.len - add(s, (||tempFrames)[j].filename) - if (||tempFrames)[j].line > 0: + add(s, tempFrames[j].filename) + if tempFrames[j].line > 0: add(s, '(') - add(s, $(||tempFrames)[j].line) + add(s, $tempFrames[j].line) add(s, ')') for k in 1..max(1, 25-(s.len-oldLen)): add(s, ' ') - add(s, (||tempFrames)[j].procname) + add(s, tempFrames[j].procname) add(s, stackTraceNewLine) proc rawWriteStackTrace(s: var string) = when nimrodStackTrace: - ThreadGlobals() - if ||framePtr == nil: + if framePtr == nil: add(s, "No stack traceback available") add(s, stackTraceNewLine) else: add(s, "Traceback (most recent call last)") add(s, stackTraceNewLine) - auxWriteStackTrace(||framePtr, s) + auxWriteStackTrace(framePtr, s) elif defined(nativeStackTrace) and nativeStackTraceSupported: add(s, "Traceback from system (most recent call last)") add(s, stackTraceNewLine) @@ -216,53 +211,50 @@ proc raiseException(e: ref E_Base, ename: CString) {.compilerRtl.} = if raiseHook != nil: if not raiseHook(e): return GC_disable() # a bad thing is an error in the GC while raising an exception - ThreadGlobals() - if ||excHandler != nil: + if excHandler != nil: pushCurrentException(e) - c_longjmp((||excHandler).context, 1) + c_longjmp(excHandler.context, 1) else: - if not isNil(||buf): - setLen(||buf, 0) - rawWriteStackTrace(||buf) + if not isNil(buf): + setLen(buf, 0) + rawWriteStackTrace(buf) if e.msg != nil and e.msg[0] != '\0': - add(||buf, "Error: unhandled exception: ") - add(||buf, $e.msg) + add(buf, "Error: unhandled exception: ") + add(buf, $e.msg) else: - add(||buf, "Error: unhandled exception") - add(||buf, " [") - add(||buf, $ename) - add(||buf, "]\n") - writeToStdErr(||buf) + add(buf, "Error: unhandled exception") + add(buf, " [") + add(buf, $ename) + add(buf, "]\n") + writeToStdErr(buf) else: writeToStdErr(ename) quitOrDebug() GC_enable() proc reraiseException() {.compilerRtl.} = - ThreadGlobals() - if ||currException == nil: + if currException == nil: raise newException(ENoExceptionToReraise, "no exception to reraise") else: - raiseException(||currException, (||currException).name) + raiseException(currException, currException.name) proc internalAssert(file: cstring, line: int, cond: bool) {.compilerproc.} = if not cond: - ThreadGlobals() #c_fprintf(c_stdout, "Assertion failure: file %s line %ld\n", file, line) #quit(1) GC_disable() # BUGFIX: `$` allocates a new string object! - if not isNil(||assertBuf): + if not isNil(assertBuf): # BUGFIX: when debugging the GC, assertBuf may be nil - setLen(||assertBuf, 0) - add(||assertBuf, "[Assertion failure] file: ") - add(||assertBuf, file) - add(||assertBuf, " line: ") - add(||assertBuf, $line) - add(||assertBuf, "\n") - (||gAssertionFailed).msg = ||assertBuf + setLen(assertBuf, 0) + add(assertBuf, "[Assertion failure] file: ") + add(assertBuf, file) + add(assertBuf, " line: ") + add(assertBuf, $line) + add(assertBuf, "\n") + gAssertionFailed.msg = assertBuf GC_enable() - if ||gAssertionFailed != nil: - raise ||gAssertionFailed + if gAssertionFailed != nil: + raise gAssertionFailed else: c_fprintf(c_stdout, "Assertion failure: file %s line %ld\n", file, line) quit(1) @@ -277,24 +269,23 @@ var proc signalHandler(sig: cint) {.exportc: "signalHandler", noconv.} = # print stack trace and quit - ThreadGlobals() var s = sig GC_disable() - setLen(||buf, 0) - rawWriteStackTrace(||buf) + setLen(buf, 0) + rawWriteStackTrace(buf) - if s == SIGINT: add(||buf, "SIGINT: Interrupted by Ctrl-C.\n") + if s == SIGINT: add(buf, "SIGINT: Interrupted by Ctrl-C.\n") elif s == SIGSEGV: - add(||buf, "SIGSEGV: Illegal storage access. (Attempt to read from nil?)\n") + add(buf, "SIGSEGV: Illegal storage access. (Attempt to read from nil?)\n") elif s == SIGABRT: if dbgAborting: return # the debugger wants to abort - add(||buf, "SIGABRT: Abnormal termination.\n") - elif s == SIGFPE: add(||buf, "SIGFPE: Arithmetic error.\n") - elif s == SIGILL: add(||buf, "SIGILL: Illegal operation.\n") + add(buf, "SIGABRT: Abnormal termination.\n") + elif s == SIGFPE: add(buf, "SIGFPE: Arithmetic error.\n") + elif s == SIGILL: add(buf, "SIGILL: Illegal operation.\n") elif s == SIGBUS: - add(||buf, "SIGBUS: Illegal storage access. (Attempt to read from nil?)\n") - else: add(||buf, "unknown signal\n") - writeToStdErr(||buf) + add(buf, "SIGBUS: Illegal storage access. (Attempt to read from nil?)\n") + else: add(buf, "unknown signal\n") + writeToStdErr(buf) dbgAborting = True # play safe here... GC_enable() quit(1) # always quit when SIGABRT diff --git a/lib/system/gc.nim b/lib/system/gc.nim index d2a6b4b94..033a7bdbe 100755 --- a/lib/system/gc.nim +++ b/lib/system/gc.nim @@ -63,7 +63,7 @@ type var stackBottom {.rtlThreadVar.}: pointer gch {.rtlThreadVar.}: TGcHeap - cycleThreshold {.rtlThreadVar.}: int = InitialCycleThreshold + cycleThreshold {.rtlThreadVar.}: int proc acquire(gch: var TGcHeap) {.inline.} = when hasThreadSupport and hasSharedHeap: @@ -267,6 +267,7 @@ proc initGC() = when not defined(useNimRtl): when traceGC: for i in low(TCellState)..high(TCellState): Init(states[i]) + cycleThreshold = InitialCycleThreshold gch.stat.stackScans = 0 gch.stat.cycleCollections = 0 gch.stat.maxThreshold = 0 diff --git a/lib/system/threads.nim b/lib/system/threads.nim index 7b035e819..db16502ff 100755 --- a/lib/system/threads.nim +++ b/lib/system/threads.nim @@ -52,22 +52,22 @@ when defined(Windows): LockSemaphore: int Reserved: int32 - proc InitSysLock(L: var TSysLock) {.stdcall, + proc InitSysLock(L: var TSysLock) {.stdcall, noSideEffect, dynlib: "kernel32", importc: "InitializeCriticalSection".} ## Initializes the lock `L`. - proc TryAcquireSysAux(L: var TSysLock): int32 {.stdcall, + proc TryAcquireSysAux(L: var TSysLock): int32 {.stdcall, noSideEffect, dynlib: "kernel32", importc: "TryEnterCriticalSection".} ## Tries to acquire the lock `L`. proc TryAcquireSys(L: var TSysLock): bool {.inline.} = result = TryAcquireSysAux(L) != 0'i32 - proc AcquireSys(L: var TSysLock) {.stdcall, + proc AcquireSys(L: var TSysLock) {.stdcall, noSideEffect, dynlib: "kernel32", importc: "EnterCriticalSection".} ## Acquires the lock `L`. - proc ReleaseSys(L: var TSysLock) {.stdcall, + proc ReleaseSys(L: var TSysLock) {.stdcall, noSideEffect, dynlib: "kernel32", importc: "LeaveCriticalSection".} ## Releases the lock `L`. @@ -120,17 +120,17 @@ else: header: "<sys/types.h>".} = object proc InitSysLock(L: var TSysLock, attr: pointer = nil) {. - importc: "pthread_mutex_init", header: "<pthread.h>".} + importc: "pthread_mutex_init", header: "<pthread.h>", noSideEffect.} - proc AcquireSys(L: var TSysLock) {. + proc AcquireSys(L: var TSysLock) {.noSideEffect, importc: "pthread_mutex_lock", header: "<pthread.h>".} - proc TryAcquireSysAux(L: var TSysLock): cint {. + proc TryAcquireSysAux(L: var TSysLock): cint {.noSideEffect, importc: "pthread_mutex_trylock", header: "<pthread.h>".} proc TryAcquireSys(L: var TSysLock): bool {.inline.} = result = TryAcquireSysAux(L) == 0'i32 - proc ReleaseSys(L: var TSysLock) {. + proc ReleaseSys(L: var TSysLock) {.noSideEffect, importc: "pthread_mutex_unlock", header: "<pthread.h>".} type @@ -184,32 +184,25 @@ else: proc pthread_setspecific(a1: TThreadVarSlot, a2: pointer): int32 {. importc: "pthread_setspecific", header: "<pthread.h>".} - proc ThreadVarAlloc(): TThreadVarSlot {.compilerproc, inline.} = + proc ThreadVarAlloc(): TThreadVarSlot {.inline.} = discard pthread_key_create(addr(result), nil) - proc ThreadVarSetValue(s: TThreadVarSlot, value: pointer) {. - compilerproc, inline.} = + proc ThreadVarSetValue(s: TThreadVarSlot, value: pointer) {.inline.} = discard pthread_setspecific(s, value) - proc ThreadVarGetValue(s: TThreadVarSlot): pointer {.compilerproc, inline.} = + proc ThreadVarGetValue(s: TThreadVarSlot): pointer {.inline.} = result = pthread_getspecific(s) -type - TGlobals {.final, pure.} = object - excHandler: PSafePoint - currException: ref E_Base - framePtr: PFrame - buf: string # cannot be allocated on the stack! - assertBuf: string # we need a different buffer for - # assert, as it raises an exception and - # exception handler needs the buffer too - gAssertionFailed: ref EAssertionFailed - tempFrames: array [0..127, PFrame] # cannot be allocated on the stack! - data: float # compiler should add thread local variables here! - -proc initGlobals(g: var TGlobals) = - new(g.gAssertionFailed) - g.buf = newStringOfCap(2000) - g.assertBuf = newStringOfCap(2000) +const emulatedThreadVars = defined(macosx) + +when emulatedThreadVars: + # the compiler generates this proc for us, so that we can get the size of + # the thread local var block: + proc NimThreadVarsSize(): int {.noconv, importc: "NimThreadVarsSize".} +proc ThreadVarsAlloc(size: int): pointer = + result = c_malloc(size) + zeroMem(result, size) +proc ThreadVarsDealloc(p: pointer) {.importc: "free", nodecl.} +proc initGlobals() type PGcThread = ptr TGcThread @@ -218,7 +211,6 @@ type next, prev: PGcThread stackBottom, stackTop, threadLocalStorage: pointer stackSize: int - g: TGlobals locksLen: int locks: array [0..MaxLocksPerThread-1, pointer] registers: array[0..maxRegisters-1, pointer] # register contents for GC @@ -240,8 +232,14 @@ proc GetThreadLocalVars(): pointer {.compilerRtl, inl.} = # of all threads; it's not to be stopped etc. when not defined(useNimRtl): var mainThread: TGcThread - initGlobals(mainThread.g) + ThreadVarSetValue(globalsSlot, addr(mainThread)) + when emulatedThreadVars: + mainThread.threadLocalStorage = ThreadVarsAlloc(NimThreadVarsSize()) + + initStackBottom() + initGC() + initGlobals() var heapLock: TSysLock InitSysLock(HeapLock) @@ -296,15 +294,21 @@ when not defined(boehmgc) and not hasSharedHeap: template ThreadProcWrapperBody(closure: expr) = ThreadVarSetValue(globalsSlot, closure) var t = cast[ptr TThread[TParam]](closure) + when emulatedThreadVars: + t.threadLocalStorage = ThreadVarsAlloc(NimThreadVarsSize()) when not defined(boehmgc) and not hasSharedHeap: # init the GC for this thread: setStackBottom(addr(t)) initGC() t.stackBottom = addr(t) + initGlobals() registerThread(t) try: t.fn(t.data) finally: + # XXX shut-down is not executed when the thread is forced down! + when emulatedThreadVars: + ThreadVarsDealloc(t.threadLocalStorage) unregisterThread(t) when defined(deallocOsPages): deallocOsPages() diff --git a/todo.txt b/todo.txt index 873cf8bf9..5d928ab88 100755 --- a/todo.txt +++ b/todo.txt @@ -1,14 +1,17 @@ -* codegen for threadvars +High priority (version 0.8.12) +============================== * implement message passing built-ins * add --deadlock_prevention:on|off switch? timeout for locks? -* test the sort implementation again +* iterators should not always be destructive! +* real types for template results +* built-in serialization -High priority (version 0.9.0) -============================= +version 0.9.0 +============= -- iterators should not always be destructive! +- test the sort implementation again - warning for implicit openArray -> varargs convention - implement explicit varargs - tests: run modules that contain "#RUN_ME", compile the other @@ -17,6 +20,7 @@ High priority (version 0.9.0) - fix overloading resolution - wrong co-/contravariance - make ^ available as operator +- implement closures for the C code generator Bugs ---- @@ -33,26 +37,13 @@ Bugs --> system.swap or genericAssign is broken! And indeed, if reference counts are not modified and the GC is triggered in between a swap, bad things may happen! - - proc sort*[A](t: var TCountTable[A]) = - for i in 0 .. high(t.data)-1: - var maxIdx = i - for j in i+1 .. high(t.data): - if t.data[j].val > t.data[maxIdx].val: maxIdx = j - swap(t.data[maxIdx], t.data[i]) - - -To implement ------------- -* distinct types for array/seq indexes -* implement closures for the C code generator -* GC: marker procs for native Nimrod GC and Boehm GC -* built-in serialization +version 0.9.XX +============== -Low priority ------------- +- distinct types for array/seq indexes +- GC: marker procs for native Nimrod GC and Boehm GC - implicit ref/ptr->var conversion; the compiler may store an object implicitly on the heap for write barrier efficiency - resizing of strings/sequences could take into account the memory that @@ -61,9 +52,8 @@ Low priority - find a way to reintroduce the cleanup() pass for C code generation: this is hard because of partial evaluation --> symbol files will fix this as a side effect -- floating point checks for EcmaScript +- EcmaScript needs a new and better code gen: simply adapt the C code gen to it - prefer proc in current module over other procs with same overloading result? -- real types for template results - generalized case statement (requires better transf) - tlastmod returns wrong results on BSD (Linux, MacOS X: works) - nested tuple unpacking @@ -96,7 +86,7 @@ Library Version 2 ---------- +========= - language change: inheritance should only work with reference types, so that the ``type`` field is not needed for objects! --> zero overhead aggregation @@ -123,6 +113,8 @@ Version 2 var x = myProc() # checks myProc() initializes every pointer explicitely - the two other parsers +- rethink the syntax: distinction between expr and stmt is unfortunate; + indentation handling is quite complex too Low priority @@ -135,10 +127,3 @@ Low priority important than constructors) - code generated for type information is wasteful - -Other ideas ------------ -- startsWith `=^` -- endsWith `=$` -- ignore case `=?` --> `=$?` too? - |