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);
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,9 +1644,16 @@ 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)
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)):
@ -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)

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
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,23 +388,6 @@ 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],
@ -417,11 +402,6 @@ 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):
@ -429,33 +409,22 @@ proc assignLocalVar(p: BProc, s: PSym) =
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 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 sfThreadVar in s.flags: declareThreadVar(p.module, s)
else: app(p.module.s[cfsVars], getTypeDesc(p.module, s.loc.t))
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 ==
if p.module.module.options * {optStackTrace, optEndb} ==
{optStackTrace, optEndb}:
appcg(p.module, p.module.s[cfsDebugInit],
"#dbgRegisterGlobal($1, &$2, $3);$n",
@ -463,10 +432,6 @@ proc assignGlobalVar(p: BProc, s: PSym) =
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)
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 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",
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):
@ -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)])
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:

View file

@ -86,7 +86,7 @@ type
errCannotInterpretNodeX, errFieldXNotFound, errInvalidConversionFromTypeX,
errAssertionFailed, errCannotGenerateCodeForX, errXRequiresOneArgument,
errUnhandledExceptionX, errCyclicTree, errXisNoMacroOrTemplate,
errXhasSideEffects, errIteratorExpected, errDifferentHeaps,
errXhasSideEffects, errIteratorExpected,
errUser,
warnCannotOpenFile,
warnOctalEscape, warnXIsNeverRead, warnXmightNotBeenInit,
@ -94,6 +94,7 @@ type
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",

View file

@ -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,13 +221,14 @@ 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:
if tfNoSideEffect notin n[0].typ.flags:
Message(n.info, warnAnalysisLoophole, renderTree(n))
result = toNil
else:
@ -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)

View file

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

View file

@ -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
var
framePtr {.rtlThreadVar.}: PFrame
excHandler {.rtlThreadVar.}: PSafePoint
# list of exception handlers
# 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!
assertBuf: string # we need a different buffer for
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
tempFrames: array [0..127, PFrame] # cannot be allocated on the stack!
gAssertionFailed: ref EAssertionFailed
gAssertionFailed {.rtlThreadVar.}: 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
@ -144,17 +134,23 @@ when defined(nativeStacktrace) and nativeStackTraceSupported:
# 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) =
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

View file

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

View file

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

View file

@ -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
----
@ -34,25 +38,12 @@ Bugs
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])
version 0.9.XX
==============
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
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?