compiler can emulate thread local variables

This commit is contained in:
Araq 2011-06-15 02:09:02 +02:00
commit 4fa80956b8
10 changed files with 255 additions and 250 deletions

View file

@ -1482,7 +1482,6 @@ proc genSetConstr(p: BProc, e: PNode, d: var TLoc) =
# incl(tmp, d); incl(tmp, e); inclRange(tmp, f, g); # incl(tmp, d); incl(tmp, e); inclRange(tmp, f, g);
var var
a, b, idx: TLoc a, b, idx: TLoc
ts: string
if nfAllConst in e.flags: if nfAllConst in e.flags:
putIntoDest(p, d, e.typ, genSetNode(p, e)) putIntoDest(p, d, e.typ, genSetNode(p, e))
else: else:
@ -1504,7 +1503,7 @@ proc genSetConstr(p: BProc, e: PNode, d: var TLoc) =
[rdLoc(d), rdSetElemLoc(a, e.typ)]) [rdLoc(d), rdSetElemLoc(a, e.typ)])
else: else:
# small set # small set
ts = "NI" & $(getSize(e.typ) * 8) var ts = "NI" & $(getSize(e.typ) * 8)
appf(p.s[cpsStmts], "$1 = 0;$n", [rdLoc(d)]) appf(p.s[cpsStmts], "$1 = 0;$n", [rdLoc(d)])
for i in countup(0, sonsLen(e) - 1): for i in countup(0, sonsLen(e) - 1):
if e.sons[i].kind == nkRange: if e.sons[i].kind == nkRange:
@ -1645,9 +1644,16 @@ proc expr(p: BProc, e: PNode, d: var TLoc) =
of skEnumField: of skEnumField:
putIntoDest(p, d, e.typ, toRope(sym.position)) putIntoDest(p, d, e.typ, toRope(sym.position))
of skVar: 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)): if ((sym.loc.r == nil) or (sym.loc.t == nil)):
InternalError(e.info, "expr: var not init " & sym.name.s) InternalError(e.info, "expr: var not init " & sym.name.s)
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) putLocIntoDest(p, d, sym.loc)
of skForVar, skTemp: of skForVar, skTemp:
if ((sym.loc.r == nil) or (sym.loc.t == nil)): if ((sym.loc.r == nil) or (sym.loc.t == nil)):
@ -1727,7 +1733,6 @@ proc genConstExpr(p: BProc, n: PNode): PRope =
# XXX: tySequence! # XXX: tySequence!
result = genConstSimpleList(p, n) result = genConstSimpleList(p, n)
else: else:
# result := genLiteral(p, n)
var d: TLoc var d: TLoc
initLocExpr(p, n, d) initLocExpr(p, n, d)
result = rdLoc(d) result = rdLoc(d)

View file

@ -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)

View file

@ -66,6 +66,7 @@ type
s: TCProcSections # the procs sections; short name for readability s: TCProcSections # the procs sections; short name for readability
prc: PSym # the Nimrod proc that this C proc belongs to prc: PSym # the Nimrod proc that this C proc belongs to
BeforeRetNeeded: bool # true iff 'BeforeRet' label for proc is needed 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 nestedTryStmts: seq[PNode] # in how many nested try statements we are
# (the vars must be volatile then) # (the vars must be volatile then)
labels: Natural # for generating unique labels in the C proc labels: Natural # for generating unique labels in the C proc
@ -85,6 +86,7 @@ type
filename*: string filename*: string
s*: TCFileSections # sections of the C file s*: TCFileSections # sections of the C file
PreventStackTrace: bool # true if stack traces need to be prevented 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, cfilename*: string # filename of the module (including path,
# without extension) # without extension)
typeCache*: TIdTable # cache the generated types typeCache*: TIdTable # cache the generated types
@ -386,23 +388,6 @@ proc localDebugInfo(p: BProc, s: PSym) =
if {optStackTrace, optEndb} * p.options != {optStackTrace, optEndb}: return if {optStackTrace, optEndb} * p.options != {optStackTrace, optEndb}: return
# XXX work around a bug: No type information for open arrays possible: # XXX work around a bug: No type information for open arrays possible:
if skipTypes(s.typ, abstractVar).kind == tyOpenArray: return 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) var a = con("&", s.loc.r)
if (s.kind == skParam) and ccgIntroducedPtr(s): a = s.loc.r if (s.kind == skParam) and ccgIntroducedPtr(s): a = s.loc.r
appf(p.s[cpsInit], appf(p.s[cpsInit],
@ -417,11 +402,6 @@ proc assignLocalVar(p: BProc, s: PSym) =
# for each module that uses them! # for each module that uses them!
if s.loc.k == locNone: if s.loc.k == locNone:
fillLoc(s.loc, locLocalVar, s.typ, mangleName(s), OnStack) 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)) app(p.s[cpsLocals], getTypeDesc(p.module, s.loc.t))
if sfRegister in s.flags: app(p.s[cpsLocals], " register") if sfRegister in s.flags: app(p.s[cpsLocals], " register")
if (sfVolatile in s.flags) or (p.nestedTryStmts.len > 0): if (sfVolatile in s.flags) or (p.nestedTryStmts.len > 0):
@ -429,33 +409,22 @@ proc assignLocalVar(p: BProc, s: PSym) =
appf(p.s[cpsLocals], " $1;$n", [s.loc.r]) appf(p.s[cpsLocals], " $1;$n", [s.loc.r])
localDebugInfo(p, s) localDebugInfo(p, s)
proc declareThreadVar(m: BModule, s: PSym) = include ccgthreadvars
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))
proc assignGlobalVar(p: BProc, s: PSym) = proc assignGlobalVar(p: BProc, s: PSym) =
if s.loc.k == locNone: if s.loc.k == locNone:
fillLoc(s.loc, locGlobalVar, s.typ, mangleName(s), OnHeap) fillLoc(s.loc, locGlobalVar, s.typ, mangleName(s), OnHeap)
useHeader(p.module, s) useHeader(p.module, s)
if lfNoDecl in s.loc.flags: return 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 ") if sfImportc in s.flags: app(p.module.s[cfsVars], "extern ")
if sfThreadVar in s.flags: declareThreadVar(p.module, s) app(p.module.s[cfsVars], getTypeDesc(p.module, s.loc.t))
else: app(p.module.s[cfsVars], getTypeDesc(p.module, s.loc.t))
if sfRegister in s.flags: app(p.module.s[cfsVars], " register") if sfRegister in s.flags: app(p.module.s[cfsVars], " register")
if sfVolatile in s.flags: app(p.module.s[cfsVars], " volatile") if sfVolatile in s.flags: app(p.module.s[cfsVars], " volatile")
appf(p.module.s[cfsVars], " $1;$n", [s.loc.r]) appf(p.module.s[cfsVars], " $1;$n", [s.loc.r])
if {optStackTrace, optEndb} * p.module.module.options == if p.module.module.options * {optStackTrace, optEndb} ==
{optStackTrace, optEndb}: {optStackTrace, optEndb}:
appcg(p.module, p.module.s[cfsDebugInit], appcg(p.module, p.module.s[cfsDebugInit],
"#dbgRegisterGlobal($1, &$2, $3);$n", "#dbgRegisterGlobal($1, &$2, $3);$n",
@ -463,10 +432,6 @@ proc assignGlobalVar(p: BProc, s: PSym) =
normalize(s.owner.name.s & '.' & s.name.s)), normalize(s.owner.name.s & '.' & s.name.s)),
s.loc.r, genTypeInfo(p.module, s.typ)]) 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) = proc assignParam(p: BProc, s: PSym) =
assert(s.loc.r != nil) assert(s.loc.r != nil)
if sfAddrTaken in s.flags and gCmd == cmdCompileToLLVM: allocParam(p, s) if sfAddrTaken in s.flags and gCmd == cmdCompileToLLVM: allocParam(p, s)
@ -691,8 +656,7 @@ proc genProcPrototype(m: BModule, sym: PSym) =
if lfDynamicLib in sym.loc.Flags: if lfDynamicLib in sym.loc.Flags:
if sym.owner.id != m.module.id and if sym.owner.id != m.module.id and
not ContainsOrIncl(m.declaredThings, sym.id): not ContainsOrIncl(m.declaredThings, sym.id):
appff(m.s[cfsVars], "extern $1 $2;$n", appf(m.s[cfsVars], "extern $1 $2;$n",
"@$2 = linkonce global $1 zeroinitializer$n",
[getTypeDesc(m, sym.loc.t), mangleDynLibProc(sym)]) [getTypeDesc(m, sym.loc.t), mangleDynLibProc(sym)])
if gCmd == cmdCompileToLLVM: incl(sym.loc.flags, lfIndirect) if gCmd == cmdCompileToLLVM: incl(sym.loc.flags, lfIndirect)
elif not ContainsOrIncl(m.declaredProtos, sym.id): elif not ContainsOrIncl(m.declaredProtos, sym.id):
@ -725,20 +689,16 @@ proc genVarPrototype(m: BModule, sym: PSym) =
assert(sfGlobal in sym.flags) assert(sfGlobal in sym.flags)
useHeader(m, sym) useHeader(m, sym)
fillLoc(sym.loc, locGlobalVar, sym.typ, mangleName(sym), OnHeap) fillLoc(sym.loc, locGlobalVar, sym.typ, mangleName(sym), OnHeap)
if (lfNoDecl in sym.loc.Flags) or if (lfNoDecl in sym.loc.Flags) or ContainsOrIncl(m.declaredThings, sym.id):
ContainsOrIncl(m.declaredThings, sym.id):
return return
if sym.owner.id != m.module.id: if sym.owner.id != m.module.id:
# else we already have the symbol generated! # else we already have the symbol generated!
assert(sym.loc.r != nil) assert(sym.loc.r != nil)
if gCmd == cmdCompileToLLVM: if sfThreadVar in sym.flags:
incl(sym.loc.flags, lfIndirect) declareThreadVar(m, sym, true)
appf(m.s[cfsVars], "$1 = linkonce global $2 zeroinitializer$n",
[sym.loc.r, getTypeDesc(m, sym.loc.t)])
else: else:
app(m.s[cfsVars], "extern ") app(m.s[cfsVars], "extern ")
if sfThreadVar in sym.flags: declareThreadVar(m, sym) app(m.s[cfsVars], getTypeDesc(m, sym.loc.t))
else: app(m.s[cfsVars], getTypeDesc(m, sym.loc.t))
if sfRegister in sym.flags: app(m.s[cfsVars], " register") if sfRegister in sym.flags: app(m.s[cfsVars], " register")
if sfVolatile in sym.flags: app(m.s[cfsVars], " volatile") if sfVolatile in sym.flags: app(m.s[cfsVars], " volatile")
appf(m.s[cfsVars], " $1;$n", [sym.loc.r]) appf(m.s[cfsVars], " $1;$n", [sym.loc.r])
@ -898,6 +858,7 @@ proc genInitCode(m: BModule) =
proc genModule(m: BModule, cfilenoext: string): PRope = proc genModule(m: BModule, cfilenoext: string): PRope =
result = getFileHeader(cfilenoext) result = getFileHeader(cfilenoext)
generateHeaders(m) generateHeaders(m)
generateThreadLocalStorage(m)
for i in countup(low(TCFileSection), cfsProcs): app(result, m.s[i]) for i in countup(low(TCFileSection), cfsProcs): app(result, m.s[i])
proc rawNewModule(module: PSym, filename: string): BModule = proc rawNewModule(module: PSym, filename: string): BModule =
@ -990,6 +951,7 @@ proc writeModule(m: BModule) =
if sfMainModule in m.module.flags: if sfMainModule in m.module.flags:
# generate main file: # generate main file:
app(m.s[cfsProcHeaders], mainModProcs) app(m.s[cfsProcHeaders], mainModProcs)
GenerateThreadVarsSize(m)
var code = genModule(m, cfilenoext) var code = genModule(m, cfilenoext)
when hasTinyCBackend: when hasTinyCBackend:

View file

@ -86,7 +86,7 @@ type
errCannotInterpretNodeX, errFieldXNotFound, errInvalidConversionFromTypeX, errCannotInterpretNodeX, errFieldXNotFound, errInvalidConversionFromTypeX,
errAssertionFailed, errCannotGenerateCodeForX, errXRequiresOneArgument, errAssertionFailed, errCannotGenerateCodeForX, errXRequiresOneArgument,
errUnhandledExceptionX, errCyclicTree, errXisNoMacroOrTemplate, errUnhandledExceptionX, errCyclicTree, errXisNoMacroOrTemplate,
errXhasSideEffects, errIteratorExpected, errDifferentHeaps, errXhasSideEffects, errIteratorExpected,
errUser, errUser,
warnCannotOpenFile, warnCannotOpenFile,
warnOctalEscape, warnXIsNeverRead, warnXmightNotBeenInit, warnOctalEscape, warnXIsNeverRead, warnXmightNotBeenInit,
@ -94,6 +94,7 @@ type
warnSmallLshouldNotBeUsed, warnUnknownMagic, warnRedefinitionOfLabel, warnSmallLshouldNotBeUsed, warnUnknownMagic, warnRedefinitionOfLabel,
warnUnknownSubstitutionX, warnLanguageXNotSupported, warnCommentXIgnored, warnUnknownSubstitutionX, warnLanguageXNotSupported, warnCommentXIgnored,
warnXisPassedToProcVar, warnDerefDeprecated, warnAnalysisLoophole, warnXisPassedToProcVar, warnDerefDeprecated, warnAnalysisLoophole,
warnDifferentHeaps,
warnUser, warnUser,
hintSuccess, hintSuccessX, hintSuccess, hintSuccessX,
hintLineTooLong, hintXDeclaredButNotUsed, hintConvToBaseNotNeeded, hintLineTooLong, hintXDeclaredButNotUsed, hintConvToBaseNotNeeded,
@ -310,7 +311,6 @@ const
errXisNoMacroOrTemplate: "\'$1\' is no macro or template", errXisNoMacroOrTemplate: "\'$1\' is no macro or template",
errXhasSideEffects: "\'$1\' can have side effects", errXhasSideEffects: "\'$1\' can have side effects",
errIteratorExpected: "iterator within for loop context expected", errIteratorExpected: "iterator within for loop context expected",
errDifferentHeaps: "possible inconsistency of thread local heaps",
errUser: "$1", errUser: "$1",
warnCannotOpenFile: "cannot open \'$1\' [CannotOpenFile]", warnCannotOpenFile: "cannot open \'$1\' [CannotOpenFile]",
warnOctalEscape: "octal escape sequences do not exist; leading zero is ignored [OctalEscape]", warnOctalEscape: "octal escape sequences do not exist; leading zero is ignored [OctalEscape]",
@ -327,7 +327,8 @@ const
warnCommentXIgnored: "comment \'$1\' ignored [CommentXIgnored]", warnCommentXIgnored: "comment \'$1\' ignored [CommentXIgnored]",
warnXisPassedToProcVar: "\'$1\' is passed to a procvar; deprecated [XisPassedToProcVar]", warnXisPassedToProcVar: "\'$1\' is passed to a procvar; deprecated [XisPassedToProcVar]",
warnDerefDeprecated: "p^ is deprecated; use p[] instead [DerefDeprecated]", 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]", warnUser: "$1 [User]",
hintSuccess: "operation successful [Success]", hintSuccess: "operation successful [Success]",
hintSuccessX: "operation successful ($1 lines compiled; $2 sec total) [SuccessX]", hintSuccessX: "operation successful ($1 lines compiled; $2 sec total) [SuccessX]",
@ -345,12 +346,12 @@ const
hintUser: "$1 [User]"] hintUser: "$1 [User]"]
const const
WarningsToStr*: array[0..16, string] = ["CannotOpenFile", "OctalEscape", WarningsToStr*: array[0..17, string] = ["CannotOpenFile", "OctalEscape",
"XIsNeverRead", "XmightNotBeenInit", "CannotWriteMO2", "CannotReadMO2", "XIsNeverRead", "XmightNotBeenInit", "CannotWriteMO2", "CannotReadMO2",
"Deprecated", "SmallLshouldNotBeUsed", "UnknownMagic", "Deprecated", "SmallLshouldNotBeUsed", "UnknownMagic",
"RedefinitionOfLabel", "UnknownSubstitutionX", "LanguageXNotSupported", "RedefinitionOfLabel", "UnknownSubstitutionX", "LanguageXNotSupported",
"CommentXIgnored", "XisPassedToProcVar", "DerefDeprecated", "CommentXIgnored", "XisPassedToProcVar", "DerefDeprecated",
"AnalysisLoophole", "User"] "AnalysisLoophole", "DifferentHeaps", "User"]
HintsToStr*: array[0..13, string] = ["Success", "SuccessX", "LineTooLong", HintsToStr*: array[0..13, string] = ["Success", "SuccessX", "LineTooLong",
"XDeclaredButNotUsed", "ConvToBaseNotNeeded", "ConvFromXtoItselfNotNeeded", "XDeclaredButNotUsed", "ConvToBaseNotNeeded", "ConvFromXtoItselfNotNeeded",

View file

@ -138,7 +138,7 @@ proc writeAccess(c: PProcCtx, n: PNode, owner: TThreadOwner) =
of toVoid, toUndefined: InternalError(n.info, "writeAccess") of toVoid, toUndefined: InternalError(n.info, "writeAccess")
of toTheirs, toMine: of toTheirs, toMine:
if lastOwner != owner and owner != toNil: if lastOwner != owner and owner != toNil:
LocalError(n.info, errDifferentHeaps) Message(n.info, warnDifferentHeaps)
else: else:
# we could not backtrack to a concrete symbol, but that's fine: # we could not backtrack to a concrete symbol, but that's fine:
var lastOwner = analyseSym(c, n) 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 toVoid, toUndefined: InternalError(n.info, "writeAccess")
of toTheirs, toMine: of toTheirs, toMine:
if lastOwner != owner and owner != toNil: if lastOwner != owner and owner != toNil:
LocalError(n.info, errDifferentHeaps) Message(n.info, warnDifferentHeaps)
proc analyseAssign(c: PProcCtx, le, ri: PNode) = proc analyseAssign(c: PProcCtx, le, ri: PNode) =
var y = analyse(c, ri) # read access; ok 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] newCtx.mapping[formal.id] = call.args[i-1]
pushInfoContext(n.info) pushInfoContext(n.info)
result = analyse(newCtx, prc.ast.sons[codePos]) 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)) Message(n.info, warnAnalysisLoophole, renderTree(n))
if prc.typ.sons[0] != nil: if prc.typ.sons[0] != nil:
if prc.ast.len > resultPos: if prc.ast.len > resultPos:
@ -221,13 +221,14 @@ template aggregateOwner(result, ana: expr) =
var a = ana # eval once var a = ana # eval once
if result != a: if result != a:
if result == toNil: 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) = proc analyseArgs(c: PProcCtx, n: PNode, start = 1) =
for i in start..n.len-1: discard analyse(c, n[i]) for i in start..n.len-1: discard analyse(c, n[i])
proc analyseOp(c: PProcCtx, n: PNode): TThreadOwner = proc analyseOp(c: PProcCtx, n: PNode): TThreadOwner =
if n[0].kind != nkSym or n[0].sym.kind != skProc: if n[0].kind != nkSym or n[0].sym.kind != skProc:
if tfNoSideEffect notin n[0].typ.flags:
Message(n.info, warnAnalysisLoophole, renderTree(n)) Message(n.info, warnAnalysisLoophole, renderTree(n))
result = toNil result = toNil
else: else:
@ -316,7 +317,7 @@ proc analyse(c: PProcCtx, n: PNode): TThreadOwner =
result = analyse(c, n.sons[0]) result = analyse(c, n.sons[0])
of nkRaiseStmt: of nkRaiseStmt:
var a = analyse(c, n.sons[0]) var a = analyse(c, n.sons[0])
if a != toMine: LocalError(n.info, errDifferentHeaps) if a != toMine: Message(n.info, warnDifferentHeaps)
result = toVoid result = toVoid
of nkVarSection: result = analyseVarSection(c, n) of nkVarSection: result = analyseVarSection(c, n)
of nkConstSection: result = analyseConstSection(c, n) of nkConstSection: result = analyseConstSection(c, n)

View file

@ -1479,8 +1479,6 @@ when not defined(EcmaScript) and not defined(NimrodVM):
strDesc.size = sizeof(string) strDesc.size = sizeof(string)
strDesc.kind = tyString strDesc.kind = tyString
strDesc.flags = {ntfAcyclic} strDesc.flags = {ntfAcyclic}
initStackBottom()
initGC() # BUGFIX: need to be called here!
include "system/ansi_c" include "system/ansi_c"
@ -1692,6 +1690,10 @@ when not defined(EcmaScript) and not defined(NimrodVM):
when hasThreadSupport: when hasThreadSupport:
include "system/threads" include "system/threads"
else:
initStackBottom()
initGC()
include "system/excpt" include "system/excpt"
# we cannot compile this with stack tracing on # we cannot compile this with stack tracing on
# as it would recurse endlessly! # as it would recurse endlessly!
@ -1755,8 +1757,7 @@ when not defined(EcmaScript) and not defined(NimrodVM):
proc getCurrentException*(): ref E_Base {.compilerRtl, inl.} = proc getCurrentException*(): ref E_Base {.compilerRtl, inl.} =
## retrieves the current exception; if there is none, nil is returned. ## retrieves the current exception; if there is none, nil is returned.
ThreadGlobals() result = currException
result = ||currException
proc getCurrentExceptionMsg*(): string {.inline.} = proc getCurrentExceptionMsg*(): string {.inline.} =
## retrieves the error message that was attached to the current ## retrieves the error message that was attached to the current

View file

@ -10,6 +10,9 @@
# Exception handling code. This is difficult because it has # Exception handling code. This is difficult because it has
# to work if there is no more memory (but it doesn't yet!). # 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 var
stackTraceNewLine* = "\n" ## undocumented feature; it is replaced by ``<br>`` stackTraceNewLine* = "\n" ## undocumented feature; it is replaced by ``<br>``
## for CGI applications ## 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 chckRangeF(x, a, b: float): float {.inline, compilerproc.}
proc chckNil(p: pointer) {.inline, compilerproc.} proc chckNil(p: pointer) {.inline, compilerproc.}
when hasThreadSupport: var
template ThreadGlobals = framePtr {.rtlThreadVar.}: PFrame
var currentThread = ThisThread() excHandler {.rtlThreadVar.}: PSafePoint
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 # list of exception handlers
# a global variable for the root of all try blocks # a global variable for the root of all try blocks
currException: ref E_Base currException {.rtlThreadVar.}: ref E_Base
buf: string # cannot be allocated on the stack! buf {.rtlThreadVar.}: string # cannot be allocated on the stack!
assertBuf: string # we need a different buffer for assertBuf {.rtlThreadVar.}: string
# we need a different buffer for
# assert, as it raises an exception and # assert, as it raises an exception and
# exception handler needs the buffer too # exception handler needs the buffer too
tempFrames: array [0..127, PFrame] # cannot be allocated on the stack! gAssertionFailed {.rtlThreadVar.}: ref EAssertionFailed
gAssertionFailed: ref EAssertionFailed
new(||gAssertionFailed) proc initGlobals() =
||buf = newStringOfCap(2000) new(gAssertionFailed)
||assertBuf = newStringOfCap(2000) buf = newStringOfCap(2000)
assertBuf = newStringOfCap(2000)
when not hasThreadSupport:
initGlobals()
proc pushFrame(s: PFrame) {.compilerRtl, inl.} = proc pushFrame(s: PFrame) {.compilerRtl, inl.} =
ThreadGlobals() s.prev = framePtr
s.prev = ||framePtr framePtr = s
||framePtr = s
proc popFrame {.compilerRtl, inl.} = proc popFrame {.compilerRtl, inl.} =
ThreadGlobals() framePtr = framePtr.prev
||framePtr = (||framePtr).prev
proc setFrame(s: PFrame) {.compilerRtl, inl.} = proc setFrame(s: PFrame) {.compilerRtl, inl.} =
ThreadGlobals() framePtr = s
||framePtr = s
proc pushSafePoint(s: PSafePoint) {.compilerRtl, inl.} = proc pushSafePoint(s: PSafePoint) {.compilerRtl, inl.} =
ThreadGlobals() s.prev = excHandler
s.prev = ||excHandler excHandler = s
||excHandler = s
proc popSafePoint {.compilerRtl, inl.} = proc popSafePoint {.compilerRtl, inl.} =
ThreadGlobals() excHandler = excHandler.prev
||excHandler = (||excHandler).prev
proc pushCurrentException(e: ref E_Base) {.compilerRtl, inl.} = proc pushCurrentException(e: ref E_Base) {.compilerRtl, inl.} =
ThreadGlobals() e.parent = currException
e.parent = ||currException currException = e
||currException = e
proc popCurrentException {.compilerRtl, inl.} = proc popCurrentException {.compilerRtl, inl.} =
ThreadGlobals() currException = currException.parent
||currException = (||currException).parent
# some platforms have native support for stack traces: # some platforms have native support for stack traces:
const const
@ -144,17 +134,23 @@ when defined(nativeStacktrace) and nativeStackTraceSupported:
# interested in # interested in
enabled = true 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) = proc auxWriteStackTrace(f: PFrame, s: var string) =
when hasThreadSupport:
var
tempFrames: array [0..127, PFrame] # but better than a threadvar
const const
firstCalls = 32 firstCalls = 32
ThreadGlobals()
var var
it = f it = f
i = 0 i = 0
total = 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 # the (-1) is for a nil entry that marks where the '...' should occur
(||tempFrames)[i] = it tempFrames[i] = it
inc(i) inc(i)
inc(total) inc(total)
it = it.prev it = it.prev
@ -165,38 +161,37 @@ proc auxWriteStackTrace(f: PFrame, s: var string) =
for j in 1..total-i-(firstCalls-1): for j in 1..total-i-(firstCalls-1):
if b != nil: b = b.prev if b != nil: b = b.prev
if total != i: if total != i:
(||tempFrames)[i] = nil tempFrames[i] = nil
inc(i) inc(i)
while b != nil and i <= high(||tempFrames): while b != nil and i <= high(tempFrames):
(||tempFrames)[i] = b tempFrames[i] = b
inc(i) inc(i)
b = b.prev b = b.prev
for j in countdown(i-1, 0): for j in countdown(i-1, 0):
if (||tempFrames)[j] == nil: if tempFrames[j] == nil:
add(s, "(") add(s, "(")
add(s, $(total-i-1)) add(s, $(total-i-1))
add(s, " calls omitted) ...") add(s, " calls omitted) ...")
else: else:
var oldLen = s.len var oldLen = s.len
add(s, (||tempFrames)[j].filename) add(s, tempFrames[j].filename)
if (||tempFrames)[j].line > 0: if tempFrames[j].line > 0:
add(s, '(') add(s, '(')
add(s, $(||tempFrames)[j].line) add(s, $tempFrames[j].line)
add(s, ')') add(s, ')')
for k in 1..max(1, 25-(s.len-oldLen)): 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) add(s, stackTraceNewLine)
proc rawWriteStackTrace(s: var string) = proc rawWriteStackTrace(s: var string) =
when nimrodStackTrace: when nimrodStackTrace:
ThreadGlobals() if framePtr == nil:
if ||framePtr == nil:
add(s, "No stack traceback available") add(s, "No stack traceback available")
add(s, stackTraceNewLine) add(s, stackTraceNewLine)
else: else:
add(s, "Traceback (most recent call last)") add(s, "Traceback (most recent call last)")
add(s, stackTraceNewLine) add(s, stackTraceNewLine)
auxWriteStackTrace(||framePtr, s) auxWriteStackTrace(framePtr, s)
elif defined(nativeStackTrace) and nativeStackTraceSupported: elif defined(nativeStackTrace) and nativeStackTraceSupported:
add(s, "Traceback from system (most recent call last)") add(s, "Traceback from system (most recent call last)")
add(s, stackTraceNewLine) add(s, stackTraceNewLine)
@ -216,53 +211,50 @@ proc raiseException(e: ref E_Base, ename: CString) {.compilerRtl.} =
if raiseHook != nil: if raiseHook != nil:
if not raiseHook(e): return if not raiseHook(e): return
GC_disable() # a bad thing is an error in the GC while raising an exception GC_disable() # a bad thing is an error in the GC while raising an exception
ThreadGlobals() if excHandler != nil:
if ||excHandler != nil:
pushCurrentException(e) pushCurrentException(e)
c_longjmp((||excHandler).context, 1) c_longjmp(excHandler.context, 1)
else: else:
if not isNil(||buf): if not isNil(buf):
setLen(||buf, 0) setLen(buf, 0)
rawWriteStackTrace(||buf) rawWriteStackTrace(buf)
if e.msg != nil and e.msg[0] != '\0': if e.msg != nil and e.msg[0] != '\0':
add(||buf, "Error: unhandled exception: ") add(buf, "Error: unhandled exception: ")
add(||buf, $e.msg) add(buf, $e.msg)
else: else:
add(||buf, "Error: unhandled exception") add(buf, "Error: unhandled exception")
add(||buf, " [") add(buf, " [")
add(||buf, $ename) add(buf, $ename)
add(||buf, "]\n") add(buf, "]\n")
writeToStdErr(||buf) writeToStdErr(buf)
else: else:
writeToStdErr(ename) writeToStdErr(ename)
quitOrDebug() quitOrDebug()
GC_enable() GC_enable()
proc reraiseException() {.compilerRtl.} = proc reraiseException() {.compilerRtl.} =
ThreadGlobals() if currException == nil:
if ||currException == nil:
raise newException(ENoExceptionToReraise, "no exception to reraise") raise newException(ENoExceptionToReraise, "no exception to reraise")
else: else:
raiseException(||currException, (||currException).name) raiseException(currException, currException.name)
proc internalAssert(file: cstring, line: int, cond: bool) {.compilerproc.} = proc internalAssert(file: cstring, line: int, cond: bool) {.compilerproc.} =
if not cond: if not cond:
ThreadGlobals()
#c_fprintf(c_stdout, "Assertion failure: file %s line %ld\n", file, line) #c_fprintf(c_stdout, "Assertion failure: file %s line %ld\n", file, line)
#quit(1) #quit(1)
GC_disable() # BUGFIX: `$` allocates a new string object! 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 # BUGFIX: when debugging the GC, assertBuf may be nil
setLen(||assertBuf, 0) setLen(assertBuf, 0)
add(||assertBuf, "[Assertion failure] file: ") add(assertBuf, "[Assertion failure] file: ")
add(||assertBuf, file) add(assertBuf, file)
add(||assertBuf, " line: ") add(assertBuf, " line: ")
add(||assertBuf, $line) add(assertBuf, $line)
add(||assertBuf, "\n") add(assertBuf, "\n")
(||gAssertionFailed).msg = ||assertBuf gAssertionFailed.msg = assertBuf
GC_enable() GC_enable()
if ||gAssertionFailed != nil: if gAssertionFailed != nil:
raise ||gAssertionFailed raise gAssertionFailed
else: else:
c_fprintf(c_stdout, "Assertion failure: file %s line %ld\n", file, line) c_fprintf(c_stdout, "Assertion failure: file %s line %ld\n", file, line)
quit(1) quit(1)
@ -277,24 +269,23 @@ var
proc signalHandler(sig: cint) {.exportc: "signalHandler", noconv.} = proc signalHandler(sig: cint) {.exportc: "signalHandler", noconv.} =
# print stack trace and quit # print stack trace and quit
ThreadGlobals()
var s = sig var s = sig
GC_disable() GC_disable()
setLen(||buf, 0) setLen(buf, 0)
rawWriteStackTrace(||buf) 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: 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: elif s == SIGABRT:
if dbgAborting: return # the debugger wants to abort if dbgAborting: return # the debugger wants to abort
add(||buf, "SIGABRT: Abnormal termination.\n") add(buf, "SIGABRT: Abnormal termination.\n")
elif s == SIGFPE: add(||buf, "SIGFPE: Arithmetic error.\n") elif s == SIGFPE: add(buf, "SIGFPE: Arithmetic error.\n")
elif s == SIGILL: add(||buf, "SIGILL: Illegal operation.\n") elif s == SIGILL: add(buf, "SIGILL: Illegal operation.\n")
elif s == SIGBUS: elif s == SIGBUS:
add(||buf, "SIGBUS: Illegal storage access. (Attempt to read from nil?)\n") add(buf, "SIGBUS: Illegal storage access. (Attempt to read from nil?)\n")
else: add(||buf, "unknown signal\n") else: add(buf, "unknown signal\n")
writeToStdErr(||buf) writeToStdErr(buf)
dbgAborting = True # play safe here... dbgAborting = True # play safe here...
GC_enable() GC_enable()
quit(1) # always quit when SIGABRT quit(1) # always quit when SIGABRT

View file

@ -63,7 +63,7 @@ type
var var
stackBottom {.rtlThreadVar.}: pointer stackBottom {.rtlThreadVar.}: pointer
gch {.rtlThreadVar.}: TGcHeap gch {.rtlThreadVar.}: TGcHeap
cycleThreshold {.rtlThreadVar.}: int = InitialCycleThreshold cycleThreshold {.rtlThreadVar.}: int
proc acquire(gch: var TGcHeap) {.inline.} = proc acquire(gch: var TGcHeap) {.inline.} =
when hasThreadSupport and hasSharedHeap: when hasThreadSupport and hasSharedHeap:
@ -267,6 +267,7 @@ proc initGC() =
when not defined(useNimRtl): when not defined(useNimRtl):
when traceGC: when traceGC:
for i in low(TCellState)..high(TCellState): Init(states[i]) for i in low(TCellState)..high(TCellState): Init(states[i])
cycleThreshold = InitialCycleThreshold
gch.stat.stackScans = 0 gch.stat.stackScans = 0
gch.stat.cycleCollections = 0 gch.stat.cycleCollections = 0
gch.stat.maxThreshold = 0 gch.stat.maxThreshold = 0

View file

@ -52,22 +52,22 @@ when defined(Windows):
LockSemaphore: int LockSemaphore: int
Reserved: int32 Reserved: int32
proc InitSysLock(L: var TSysLock) {.stdcall, proc InitSysLock(L: var TSysLock) {.stdcall, noSideEffect,
dynlib: "kernel32", importc: "InitializeCriticalSection".} dynlib: "kernel32", importc: "InitializeCriticalSection".}
## Initializes the lock `L`. ## Initializes the lock `L`.
proc TryAcquireSysAux(L: var TSysLock): int32 {.stdcall, proc TryAcquireSysAux(L: var TSysLock): int32 {.stdcall, noSideEffect,
dynlib: "kernel32", importc: "TryEnterCriticalSection".} dynlib: "kernel32", importc: "TryEnterCriticalSection".}
## Tries to acquire the lock `L`. ## Tries to acquire the lock `L`.
proc TryAcquireSys(L: var TSysLock): bool {.inline.} = proc TryAcquireSys(L: var TSysLock): bool {.inline.} =
result = TryAcquireSysAux(L) != 0'i32 result = TryAcquireSysAux(L) != 0'i32
proc AcquireSys(L: var TSysLock) {.stdcall, proc AcquireSys(L: var TSysLock) {.stdcall, noSideEffect,
dynlib: "kernel32", importc: "EnterCriticalSection".} dynlib: "kernel32", importc: "EnterCriticalSection".}
## Acquires the lock `L`. ## Acquires the lock `L`.
proc ReleaseSys(L: var TSysLock) {.stdcall, proc ReleaseSys(L: var TSysLock) {.stdcall, noSideEffect,
dynlib: "kernel32", importc: "LeaveCriticalSection".} dynlib: "kernel32", importc: "LeaveCriticalSection".}
## Releases the lock `L`. ## Releases the lock `L`.
@ -120,17 +120,17 @@ else:
header: "<sys/types.h>".} = object header: "<sys/types.h>".} = object
proc InitSysLock(L: var TSysLock, attr: pointer = nil) {. 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>".} 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>".} importc: "pthread_mutex_trylock", header: "<pthread.h>".}
proc TryAcquireSys(L: var TSysLock): bool {.inline.} = proc TryAcquireSys(L: var TSysLock): bool {.inline.} =
result = TryAcquireSysAux(L) == 0'i32 result = TryAcquireSysAux(L) == 0'i32
proc ReleaseSys(L: var TSysLock) {. proc ReleaseSys(L: var TSysLock) {.noSideEffect,
importc: "pthread_mutex_unlock", header: "<pthread.h>".} importc: "pthread_mutex_unlock", header: "<pthread.h>".}
type type
@ -184,32 +184,25 @@ else:
proc pthread_setspecific(a1: TThreadVarSlot, a2: pointer): int32 {. proc pthread_setspecific(a1: TThreadVarSlot, a2: pointer): int32 {.
importc: "pthread_setspecific", header: "<pthread.h>".} importc: "pthread_setspecific", header: "<pthread.h>".}
proc ThreadVarAlloc(): TThreadVarSlot {.compilerproc, inline.} = proc ThreadVarAlloc(): TThreadVarSlot {.inline.} =
discard pthread_key_create(addr(result), nil) discard pthread_key_create(addr(result), nil)
proc ThreadVarSetValue(s: TThreadVarSlot, value: pointer) {. proc ThreadVarSetValue(s: TThreadVarSlot, value: pointer) {.inline.} =
compilerproc, inline.} =
discard pthread_setspecific(s, value) discard pthread_setspecific(s, value)
proc ThreadVarGetValue(s: TThreadVarSlot): pointer {.compilerproc, inline.} = proc ThreadVarGetValue(s: TThreadVarSlot): pointer {.inline.} =
result = pthread_getspecific(s) result = pthread_getspecific(s)
type const emulatedThreadVars = defined(macosx)
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) = when emulatedThreadVars:
new(g.gAssertionFailed) # the compiler generates this proc for us, so that we can get the size of
g.buf = newStringOfCap(2000) # the thread local var block:
g.assertBuf = newStringOfCap(2000) 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 type
PGcThread = ptr TGcThread PGcThread = ptr TGcThread
@ -218,7 +211,6 @@ type
next, prev: PGcThread next, prev: PGcThread
stackBottom, stackTop, threadLocalStorage: pointer stackBottom, stackTop, threadLocalStorage: pointer
stackSize: int stackSize: int
g: TGlobals
locksLen: int locksLen: int
locks: array [0..MaxLocksPerThread-1, pointer] locks: array [0..MaxLocksPerThread-1, pointer]
registers: array[0..maxRegisters-1, pointer] # register contents for GC 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. # of all threads; it's not to be stopped etc.
when not defined(useNimRtl): when not defined(useNimRtl):
var mainThread: TGcThread var mainThread: TGcThread
initGlobals(mainThread.g)
ThreadVarSetValue(globalsSlot, addr(mainThread)) ThreadVarSetValue(globalsSlot, addr(mainThread))
when emulatedThreadVars:
mainThread.threadLocalStorage = ThreadVarsAlloc(NimThreadVarsSize())
initStackBottom()
initGC()
initGlobals()
var heapLock: TSysLock var heapLock: TSysLock
InitSysLock(HeapLock) InitSysLock(HeapLock)
@ -296,15 +294,21 @@ when not defined(boehmgc) and not hasSharedHeap:
template ThreadProcWrapperBody(closure: expr) = template ThreadProcWrapperBody(closure: expr) =
ThreadVarSetValue(globalsSlot, closure) ThreadVarSetValue(globalsSlot, closure)
var t = cast[ptr TThread[TParam]](closure) var t = cast[ptr TThread[TParam]](closure)
when emulatedThreadVars:
t.threadLocalStorage = ThreadVarsAlloc(NimThreadVarsSize())
when not defined(boehmgc) and not hasSharedHeap: when not defined(boehmgc) and not hasSharedHeap:
# init the GC for this thread: # init the GC for this thread:
setStackBottom(addr(t)) setStackBottom(addr(t))
initGC() initGC()
t.stackBottom = addr(t) t.stackBottom = addr(t)
initGlobals()
registerThread(t) registerThread(t)
try: try:
t.fn(t.data) t.fn(t.data)
finally: finally:
# XXX shut-down is not executed when the thread is forced down!
when emulatedThreadVars:
ThreadVarsDealloc(t.threadLocalStorage)
unregisterThread(t) unregisterThread(t)
when defined(deallocOsPages): deallocOsPages() when defined(deallocOsPages): deallocOsPages()

View file

@ -1,14 +1,17 @@
* codegen for threadvars High priority (version 0.8.12)
==============================
* implement message passing built-ins * implement message passing built-ins
* add --deadlock_prevention:on|off switch? timeout for locks? * 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 - warning for implicit openArray -> varargs convention
- implement explicit varargs - implement explicit varargs
- tests: run modules that contain "#RUN_ME", compile the other - tests: run modules that contain "#RUN_ME", compile the other
@ -17,6 +20,7 @@ High priority (version 0.9.0)
- fix overloading resolution - fix overloading resolution
- wrong co-/contravariance - wrong co-/contravariance
- make ^ available as operator - make ^ available as operator
- implement closures for the C code generator
Bugs Bugs
---- ----
@ -34,25 +38,12 @@ Bugs
are not modified and the GC is triggered in between a swap, bad things are not modified and the GC is triggered in between a swap, bad things
may happen! 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])
version 0.9.XX
==============
To implement - distinct types for array/seq indexes
------------ - GC: marker procs for native Nimrod GC and Boehm GC
* 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
------------
- implicit ref/ptr->var conversion; the compiler may store an object - implicit ref/ptr->var conversion; the compiler may store an object
implicitly on the heap for write barrier efficiency implicitly on the heap for write barrier efficiency
- resizing of strings/sequences could take into account the memory that - 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 - 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 is hard because of partial evaluation --> symbol files will fix this as
a side effect 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? - prefer proc in current module over other procs with same overloading result?
- real types for template results
- generalized case statement (requires better transf) - generalized case statement (requires better transf)
- tlastmod returns wrong results on BSD (Linux, MacOS X: works) - tlastmod returns wrong results on BSD (Linux, MacOS X: works)
- nested tuple unpacking - nested tuple unpacking
@ -96,7 +86,7 @@ Library
Version 2 Version 2
--------- =========
- language change: inheritance should only work with reference types, so that - language change: inheritance should only work with reference types, so that
the ``type`` field is not needed for objects! --> zero overhead aggregation 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 var x = myProc() # checks myProc() initializes every pointer explicitely
- the two other parsers - the two other parsers
- rethink the syntax: distinction between expr and stmt is unfortunate;
indentation handling is quite complex too
Low priority Low priority
@ -135,10 +127,3 @@ Low priority
important than constructors) important than constructors)
- code generated for type information is wasteful - code generated for type information is wasteful
Other ideas
-----------
- startsWith `=^`
- endsWith `=$`
- ignore case `=?` --> `=$?` too?