implemented locking levels; still incomplete
This commit is contained in:
parent
1088814e56
commit
d576fbb39a
7 changed files with 58 additions and 19 deletions
|
|
@ -1165,6 +1165,7 @@ proc newProcNode*(kind: TNodeKind, info: TLineInfo, body: PNode,
|
||||||
result.sons = @[name, pattern, genericParams, params,
|
result.sons = @[name, pattern, genericParams, params,
|
||||||
pragmas, exceptions, body]
|
pragmas, exceptions, body]
|
||||||
|
|
||||||
|
const UnspecifiedLockLevel* = -1
|
||||||
|
|
||||||
proc newType(kind: TTypeKind, owner: PSym): PType =
|
proc newType(kind: TTypeKind, owner: PSym): PType =
|
||||||
new(result)
|
new(result)
|
||||||
|
|
@ -1173,6 +1174,7 @@ proc newType(kind: TTypeKind, owner: PSym): PType =
|
||||||
result.size = - 1
|
result.size = - 1
|
||||||
result.align = 2 # default alignment
|
result.align = 2 # default alignment
|
||||||
result.id = getID()
|
result.id = getID()
|
||||||
|
result.lockLevel = UnspecifiedLockLevel
|
||||||
when debugIds:
|
when debugIds:
|
||||||
registerId(result)
|
registerId(result)
|
||||||
#if result.id < 2000:
|
#if result.id < 2000:
|
||||||
|
|
@ -1195,6 +1197,7 @@ proc assignType(dest, src: PType) =
|
||||||
dest.align = src.align
|
dest.align = src.align
|
||||||
dest.destructor = src.destructor
|
dest.destructor = src.destructor
|
||||||
dest.deepCopy = src.deepCopy
|
dest.deepCopy = src.deepCopy
|
||||||
|
dest.lockLevel = src.lockLevel
|
||||||
# this fixes 'type TLock = TSysLock':
|
# this fixes 'type TLock = TSysLock':
|
||||||
if src.sym != nil:
|
if src.sym != nil:
|
||||||
if dest.sym != nil:
|
if dest.sym != nil:
|
||||||
|
|
|
||||||
|
|
@ -898,7 +898,7 @@ include ccgtrav
|
||||||
|
|
||||||
proc genDeepCopyProc(m: BModule; s: PSym; result: PRope) =
|
proc genDeepCopyProc(m: BModule; s: PSym; result: PRope) =
|
||||||
genProc(m, s)
|
genProc(m, s)
|
||||||
appf(m.s[cfsTypeInit3], "$1.deepcopy =(N_NIMCALL_PTR(void*,)(void*))$2;$n",
|
appf(m.s[cfsTypeInit3], "$1.deepcopy =(void* (N_RAW_NIMCALL*)(void*))$2;$n",
|
||||||
[result, s.loc.r])
|
[result, s.loc.r])
|
||||||
|
|
||||||
proc genTypeInfo(m: BModule, t: PType): PRope =
|
proc genTypeInfo(m: BModule, t: PType): PRope =
|
||||||
|
|
|
||||||
|
|
@ -529,10 +529,10 @@ proc pragmaLocks(c: PContext, it: PNode): int16 =
|
||||||
if it.kind != nkExprColonExpr:
|
if it.kind != nkExprColonExpr:
|
||||||
invalidPragma(it)
|
invalidPragma(it)
|
||||||
else:
|
else:
|
||||||
let n = it[1]
|
if it[1].kind != nkNilLit:
|
||||||
let x = expectIntLit(c, n)
|
let x = expectIntLit(c, it)
|
||||||
if x < low(int16) or x > high(int16):
|
if x < 0 or x > high(int16):
|
||||||
localError(n.info, "integer must be in the valid range of int16")
|
localError(it[1].info, "integer must be within 0..high(int16)")
|
||||||
else:
|
else:
|
||||||
result = int16(x)
|
result = int16(x)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -69,24 +69,44 @@ type
|
||||||
TEffects = object
|
TEffects = object
|
||||||
exc: PNode # stack of exceptions
|
exc: PNode # stack of exceptions
|
||||||
tags: PNode # list of tags
|
tags: PNode # list of tags
|
||||||
uses: PNode # list of used global variables
|
|
||||||
bottom: int
|
bottom: int
|
||||||
owner: PSym
|
owner: PSym
|
||||||
init: seq[int] # list of initialized variables
|
init: seq[int] # list of initialized variables
|
||||||
guards: TModel # nested guards
|
guards: TModel # nested guards
|
||||||
locked: seq[PNode] # locked locations
|
locked: seq[PNode] # locked locations
|
||||||
gcUnsafe, isRecursive, isToplevel: bool
|
gcUnsafe, isRecursive, isToplevel: bool
|
||||||
|
maxLockLevel, currLockLevel: int16
|
||||||
PEffects = var TEffects
|
PEffects = var TEffects
|
||||||
|
|
||||||
proc isLocalVar(a: PEffects, s: PSym): bool =
|
proc isLocalVar(a: PEffects, s: PSym): bool =
|
||||||
s.kind in {skVar, skResult} and sfGlobal notin s.flags and s.owner == a.owner
|
s.kind in {skVar, skResult} and sfGlobal notin s.flags and s.owner == a.owner
|
||||||
|
|
||||||
|
proc getLockLevel(t: PType): int16 =
|
||||||
|
var t = t
|
||||||
|
# tyGenericInst(TLock {tyGenericBody}, tyStatic, tyObject):
|
||||||
|
if t.kind == tyGenericInst and t.len == 3: t = t.sons[1]
|
||||||
|
if t.kind == tyStatic and t.n != nil and t.n.kind in {nkCharLit..nkInt64Lit}:
|
||||||
|
result = t.n.intVal.int16
|
||||||
|
|
||||||
proc lockLocations(a: PEffects; pragma: PNode) =
|
proc lockLocations(a: PEffects; pragma: PNode) =
|
||||||
if pragma.kind != nkExprColonExpr:
|
if pragma.kind != nkExprColonExpr:
|
||||||
internalError(pragma.info, "no colon")
|
internalError(pragma.info, "no colon")
|
||||||
return
|
return
|
||||||
|
var firstLL = -1'i16
|
||||||
for x in pragma[1]:
|
for x in pragma[1]:
|
||||||
|
let thisLL = getLockLevel(x.typ)
|
||||||
|
if thisLL != 0:
|
||||||
|
if firstLL < 0: firstLL = thisLL
|
||||||
|
elif firstLL != thisLL:
|
||||||
|
localError(x.info, errGenerated,
|
||||||
|
"multi-lock requires the same static lock level for every operand")
|
||||||
|
a.maxLockLevel = max(a.maxLockLevel, firstLL)
|
||||||
a.locked.add x
|
a.locked.add x
|
||||||
|
if firstLL >= 0 and firstLL != a.currLockLevel:
|
||||||
|
if a.currLockLevel > 0 and a.currLockLevel < firstLL:
|
||||||
|
localError(pragma.info, errGenerated,
|
||||||
|
"invalid nested locking")
|
||||||
|
a.currLockLevel = firstLL
|
||||||
|
|
||||||
proc guardGlobal(a: PEffects; n: PNode; guard: PSym) =
|
proc guardGlobal(a: PEffects; n: PNode; guard: PSym) =
|
||||||
# check whether the corresponding lock is held:
|
# check whether the corresponding lock is held:
|
||||||
|
|
@ -242,7 +262,8 @@ proc mergeTags(a: PEffects, b, comesFrom: PNode) =
|
||||||
proc listEffects(a: PEffects) =
|
proc listEffects(a: PEffects) =
|
||||||
for e in items(a.exc): message(e.info, hintUser, typeToString(e.typ))
|
for e in items(a.exc): message(e.info, hintUser, typeToString(e.typ))
|
||||||
for e in items(a.tags): message(e.info, hintUser, typeToString(e.typ))
|
for e in items(a.tags): message(e.info, hintUser, typeToString(e.typ))
|
||||||
for e in items(a.uses): message(e.info, hintUser, e.sym.name.s)
|
#if a.maxLockLevel != 0:
|
||||||
|
# message(e.info, hintUser, "lockLevel: " & a.maxLockLevel)
|
||||||
|
|
||||||
proc catches(tracked: PEffects, e: PType) =
|
proc catches(tracked: PEffects, e: PType) =
|
||||||
let e = skipTypes(e, skipPtrs)
|
let e = skipTypes(e, skipPtrs)
|
||||||
|
|
@ -368,7 +389,6 @@ proc documentRaises*(n: PNode) =
|
||||||
if n.sons[namePos].kind != nkSym: return
|
if n.sons[namePos].kind != nkSym: return
|
||||||
documentEffect(n, n.sons[pragmasPos], wRaises, exceptionEffects)
|
documentEffect(n, n.sons[pragmasPos], wRaises, exceptionEffects)
|
||||||
documentEffect(n, n.sons[pragmasPos], wTags, tagEffects)
|
documentEffect(n, n.sons[pragmasPos], wTags, tagEffects)
|
||||||
#documentEffect(n, n.sons[pragmasPos], wUses, usesEffects)
|
|
||||||
|
|
||||||
template notGcSafe(t): expr = {tfGcSafe, tfNoSideEffect} * t.flags == {}
|
template notGcSafe(t): expr = {tfGcSafe, tfNoSideEffect} * t.flags == {}
|
||||||
|
|
||||||
|
|
@ -376,6 +396,15 @@ proc importedFromC(n: PNode): bool =
|
||||||
# when imported from C, we assume GC-safety.
|
# when imported from C, we assume GC-safety.
|
||||||
result = n.kind == nkSym and sfImportc in n.sym.flags
|
result = n.kind == nkSym and sfImportc in n.sym.flags
|
||||||
|
|
||||||
|
proc mergeLockLevels(tracked: PEffects, n: PNode, t: PType) =
|
||||||
|
if t.lockLevel > tracked.currLockLevel:
|
||||||
|
# if in lock section:
|
||||||
|
if tracked.currLockLevel > 0:
|
||||||
|
localError n.info, errGenerated,
|
||||||
|
"expected lock level <= " & $tracked.currLockLevel &
|
||||||
|
" but got lock level " & $t.lockLevel
|
||||||
|
tracked.maxLockLevel = max(tracked.maxLockLevel, t.lockLevel)
|
||||||
|
|
||||||
proc propagateEffects(tracked: PEffects, n: PNode, s: PSym) =
|
proc propagateEffects(tracked: PEffects, n: PNode, s: PSym) =
|
||||||
let pragma = s.ast.sons[pragmasPos]
|
let pragma = s.ast.sons[pragmasPos]
|
||||||
let spec = effectSpec(pragma, wRaises)
|
let spec = effectSpec(pragma, wRaises)
|
||||||
|
|
@ -387,6 +416,7 @@ proc propagateEffects(tracked: PEffects, n: PNode, s: PSym) =
|
||||||
if notGcSafe(s.typ) and sfImportc notin s.flags:
|
if notGcSafe(s.typ) and sfImportc notin s.flags:
|
||||||
if warnGcUnsafe in gNotes: message(n.info, warnGcUnsafe, renderTree(n))
|
if warnGcUnsafe in gNotes: message(n.info, warnGcUnsafe, renderTree(n))
|
||||||
tracked.gcUnsafe = true
|
tracked.gcUnsafe = true
|
||||||
|
mergeLockLevels(tracked, n, s.typ)
|
||||||
|
|
||||||
proc notNilCheck(tracked: PEffects, n: PNode, paramType: PType) =
|
proc notNilCheck(tracked: PEffects, n: PNode, paramType: PType) =
|
||||||
let n = n.skipConv
|
let n = n.skipConv
|
||||||
|
|
@ -577,6 +607,7 @@ proc track(tracked: PEffects, n: PNode) =
|
||||||
if not (a.kind == nkSym and a.sym == tracked.owner):
|
if not (a.kind == nkSym and a.sym == tracked.owner):
|
||||||
message(n.info, warnGcUnsafe, renderTree(n))
|
message(n.info, warnGcUnsafe, renderTree(n))
|
||||||
tracked.gcUnsafe = true
|
tracked.gcUnsafe = true
|
||||||
|
mergeLockLevels(tracked, n, op)
|
||||||
for i in 1 .. <len(n): trackOperand(tracked, n.sons[i], paramType(op, i))
|
for i in 1 .. <len(n): trackOperand(tracked, n.sons[i], paramType(op, i))
|
||||||
if a.kind == nkSym and a.sym.magic in {mNew, mNewFinalize, mNewSeq}:
|
if a.kind == nkSym and a.sym.magic in {mNew, mNewFinalize, mNewSeq}:
|
||||||
# may not look like an assignment, but it is:
|
# may not look like an assignment, but it is:
|
||||||
|
|
@ -644,11 +675,13 @@ proc track(tracked: PEffects, n: PNode) =
|
||||||
of nkPragmaBlock:
|
of nkPragmaBlock:
|
||||||
let pragmaList = n.sons[0]
|
let pragmaList = n.sons[0]
|
||||||
let oldLocked = tracked.locked.len
|
let oldLocked = tracked.locked.len
|
||||||
|
let oldLockLevel = tracked.currLockLevel
|
||||||
for i in 0 .. <pragmaList.len:
|
for i in 0 .. <pragmaList.len:
|
||||||
if whichPragma(pragmaList.sons[i]) == wLocks:
|
if whichPragma(pragmaList.sons[i]) == wLocks:
|
||||||
lockLocations(tracked, pragmaList.sons[i])
|
lockLocations(tracked, pragmaList.sons[i])
|
||||||
track(tracked, n.lastSon)
|
track(tracked, n.lastSon)
|
||||||
setLen(tracked.locked, oldLocked)
|
setLen(tracked.locked, oldLocked)
|
||||||
|
tracked.currLockLevel = oldLockLevel
|
||||||
of nkTypeSection, nkProcDef, nkConverterDef, nkMethodDef, nkIteratorDef,
|
of nkTypeSection, nkProcDef, nkConverterDef, nkMethodDef, nkIteratorDef,
|
||||||
nkMacroDef, nkTemplateDef:
|
nkMacroDef, nkTemplateDef:
|
||||||
discard
|
discard
|
||||||
|
|
@ -699,6 +732,10 @@ proc checkMethodEffects*(disp, branch: PSym) =
|
||||||
if sfThread in disp.flags and notGcSafe(branch.typ):
|
if sfThread in disp.flags and notGcSafe(branch.typ):
|
||||||
localError(branch.info, "base method is GC-safe, but '$1' is not" %
|
localError(branch.info, "base method is GC-safe, but '$1' is not" %
|
||||||
branch.name.s)
|
branch.name.s)
|
||||||
|
if branch.typ.lockLevel > disp.typ.lockLevel:
|
||||||
|
localError(branch.info,
|
||||||
|
"base method has lock level $1, but dispatcher has $2" %
|
||||||
|
[$branch.typ.lockLevel, $disp.typ.lockLevel])
|
||||||
|
|
||||||
proc setEffectsForProcType*(t: PType, n: PNode) =
|
proc setEffectsForProcType*(t: PType, n: PNode) =
|
||||||
var effects = t.n.sons[0]
|
var effects = t.n.sons[0]
|
||||||
|
|
@ -764,6 +801,12 @@ proc trackProc*(s: PSym, body: PNode) =
|
||||||
else:
|
else:
|
||||||
localError(s.info, warnGcUnsafe2, s.name.s)
|
localError(s.info, warnGcUnsafe2, s.name.s)
|
||||||
if not t.gcUnsafe: s.typ.flags.incl tfGcSafe
|
if not t.gcUnsafe: s.typ.flags.incl tfGcSafe
|
||||||
|
if s.typ.lockLevel == UnspecifiedLockLevel:
|
||||||
|
s.typ.lockLevel = t.maxLockLevel
|
||||||
|
elif t.maxLockLevel > s.typ.lockLevel:
|
||||||
|
localError(s.info,
|
||||||
|
"declared lock level is $1, but real lock level is $2" %
|
||||||
|
[$s.typ.lockLevel, $t.maxLockLevel])
|
||||||
|
|
||||||
proc trackTopLevelStmt*(module: PSym; n: PNode) =
|
proc trackTopLevelStmt*(module: PSym; n: PNode) =
|
||||||
if n.kind in {nkPragma, nkMacroDef, nkTemplateDef, nkProcDef,
|
if n.kind in {nkPragma, nkMacroDef, nkTemplateDef, nkProcDef,
|
||||||
|
|
|
||||||
|
|
@ -304,13 +304,6 @@ proc handleGenericInvokation(cl: var TReplTypeVars, t: PType): PType =
|
||||||
# 'deepCopy' needs to be instantiated for
|
# 'deepCopy' needs to be instantiated for
|
||||||
# generics *when the type is constructed*:
|
# generics *when the type is constructed*:
|
||||||
newbody.deepCopy = cl.c.instDeepCopy(cl.c, dc, result, cl.info)
|
newbody.deepCopy = cl.c.instDeepCopy(cl.c, dc, result, cl.info)
|
||||||
when false:
|
|
||||||
var bindings: TIdTable
|
|
||||||
initIdTable(bindings)
|
|
||||||
debug newbody
|
|
||||||
bindings.idTablePut(dc.ast[genericParamsPos].sons[0].typ, newbody)
|
|
||||||
newbody.deepCopy = cl.c.semGenerateInstance(cl.c, dc, bindings, cl.info)
|
|
||||||
assert sfFromGeneric in newbody.deepCopy.flags
|
|
||||||
|
|
||||||
proc eraseVoidParams*(t: PType) =
|
proc eraseVoidParams*(t: PType) =
|
||||||
if t.sons[0] != nil and t.sons[0].kind == tyEmpty:
|
if t.sons[0] != nil and t.sons[0].kind == tyEmpty:
|
||||||
|
|
|
||||||
|
|
@ -141,9 +141,11 @@ __clang__
|
||||||
/* these compilers have a fastcall so use it: */
|
/* these compilers have a fastcall so use it: */
|
||||||
# define N_NIMCALL(rettype, name) rettype __fastcall name
|
# define N_NIMCALL(rettype, name) rettype __fastcall name
|
||||||
# define N_NIMCALL_PTR(rettype, name) rettype (__fastcall *name)
|
# define N_NIMCALL_PTR(rettype, name) rettype (__fastcall *name)
|
||||||
|
# define N_RAW_NIMCALL __fastcall
|
||||||
#else
|
#else
|
||||||
# define N_NIMCALL(rettype, name) rettype name /* no modifier */
|
# define N_NIMCALL(rettype, name) rettype name /* no modifier */
|
||||||
# define N_NIMCALL_PTR(rettype, name) rettype (*name)
|
# define N_NIMCALL_PTR(rettype, name) rettype (*name)
|
||||||
|
# define N_RAW_NIMCALL
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define N_CLOSURE(rettype, name) N_NIMCALL(rettype, name)
|
#define N_CLOSURE(rettype, name) N_NIMCALL(rettype, name)
|
||||||
|
|
|
||||||
2
todo.txt
2
todo.txt
|
|
@ -1,7 +1,6 @@
|
||||||
version 0.10
|
version 0.10
|
||||||
============
|
============
|
||||||
|
|
||||||
- use the effect system for static deadlock prevention
|
|
||||||
- Test nimfix on various babel packages
|
- Test nimfix on various babel packages
|
||||||
- deprecate recursive tuples; tuple needs laxer type checking
|
- deprecate recursive tuples; tuple needs laxer type checking
|
||||||
- string case should require an 'else'
|
- string case should require an 'else'
|
||||||
|
|
@ -30,7 +29,6 @@ Low priority:
|
||||||
- support for exception propagation? (hard to implement)
|
- support for exception propagation? (hard to implement)
|
||||||
- the copying of the 'ref Promise' into the thead local storage only
|
- the copying of the 'ref Promise' into the thead local storage only
|
||||||
happens to work due to the write barrier's implementation
|
happens to work due to the write barrier's implementation
|
||||||
- implement lock levels
|
|
||||||
|
|
||||||
|
|
||||||
Misc
|
Misc
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue