diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim
index faf37247e2..d054e5bac6 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 0000000000..e92e955c58
--- /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 449814d013..1ecadbec4e 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 ebc6464832..ed88ff5517 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 70ae3d3d30..3ca52fc302 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 1c8bf3ae9c..7670288fca 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 75cac97ba9..ac4ec2f0b4 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 ``
``
## 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 {.rtlThreadVar.}: PFrame
+ excHandler {.rtlThreadVar.}: PSafePoint
+ # list of exception handlers
+ # a global variable for the root of all try blocks
+ currException {.rtlThreadVar.}: ref E_Base
- 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 {.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
- 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)
+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 d2a6b4b944..033a7bdbe4 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 7b035e819c..db16502ffa 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: "".} = object
proc InitSysLock(L: var TSysLock, attr: pointer = nil) {.
- importc: "pthread_mutex_init", header: "".}
+ importc: "pthread_mutex_init", header: "", noSideEffect.}
- proc AcquireSys(L: var TSysLock) {.
+ proc AcquireSys(L: var TSysLock) {.noSideEffect,
importc: "pthread_mutex_lock", header: "".}
- proc TryAcquireSysAux(L: var TSysLock): cint {.
+ proc TryAcquireSysAux(L: var TSysLock): cint {.noSideEffect,
importc: "pthread_mutex_trylock", header: "".}
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: "".}
type
@@ -184,32 +184,25 @@ else:
proc pthread_setspecific(a1: TThreadVarSlot, a2: pointer): int32 {.
importc: "pthread_setspecific", header: "".}
- 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!
+const emulatedThreadVars = defined(macosx)
-proc initGlobals(g: var TGlobals) =
- new(g.gAssertionFailed)
- g.buf = newStringOfCap(2000)
- g.assertBuf = newStringOfCap(2000)
+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 873cf8bf91..5d928ab886 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
-------------
+version 0.9.XX
+==============
-* 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
-
-
-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?
-