Merge branch 'devel' of github.com:nim-lang/Nim into devel
This commit is contained in:
commit
9ac76b49c0
21 changed files with 440 additions and 277 deletions
|
|
@ -657,14 +657,62 @@ proc compileCFile(list: TLinkedList, script: var Rope, cmds: var TStringSeq,
|
||||||
add(script, tnl)
|
add(script, tnl)
|
||||||
it = PStrEntry(it.next)
|
it = PStrEntry(it.next)
|
||||||
|
|
||||||
|
proc getLinkCmd(projectfile, objfiles: string): string =
|
||||||
|
if optGenStaticLib in gGlobalOptions:
|
||||||
|
var libname: string
|
||||||
|
if options.outFile.len > 0:
|
||||||
|
libname = options.outFile.expandTilde
|
||||||
|
if not libname.isAbsolute():
|
||||||
|
libname = getCurrentDir() / libname
|
||||||
|
else:
|
||||||
|
libname = (libNameTmpl() % splitFile(gProjectName).name)
|
||||||
|
result = CC[cCompiler].buildLib % ["libfile", libname,
|
||||||
|
"objfiles", objfiles]
|
||||||
|
else:
|
||||||
|
var linkerExe = getConfigVar(cCompiler, ".linkerexe")
|
||||||
|
if len(linkerExe) == 0: linkerExe = cCompiler.getLinkerExe
|
||||||
|
if needsExeExt(): linkerExe = addFileExt(linkerExe, "exe")
|
||||||
|
if noAbsolutePaths(): result = quoteShell(linkerExe)
|
||||||
|
else: result = quoteShell(joinPath(ccompilerpath, linkerExe))
|
||||||
|
let buildgui = if optGenGuiApp in gGlobalOptions: CC[cCompiler].buildGui
|
||||||
|
else: ""
|
||||||
|
var exefile, builddll: string
|
||||||
|
if optGenDynLib in gGlobalOptions:
|
||||||
|
exefile = platform.OS[targetOS].dllFrmt % splitFile(projectfile).name
|
||||||
|
builddll = CC[cCompiler].buildDll
|
||||||
|
else:
|
||||||
|
exefile = splitFile(projectfile).name & platform.OS[targetOS].exeExt
|
||||||
|
builddll = ""
|
||||||
|
if options.outFile.len > 0:
|
||||||
|
exefile = options.outFile.expandTilde
|
||||||
|
if not exefile.isAbsolute():
|
||||||
|
exefile = getCurrentDir() / exefile
|
||||||
|
if not noAbsolutePaths():
|
||||||
|
if not exefile.isAbsolute():
|
||||||
|
exefile = joinPath(splitFile(projectfile).dir, exefile)
|
||||||
|
if optCDebug in gGlobalOptions:
|
||||||
|
writeDebugInfo(exefile.changeFileExt("ndb"))
|
||||||
|
exefile = quoteShell(exefile)
|
||||||
|
let linkOptions = getLinkOptions() & " " &
|
||||||
|
getConfigVar(cCompiler, ".options.linker")
|
||||||
|
result = quoteShell(result % ["builddll", builddll,
|
||||||
|
"buildgui", buildgui, "options", linkOptions, "objfiles", objfiles,
|
||||||
|
"exefile", exefile, "nim", getPrefixDir(), "lib", libpath])
|
||||||
|
result.add ' '
|
||||||
|
addf(result, CC[cCompiler].linkTmpl, ["builddll", builddll,
|
||||||
|
"buildgui", buildgui, "options", linkOptions,
|
||||||
|
"objfiles", objfiles, "exefile", exefile,
|
||||||
|
"nim", quoteShell(getPrefixDir()),
|
||||||
|
"lib", quoteShell(libpath)])
|
||||||
|
|
||||||
proc callCCompiler*(projectfile: string) =
|
proc callCCompiler*(projectfile: string) =
|
||||||
var
|
var
|
||||||
linkCmd, buildgui, builddll: string
|
linkCmd, buildgui: string
|
||||||
if gGlobalOptions * {optCompileOnly, optGenScript} == {optCompileOnly}:
|
if gGlobalOptions * {optCompileOnly, optGenScript} == {optCompileOnly}:
|
||||||
return # speed up that call if only compiling and no script shall be
|
return # speed up that call if only compiling and no script shall be
|
||||||
# generated
|
# generated
|
||||||
fileCounter = 0
|
fileCounter = 0
|
||||||
var c = cCompiler
|
#var c = cCompiler
|
||||||
var script: Rope = nil
|
var script: Rope = nil
|
||||||
var cmds: TStringSeq = @[]
|
var cmds: TStringSeq = @[]
|
||||||
var prettyCmds: TStringSeq = @[]
|
var prettyCmds: TStringSeq = @[]
|
||||||
|
|
@ -708,46 +756,7 @@ proc callCCompiler*(projectfile: string) =
|
||||||
addFileExt(objFile, CC[cCompiler].objExt)))
|
addFileExt(objFile, CC[cCompiler].objExt)))
|
||||||
it = PStrEntry(it.next)
|
it = PStrEntry(it.next)
|
||||||
|
|
||||||
if optGenStaticLib in gGlobalOptions:
|
linkCmd = getLinkCmd(projectfile, objfiles)
|
||||||
let name = splitFile(gProjectName).name
|
|
||||||
linkCmd = CC[c].buildLib % ["libfile", (libNameTmpl() % name),
|
|
||||||
"objfiles", objfiles]
|
|
||||||
else:
|
|
||||||
var linkerExe = getConfigVar(c, ".linkerexe")
|
|
||||||
if len(linkerExe) == 0: linkerExe = c.getLinkerExe
|
|
||||||
if needsExeExt(): linkerExe = addFileExt(linkerExe, "exe")
|
|
||||||
if noAbsolutePaths(): linkCmd = quoteShell(linkerExe)
|
|
||||||
else: linkCmd = quoteShell(joinPath(ccompilerpath, linkerExe))
|
|
||||||
if optGenGuiApp in gGlobalOptions: buildgui = CC[c].buildGui
|
|
||||||
else: buildgui = ""
|
|
||||||
var exefile: string
|
|
||||||
if optGenDynLib in gGlobalOptions:
|
|
||||||
exefile = platform.OS[targetOS].dllFrmt % splitFile(projectfile).name
|
|
||||||
builddll = CC[c].buildDll
|
|
||||||
else:
|
|
||||||
exefile = splitFile(projectfile).name & platform.OS[targetOS].exeExt
|
|
||||||
builddll = ""
|
|
||||||
if options.outFile.len > 0:
|
|
||||||
exefile = options.outFile.expandTilde
|
|
||||||
if not exefile.isAbsolute():
|
|
||||||
exefile = getCurrentDir() / exefile
|
|
||||||
if not noAbsolutePaths():
|
|
||||||
if not exefile.isAbsolute():
|
|
||||||
exefile = joinPath(splitFile(projectfile).dir, exefile)
|
|
||||||
if optCDebug in gGlobalOptions:
|
|
||||||
writeDebugInfo(exefile.changeFileExt("ndb"))
|
|
||||||
exefile = quoteShell(exefile)
|
|
||||||
let linkOptions = getLinkOptions() & " " &
|
|
||||||
getConfigVar(cCompiler, ".options.linker")
|
|
||||||
linkCmd = quoteShell(linkCmd % ["builddll", builddll,
|
|
||||||
"buildgui", buildgui, "options", linkOptions, "objfiles", objfiles,
|
|
||||||
"exefile", exefile, "nim", getPrefixDir(), "lib", libpath])
|
|
||||||
linkCmd.add ' '
|
|
||||||
addf(linkCmd, CC[c].linkTmpl, ["builddll", builddll,
|
|
||||||
"buildgui", buildgui, "options", linkOptions,
|
|
||||||
"objfiles", objfiles, "exefile", exefile,
|
|
||||||
"nim", quoteShell(getPrefixDir()),
|
|
||||||
"lib", quoteShell(libpath)])
|
|
||||||
if optCompileOnly notin gGlobalOptions:
|
if optCompileOnly notin gGlobalOptions:
|
||||||
execExternalProgram(linkCmd,
|
execExternalProgram(linkCmd,
|
||||||
if optListCmd in gGlobalOptions or gVerbosity > 1: hintExecuting else: hintLinking)
|
if optListCmd in gGlobalOptions or gVerbosity > 1: hintExecuting else: hintLinking)
|
||||||
|
|
@ -758,6 +767,62 @@ proc callCCompiler*(projectfile: string) =
|
||||||
add(script, tnl)
|
add(script, tnl)
|
||||||
generateScript(projectfile, script)
|
generateScript(projectfile, script)
|
||||||
|
|
||||||
|
from json import escapeJson
|
||||||
|
|
||||||
|
proc writeJsonBuildInstructions*(projectfile: string) =
|
||||||
|
template lit(x: untyped) = f.write x
|
||||||
|
template str(x: untyped) =
|
||||||
|
buf.setLen 0
|
||||||
|
escapeJson(x, buf)
|
||||||
|
f.write buf
|
||||||
|
|
||||||
|
proc cfiles(f: File; buf: var string; list: TLinkedList, isExternal: bool) =
|
||||||
|
var it = PStrEntry(list.head)
|
||||||
|
while it != nil:
|
||||||
|
let compileCmd = getCompileCFileCmd(it.data, isExternal)
|
||||||
|
lit "["
|
||||||
|
str it.data
|
||||||
|
lit ", "
|
||||||
|
str compileCmd
|
||||||
|
it = PStrEntry(it.next)
|
||||||
|
if it == nil:
|
||||||
|
lit "]\L"
|
||||||
|
else:
|
||||||
|
lit "],\L"
|
||||||
|
|
||||||
|
proc linkfiles(f: File; buf, objfiles: var string; toLink: TLinkedList) =
|
||||||
|
var it = PStrEntry(toLink.head)
|
||||||
|
while it != nil:
|
||||||
|
let objfile = addFileExt(it.data, CC[cCompiler].objExt)
|
||||||
|
str objfile
|
||||||
|
add(objfiles, ' ')
|
||||||
|
add(objfiles, quoteShell(objfile))
|
||||||
|
it = PStrEntry(it.next)
|
||||||
|
if it == nil:
|
||||||
|
lit "\L"
|
||||||
|
else:
|
||||||
|
lit ",\L"
|
||||||
|
|
||||||
|
var buf = newStringOfCap(50)
|
||||||
|
|
||||||
|
let file = projectfile.splitFile.name
|
||||||
|
let jsonFile = toGeneratedFile(file, "json")
|
||||||
|
|
||||||
|
var f: File
|
||||||
|
if open(f, jsonFile, fmWrite):
|
||||||
|
lit "{\"compile\":[\L"
|
||||||
|
cfiles(f, buf, toCompile, false)
|
||||||
|
lit "],\L\"extcompile\":[\L"
|
||||||
|
cfiles(f, buf, externalToCompile, true)
|
||||||
|
lit "],\L\"link\":[\L"
|
||||||
|
var objfiles = ""
|
||||||
|
linkfiles(f, buf, objfiles, toLink)
|
||||||
|
|
||||||
|
lit "],\L\"linkcmd\": "
|
||||||
|
str getLinkCmd(projectfile, objfiles)
|
||||||
|
lit "\L}\L"
|
||||||
|
close(f)
|
||||||
|
|
||||||
proc genMappingFiles(list: TLinkedList): Rope =
|
proc genMappingFiles(list: TLinkedList): Rope =
|
||||||
var it = PStrEntry(list.head)
|
var it = PStrEntry(list.head)
|
||||||
while it != nil:
|
while it != nil:
|
||||||
|
|
|
||||||
|
|
@ -66,7 +66,9 @@ proc commandCompileToC(graph: ModuleGraph; cache: IdentCache) =
|
||||||
compileProject(graph, cache)
|
compileProject(graph, cache)
|
||||||
cgenWriteModules()
|
cgenWriteModules()
|
||||||
if gCmd != cmdRun:
|
if gCmd != cmdRun:
|
||||||
extccomp.callCCompiler(changeFileExt(gProjectFull, ""))
|
let proj = changeFileExt(gProjectFull, "")
|
||||||
|
extccomp.callCCompiler(proj)
|
||||||
|
extccomp.writeJsonBuildInstructions(proj)
|
||||||
|
|
||||||
proc commandCompileToJS(graph: ModuleGraph; cache: IdentCache) =
|
proc commandCompileToJS(graph: ModuleGraph; cache: IdentCache) =
|
||||||
#incl(gGlobalOptions, optSafeCode)
|
#incl(gGlobalOptions, optSafeCode)
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ type
|
||||||
errIndexOutOfBounds, errIndexTypesDoNotMatch, errBracketsInvalidForType,
|
errIndexOutOfBounds, errIndexTypesDoNotMatch, errBracketsInvalidForType,
|
||||||
errValueOutOfSetBounds, errFieldInitTwice, errFieldNotInit,
|
errValueOutOfSetBounds, errFieldInitTwice, errFieldNotInit,
|
||||||
errExprXCannotBeCalled, errExprHasNoType, errExprXHasNoType,
|
errExprXCannotBeCalled, errExprHasNoType, errExprXHasNoType,
|
||||||
errCastNotInSafeMode, errExprCannotBeCastedToX, errCommaOrParRiExpected,
|
errCastNotInSafeMode, errExprCannotBeCastToX, errCommaOrParRiExpected,
|
||||||
errCurlyLeOrParLeExpected, errSectionExpected, errRangeExpected,
|
errCurlyLeOrParLeExpected, errSectionExpected, errRangeExpected,
|
||||||
errMagicOnlyInSystem, errPowerOfTwoExpected,
|
errMagicOnlyInSystem, errPowerOfTwoExpected,
|
||||||
errStringMayNotBeEmpty, errCallConvExpected, errProcOnlyOneCallConv,
|
errStringMayNotBeEmpty, errCallConvExpected, errProcOnlyOneCallConv,
|
||||||
|
|
@ -229,7 +229,7 @@ const
|
||||||
errExprHasNoType: "expression has no type",
|
errExprHasNoType: "expression has no type",
|
||||||
errExprXHasNoType: "expression \'$1\' has no type (or is ambiguous)",
|
errExprXHasNoType: "expression \'$1\' has no type (or is ambiguous)",
|
||||||
errCastNotInSafeMode: "\'cast\' not allowed in safe mode",
|
errCastNotInSafeMode: "\'cast\' not allowed in safe mode",
|
||||||
errExprCannotBeCastedToX: "expression cannot be casted to $1",
|
errExprCannotBeCastToX: "expression cannot be cast to $1",
|
||||||
errCommaOrParRiExpected: "',' or ')' expected",
|
errCommaOrParRiExpected: "',' or ')' expected",
|
||||||
errCurlyLeOrParLeExpected: "\'{\' or \'(\' expected",
|
errCurlyLeOrParLeExpected: "\'{\' or \'(\' expected",
|
||||||
errSectionExpected: "section (\'type\', \'proc\', etc.) expected",
|
errSectionExpected: "section (\'type\', \'proc\', etc.) expected",
|
||||||
|
|
|
||||||
|
|
@ -127,7 +127,7 @@ proc checkConvertible(c: PContext, castDest, src: PType): TConvStatus =
|
||||||
discard
|
discard
|
||||||
|
|
||||||
proc isCastable(dst, src: PType): bool =
|
proc isCastable(dst, src: PType): bool =
|
||||||
## Checks whether the source type can be casted to the destination type.
|
## Checks whether the source type can be cast to the destination type.
|
||||||
## Casting is very unrestrictive; casts are allowed as long as
|
## Casting is very unrestrictive; casts are allowed as long as
|
||||||
## castDest.size >= src.size, and typeAllowed(dst, skParam)
|
## castDest.size >= src.size, and typeAllowed(dst, skParam)
|
||||||
#const
|
#const
|
||||||
|
|
@ -223,7 +223,7 @@ proc semCast(c: PContext, n: PNode): PNode =
|
||||||
addSon(result, copyTree(n.sons[0]))
|
addSon(result, copyTree(n.sons[0]))
|
||||||
addSon(result, semExprWithType(c, n.sons[1]))
|
addSon(result, semExprWithType(c, n.sons[1]))
|
||||||
if not isCastable(result.typ, result.sons[1].typ):
|
if not isCastable(result.typ, result.sons[1].typ):
|
||||||
localError(result.info, errExprCannotBeCastedToX,
|
localError(result.info, errExprCannotBeCastToX,
|
||||||
typeToString(result.typ))
|
typeToString(result.typ))
|
||||||
|
|
||||||
proc semLowHigh(c: PContext, n: PNode, m: TMagic): PNode =
|
proc semLowHigh(c: PContext, n: PNode, m: TMagic): PNode =
|
||||||
|
|
|
||||||
|
|
@ -73,6 +73,10 @@ proc atomicTypeX(name: string; m: TMagic; t: PType; info: TLineInfo): PNode =
|
||||||
result = newSymNode(sym)
|
result = newSymNode(sym)
|
||||||
result.typ = t
|
result.typ = t
|
||||||
|
|
||||||
|
proc atomicTypeX(s: PSym; info: TLineInfo): PNode =
|
||||||
|
result = newSymNode(s)
|
||||||
|
result.info = info
|
||||||
|
|
||||||
proc mapTypeToAstX(t: PType; info: TLineInfo;
|
proc mapTypeToAstX(t: PType; info: TLineInfo;
|
||||||
inst=false; allowRecursionX=false): PNode
|
inst=false; allowRecursionX=false): PNode
|
||||||
|
|
||||||
|
|
@ -103,6 +107,7 @@ proc mapTypeToAstX(t: PType; info: TLineInfo;
|
||||||
inst=false; allowRecursionX=false): PNode =
|
inst=false; allowRecursionX=false): PNode =
|
||||||
var allowRecursion = allowRecursionX
|
var allowRecursion = allowRecursionX
|
||||||
template atomicType(name, m): untyped = atomicTypeX(name, m, t, info)
|
template atomicType(name, m): untyped = atomicTypeX(name, m, t, info)
|
||||||
|
template atomicType(s): untyped = atomicTypeX(s, info)
|
||||||
template mapTypeToAst(t,info): untyped = mapTypeToAstX(t, info, inst)
|
template mapTypeToAst(t,info): untyped = mapTypeToAstX(t, info, inst)
|
||||||
template mapTypeToAstR(t,info): untyped = mapTypeToAstX(t, info, inst, true)
|
template mapTypeToAstR(t,info): untyped = mapTypeToAstX(t, info, inst, true)
|
||||||
template mapTypeToAst(t,i,info): untyped =
|
template mapTypeToAst(t,i,info): untyped =
|
||||||
|
|
@ -125,7 +130,7 @@ proc mapTypeToAstX(t: PType; info: TLineInfo;
|
||||||
if allowRecursion: # getTypeImpl behavior: turn off recursion
|
if allowRecursion: # getTypeImpl behavior: turn off recursion
|
||||||
allowRecursion = false
|
allowRecursion = false
|
||||||
else: # getTypeInst behavior: return symbol
|
else: # getTypeInst behavior: return symbol
|
||||||
return atomicType(t.sym.name.s, t.sym.magic)
|
return atomicType(t.sym)
|
||||||
|
|
||||||
case t.kind
|
case t.kind
|
||||||
of tyNone: result = atomicType("none", mNone)
|
of tyNone: result = atomicType("none", mNone)
|
||||||
|
|
@ -180,9 +185,9 @@ proc mapTypeToAstX(t: PType; info: TLineInfo;
|
||||||
if allowRecursion or t.sym == nil:
|
if allowRecursion or t.sym == nil:
|
||||||
result = mapTypeToBracket("distinct", mDistinct, t, info)
|
result = mapTypeToBracket("distinct", mDistinct, t, info)
|
||||||
else:
|
else:
|
||||||
result = atomicType(t.sym.name.s, t.sym.magic)
|
result = atomicType(t.sym)
|
||||||
of tyGenericParam, tyForward:
|
of tyGenericParam, tyForward:
|
||||||
result = atomicType(t.sym.name.s, t.sym.magic)
|
result = atomicType(t.sym)
|
||||||
of tyObject:
|
of tyObject:
|
||||||
if inst:
|
if inst:
|
||||||
result = newNodeX(nkObjectTy)
|
result = newNodeX(nkObjectTy)
|
||||||
|
|
@ -206,7 +211,7 @@ proc mapTypeToAstX(t: PType; info: TLineInfo;
|
||||||
result.add mapTypeToAst(t.sons[0], info)
|
result.add mapTypeToAst(t.sons[0], info)
|
||||||
result.add copyTree(t.n)
|
result.add copyTree(t.n)
|
||||||
else:
|
else:
|
||||||
result = atomicType(t.sym.name.s, t.sym.magic)
|
result = atomicType(t.sym)
|
||||||
of tyEnum:
|
of tyEnum:
|
||||||
result = newNodeIT(nkEnumTy, if t.n.isNil: info else: t.n.info, t)
|
result = newNodeIT(nkEnumTy, if t.n.isNil: info else: t.n.info, t)
|
||||||
result.add copyTree(t.n)
|
result.add copyTree(t.n)
|
||||||
|
|
|
||||||
|
|
@ -160,7 +160,10 @@ proc peekLast*[T](deq: Deque[T]): T {.inline.} =
|
||||||
emptyCheck(deq)
|
emptyCheck(deq)
|
||||||
result = deq.data[(deq.tail - 1) and deq.mask]
|
result = deq.data[(deq.tail - 1) and deq.mask]
|
||||||
|
|
||||||
proc default[T](t: typedesc[T]): T {.inline.} = discard
|
template default[T](t: typedesc[T]): T =
|
||||||
|
var v: T
|
||||||
|
v
|
||||||
|
|
||||||
proc popFirst*[T](deq: var Deque[T]): T {.inline, discardable.} =
|
proc popFirst*[T](deq: var Deque[T]): T {.inline, discardable.} =
|
||||||
## Remove and returns the first element of the `deq`.
|
## Remove and returns the first element of the `deq`.
|
||||||
emptyCheck(deq)
|
emptyCheck(deq)
|
||||||
|
|
|
||||||
|
|
@ -154,7 +154,10 @@ proc add*[T](q: var Queue[T], item: T) =
|
||||||
q.data[q.wr] = item
|
q.data[q.wr] = item
|
||||||
q.wr = (q.wr + 1) and q.mask
|
q.wr = (q.wr + 1) and q.mask
|
||||||
|
|
||||||
proc default[T](t: typedesc[T]): T {.inline.} = discard
|
template default[T](t: typedesc[T]): T =
|
||||||
|
var v: T
|
||||||
|
v
|
||||||
|
|
||||||
proc pop*[T](q: var Queue[T]): T {.inline, discardable.} =
|
proc pop*[T](q: var Queue[T]): T {.inline, discardable.} =
|
||||||
## Remove and returns the first (oldest) element of the queue `q`.
|
## Remove and returns the first (oldest) element of the queue `q`.
|
||||||
emptyCheck(q)
|
emptyCheck(q)
|
||||||
|
|
|
||||||
|
|
@ -261,7 +261,9 @@ template doWhile(a, b) =
|
||||||
b
|
b
|
||||||
if not a: break
|
if not a: break
|
||||||
|
|
||||||
proc default[T](t: typedesc[T]): T {.inline.} = discard
|
template default[T](t: typedesc[T]): T =
|
||||||
|
var v: T
|
||||||
|
v
|
||||||
|
|
||||||
proc excl*[A](s: var HashSet[A], key: A) =
|
proc excl*[A](s: var HashSet[A], key: A) =
|
||||||
## Excludes `key` from the set `s`.
|
## Excludes `key` from the set `s`.
|
||||||
|
|
|
||||||
|
|
@ -954,7 +954,7 @@ proc inc*[A](t: var CountTable[A], key: A, val = 1) =
|
||||||
inc(t.counter)
|
inc(t.counter)
|
||||||
|
|
||||||
proc smallest*[A](t: CountTable[A]): tuple[key: A, val: int] =
|
proc smallest*[A](t: CountTable[A]): tuple[key: A, val: int] =
|
||||||
## returns the largest (key,val)-pair. Efficiency: O(n)
|
## returns the (key,val)-pair with the smallest `val`. Efficiency: O(n)
|
||||||
assert t.len > 0
|
assert t.len > 0
|
||||||
var minIdx = 0
|
var minIdx = 0
|
||||||
for h in 1..high(t.data):
|
for h in 1..high(t.data):
|
||||||
|
|
@ -1080,7 +1080,7 @@ proc inc*[A](t: CountTableRef[A], key: A, val = 1) =
|
||||||
t[].inc(key, val)
|
t[].inc(key, val)
|
||||||
|
|
||||||
proc smallest*[A](t: CountTableRef[A]): (A, int) =
|
proc smallest*[A](t: CountTableRef[A]): (A, int) =
|
||||||
## returns the largest (key,val)-pair. Efficiency: O(n)
|
## returns the (key,val)-pair with the smallest `val`. Efficiency: O(n)
|
||||||
t[].smallest
|
t[].smallest
|
||||||
|
|
||||||
proc largest*[A](t: CountTableRef[A]): (A, int) =
|
proc largest*[A](t: CountTableRef[A]): (A, int) =
|
||||||
|
|
|
||||||
|
|
@ -18,10 +18,12 @@
|
||||||
## Supported features: files, sockets, pipes, timers, processes, signals
|
## Supported features: files, sockets, pipes, timers, processes, signals
|
||||||
## and user events.
|
## and user events.
|
||||||
##
|
##
|
||||||
## Fully supported OS: MacOSX, FreeBSD, OpenBSD, NetBSD, Linux.
|
## Fully supported OS: MacOSX, FreeBSD, OpenBSD, NetBSD, Linux (except
|
||||||
|
## for Android).
|
||||||
##
|
##
|
||||||
## Partially supported OS: Windows (only sockets and user events),
|
## Partially supported OS: Windows (only sockets and user events),
|
||||||
## Solaris (files, sockets, handles and user events).
|
## Solaris (files, sockets, handles and user events).
|
||||||
|
## Android (files, sockets, handles and user events).
|
||||||
##
|
##
|
||||||
## TODO: ``/dev/poll``, ``event ports`` and filesystem events.
|
## TODO: ``/dev/poll``, ``event ports`` and filesystem events.
|
||||||
|
|
||||||
|
|
@ -29,9 +31,11 @@ import os
|
||||||
|
|
||||||
const hasThreadSupport = compileOption("threads") and defined(threadsafe)
|
const hasThreadSupport = compileOption("threads") and defined(threadsafe)
|
||||||
|
|
||||||
const supportedPlatform = defined(macosx) or defined(freebsd) or
|
const ioselSupportedPlatform* = defined(macosx) or defined(freebsd) or
|
||||||
defined(netbsd) or defined(openbsd) or
|
defined(netbsd) or defined(openbsd) or
|
||||||
defined(linux)
|
(defined(linux) and not defined(android))
|
||||||
|
## This constant is used to determine whether the destination platform is
|
||||||
|
## fully supported by ``ioselectors`` module.
|
||||||
|
|
||||||
const bsdPlatform = defined(macosx) or defined(freebsd) or
|
const bsdPlatform = defined(macosx) or defined(freebsd) or
|
||||||
defined(netbsd) or defined(openbsd)
|
defined(netbsd) or defined(openbsd)
|
||||||
|
|
@ -244,7 +248,7 @@ else:
|
||||||
skey.key.fd = pkeyfd
|
skey.key.fd = pkeyfd
|
||||||
skey.key.data = pdata
|
skey.key.data = pdata
|
||||||
|
|
||||||
when supportedPlatform:
|
when ioselSupportedPlatform:
|
||||||
template blockSignals(newmask: var Sigset, oldmask: var Sigset) =
|
template blockSignals(newmask: var Sigset, oldmask: var Sigset) =
|
||||||
when hasThreadSupport:
|
when hasThreadSupport:
|
||||||
if posix.pthread_sigmask(SIG_BLOCK, newmask, oldmask) == -1:
|
if posix.pthread_sigmask(SIG_BLOCK, newmask, oldmask) == -1:
|
||||||
|
|
|
||||||
|
|
@ -14,27 +14,29 @@ import posix, times
|
||||||
# Maximum number of events that can be returned
|
# Maximum number of events that can be returned
|
||||||
const MAX_EPOLL_RESULT_EVENTS = 64
|
const MAX_EPOLL_RESULT_EVENTS = 64
|
||||||
|
|
||||||
type
|
when not defined(android):
|
||||||
SignalFdInfo* {.importc: "struct signalfd_siginfo",
|
type
|
||||||
header: "<sys/signalfd.h>", pure, final.} = object
|
SignalFdInfo* {.importc: "struct signalfd_siginfo",
|
||||||
ssi_signo*: uint32
|
header: "<sys/signalfd.h>", pure, final.} = object
|
||||||
ssi_errno*: int32
|
ssi_signo*: uint32
|
||||||
ssi_code*: int32
|
ssi_errno*: int32
|
||||||
ssi_pid*: uint32
|
ssi_code*: int32
|
||||||
ssi_uid*: uint32
|
ssi_pid*: uint32
|
||||||
ssi_fd*: int32
|
ssi_uid*: uint32
|
||||||
ssi_tid*: uint32
|
ssi_fd*: int32
|
||||||
ssi_band*: uint32
|
ssi_tid*: uint32
|
||||||
ssi_overrun*: uint32
|
ssi_band*: uint32
|
||||||
ssi_trapno*: uint32
|
ssi_overrun*: uint32
|
||||||
ssi_status*: int32
|
ssi_trapno*: uint32
|
||||||
ssi_int*: int32
|
ssi_status*: int32
|
||||||
ssi_ptr*: uint64
|
ssi_int*: int32
|
||||||
ssi_utime*: uint64
|
ssi_ptr*: uint64
|
||||||
ssi_stime*: uint64
|
ssi_utime*: uint64
|
||||||
ssi_addr*: uint64
|
ssi_stime*: uint64
|
||||||
pad* {.importc: "__pad".}: array[0..47, uint8]
|
ssi_addr*: uint64
|
||||||
|
pad* {.importc: "__pad".}: array[0..47, uint8]
|
||||||
|
|
||||||
|
type
|
||||||
eventFdData {.importc: "eventfd_t",
|
eventFdData {.importc: "eventfd_t",
|
||||||
header: "<sys/eventfd.h>", pure, final.} = uint64
|
header: "<sys/eventfd.h>", pure, final.} = uint64
|
||||||
epoll_data {.importc: "union epoll_data", header: "<sys/epoll.h>",
|
epoll_data {.importc: "union epoll_data", header: "<sys/epoll.h>",
|
||||||
|
|
@ -68,12 +70,22 @@ proc timerfd_create(clock_id: ClockId, flags: cint): cint
|
||||||
proc timerfd_settime(ufd: cint, flags: cint,
|
proc timerfd_settime(ufd: cint, flags: cint,
|
||||||
utmr: var Itimerspec, otmr: var Itimerspec): cint
|
utmr: var Itimerspec, otmr: var Itimerspec): cint
|
||||||
{.cdecl, importc: "timerfd_settime", header: "<sys/timerfd.h>".}
|
{.cdecl, importc: "timerfd_settime", header: "<sys/timerfd.h>".}
|
||||||
proc signalfd(fd: cint, mask: var Sigset, flags: cint): cint
|
|
||||||
{.cdecl, importc: "signalfd", header: "<sys/signalfd.h>".}
|
|
||||||
proc eventfd(count: cuint, flags: cint): cint
|
proc eventfd(count: cuint, flags: cint): cint
|
||||||
{.cdecl, importc: "eventfd", header: "<sys/eventfd.h>".}
|
{.cdecl, importc: "eventfd", header: "<sys/eventfd.h>".}
|
||||||
proc ulimit(cmd: cint): clong
|
|
||||||
{.importc: "ulimit", header: "<ulimit.h>", varargs.}
|
when not defined(android):
|
||||||
|
proc signalfd(fd: cint, mask: var Sigset, flags: cint): cint
|
||||||
|
{.cdecl, importc: "signalfd", header: "<sys/signalfd.h>".}
|
||||||
|
|
||||||
|
var RLIMIT_NOFILE {.importc: "RLIMIT_NOFILE",
|
||||||
|
header: "<sys/resource.h>".}: cint
|
||||||
|
type
|
||||||
|
rlimit {.importc: "struct rlimit",
|
||||||
|
header: "<sys/resource.h>", pure, final.} = object
|
||||||
|
rlim_cur: int
|
||||||
|
rlim_max: int
|
||||||
|
proc getrlimit(resource: cint, rlp: var rlimit): cint
|
||||||
|
{.importc: "getrlimit",header: "<sys/resource.h>".}
|
||||||
|
|
||||||
when hasThreadSupport:
|
when hasThreadSupport:
|
||||||
type
|
type
|
||||||
|
|
@ -97,7 +109,10 @@ type
|
||||||
SelectEvent* = ptr SelectEventImpl
|
SelectEvent* = ptr SelectEventImpl
|
||||||
|
|
||||||
proc newSelector*[T](): Selector[T] =
|
proc newSelector*[T](): Selector[T] =
|
||||||
var maxFD = int(ulimit(4, 0))
|
var a = rlimit()
|
||||||
|
if getrlimit(RLIMIT_NOFILE, a) != 0:
|
||||||
|
raiseOsError(osLastError())
|
||||||
|
var maxFD = int(a.rlim_max)
|
||||||
doAssert(maxFD > 0)
|
doAssert(maxFD > 0)
|
||||||
|
|
||||||
var epollFD = epoll_create(MAX_EPOLL_RESULT_EVENTS)
|
var epollFD = epoll_create(MAX_EPOLL_RESULT_EVENTS)
|
||||||
|
|
@ -194,39 +209,53 @@ proc unregister*[T](s: Selector[T], fd: int|SocketHandle) =
|
||||||
doAssert(pkey.ident != 0)
|
doAssert(pkey.ident != 0)
|
||||||
|
|
||||||
if pkey.events != {}:
|
if pkey.events != {}:
|
||||||
if pkey.events * {Event.Read, Event.Write} != {}:
|
when not defined(android):
|
||||||
var epv = epoll_event()
|
if pkey.events * {Event.Read, Event.Write} != {}:
|
||||||
if epoll_ctl(s.epollFD, EPOLL_CTL_DEL, fdi.cint, addr epv) == -1:
|
var epv = epoll_event()
|
||||||
raiseOSError(osLastError())
|
if epoll_ctl(s.epollFD, EPOLL_CTL_DEL, fdi.cint, addr epv) == -1:
|
||||||
dec(s.count)
|
raiseOSError(osLastError())
|
||||||
elif Event.Timer in pkey.events:
|
dec(s.count)
|
||||||
var epv = epoll_event()
|
elif Event.Timer in pkey.events:
|
||||||
if epoll_ctl(s.epollFD, EPOLL_CTL_DEL, fdi.cint, addr epv) == -1:
|
var epv = epoll_event()
|
||||||
raiseOSError(osLastError())
|
if epoll_ctl(s.epollFD, EPOLL_CTL_DEL, fdi.cint, addr epv) == -1:
|
||||||
discard posix.close(fdi.cint)
|
raiseOSError(osLastError())
|
||||||
dec(s.count)
|
discard posix.close(fdi.cint)
|
||||||
elif Event.Signal in pkey.events:
|
dec(s.count)
|
||||||
var epv = epoll_event()
|
elif Event.Signal in pkey.events:
|
||||||
if epoll_ctl(s.epollFD, EPOLL_CTL_DEL, fdi.cint, addr epv) == -1:
|
var epv = epoll_event()
|
||||||
raiseOSError(osLastError())
|
if epoll_ctl(s.epollFD, EPOLL_CTL_DEL, fdi.cint, addr epv) == -1:
|
||||||
var nmask, omask: Sigset
|
raiseOSError(osLastError())
|
||||||
discard sigemptyset(nmask)
|
var nmask, omask: Sigset
|
||||||
discard sigemptyset(omask)
|
discard sigemptyset(nmask)
|
||||||
discard sigaddset(nmask, cint(s.fds[fdi].param))
|
discard sigemptyset(omask)
|
||||||
unblockSignals(nmask, omask)
|
discard sigaddset(nmask, cint(s.fds[fdi].param))
|
||||||
discard posix.close(fdi.cint)
|
unblockSignals(nmask, omask)
|
||||||
dec(s.count)
|
discard posix.close(fdi.cint)
|
||||||
elif Event.Process in pkey.events:
|
dec(s.count)
|
||||||
var epv = epoll_event()
|
elif Event.Process in pkey.events:
|
||||||
if epoll_ctl(s.epollFD, EPOLL_CTL_DEL, fdi.cint, addr epv) == -1:
|
var epv = epoll_event()
|
||||||
raiseOSError(osLastError())
|
if epoll_ctl(s.epollFD, EPOLL_CTL_DEL, fdi.cint, addr epv) == -1:
|
||||||
var nmask, omask: Sigset
|
raiseOSError(osLastError())
|
||||||
discard sigemptyset(nmask)
|
var nmask, omask: Sigset
|
||||||
discard sigemptyset(omask)
|
discard sigemptyset(nmask)
|
||||||
discard sigaddset(nmask, SIGCHLD)
|
discard sigemptyset(omask)
|
||||||
unblockSignals(nmask, omask)
|
discard sigaddset(nmask, SIGCHLD)
|
||||||
discard posix.close(fdi.cint)
|
unblockSignals(nmask, omask)
|
||||||
dec(s.count)
|
discard posix.close(fdi.cint)
|
||||||
|
dec(s.count)
|
||||||
|
else:
|
||||||
|
if pkey.events * {Event.Read, Event.Write} != {}:
|
||||||
|
var epv = epoll_event()
|
||||||
|
if epoll_ctl(s.epollFD, EPOLL_CTL_DEL, fdi.cint, addr epv) == -1:
|
||||||
|
raiseOSError(osLastError())
|
||||||
|
dec(s.count)
|
||||||
|
elif Event.Timer in pkey.events:
|
||||||
|
var epv = epoll_event()
|
||||||
|
if epoll_ctl(s.epollFD, EPOLL_CTL_DEL, fdi.cint, addr epv) == -1:
|
||||||
|
raiseOSError(osLastError())
|
||||||
|
discard posix.close(fdi.cint)
|
||||||
|
dec(s.count)
|
||||||
|
|
||||||
pkey.ident = 0
|
pkey.ident = 0
|
||||||
pkey.events = {}
|
pkey.events = {}
|
||||||
|
|
||||||
|
|
@ -280,60 +309,61 @@ proc registerTimer*[T](s: Selector[T], timeout: int, oneshot: bool,
|
||||||
inc(s.count)
|
inc(s.count)
|
||||||
result = fdi
|
result = fdi
|
||||||
|
|
||||||
proc registerSignal*[T](s: Selector[T], signal: int,
|
when not defined(android):
|
||||||
data: T): int {.discardable.} =
|
proc registerSignal*[T](s: Selector[T], signal: int,
|
||||||
var
|
data: T): int {.discardable.} =
|
||||||
nmask: Sigset
|
var
|
||||||
omask: Sigset
|
nmask: Sigset
|
||||||
|
omask: Sigset
|
||||||
|
|
||||||
discard sigemptyset(nmask)
|
discard sigemptyset(nmask)
|
||||||
discard sigemptyset(omask)
|
discard sigemptyset(omask)
|
||||||
discard sigaddset(nmask, cint(signal))
|
discard sigaddset(nmask, cint(signal))
|
||||||
blockSignals(nmask, omask)
|
blockSignals(nmask, omask)
|
||||||
|
|
||||||
let fdi = signalfd(-1, nmask, 0).int
|
let fdi = signalfd(-1, nmask, 0).int
|
||||||
if fdi == -1:
|
if fdi == -1:
|
||||||
raiseOSError(osLastError())
|
raiseOSError(osLastError())
|
||||||
setNonBlocking(fdi.cint)
|
setNonBlocking(fdi.cint)
|
||||||
|
|
||||||
s.checkFd(fdi)
|
s.checkFd(fdi)
|
||||||
doAssert(s.fds[fdi].ident == 0)
|
doAssert(s.fds[fdi].ident == 0)
|
||||||
|
|
||||||
var epv = epoll_event(events: EPOLLIN or EPOLLRDHUP)
|
var epv = epoll_event(events: EPOLLIN or EPOLLRDHUP)
|
||||||
epv.data.u64 = fdi.uint
|
epv.data.u64 = fdi.uint
|
||||||
if epoll_ctl(s.epollFD, EPOLL_CTL_ADD, fdi.cint, addr epv) == -1:
|
if epoll_ctl(s.epollFD, EPOLL_CTL_ADD, fdi.cint, addr epv) == -1:
|
||||||
raiseOSError(osLastError())
|
raiseOSError(osLastError())
|
||||||
s.setKey(fdi, signal, {Event.Signal}, signal, data)
|
s.setKey(fdi, signal, {Event.Signal}, signal, data)
|
||||||
inc(s.count)
|
inc(s.count)
|
||||||
result = fdi
|
result = fdi
|
||||||
|
|
||||||
proc registerProcess*[T](s: Selector, pid: int,
|
proc registerProcess*[T](s: Selector, pid: int,
|
||||||
data: T): int {.discardable.} =
|
data: T): int {.discardable.} =
|
||||||
var
|
var
|
||||||
nmask: Sigset
|
nmask: Sigset
|
||||||
omask: Sigset
|
omask: Sigset
|
||||||
|
|
||||||
discard sigemptyset(nmask)
|
discard sigemptyset(nmask)
|
||||||
discard sigemptyset(omask)
|
discard sigemptyset(omask)
|
||||||
discard sigaddset(nmask, posix.SIGCHLD)
|
discard sigaddset(nmask, posix.SIGCHLD)
|
||||||
blockSignals(nmask, omask)
|
blockSignals(nmask, omask)
|
||||||
|
|
||||||
let fdi = signalfd(-1, nmask, 0).int
|
let fdi = signalfd(-1, nmask, 0).int
|
||||||
if fdi == -1:
|
if fdi == -1:
|
||||||
raiseOSError(osLastError())
|
raiseOSError(osLastError())
|
||||||
setNonBlocking(fdi.cint)
|
setNonBlocking(fdi.cint)
|
||||||
|
|
||||||
s.checkFd(fdi)
|
s.checkFd(fdi)
|
||||||
doAssert(s.fds[fdi].ident == 0)
|
doAssert(s.fds[fdi].ident == 0)
|
||||||
|
|
||||||
var epv = epoll_event(events: EPOLLIN or EPOLLRDHUP)
|
var epv = epoll_event(events: EPOLLIN or EPOLLRDHUP)
|
||||||
epv.data.u64 = fdi.uint
|
epv.data.u64 = fdi.uint
|
||||||
epv.events = EPOLLIN or EPOLLRDHUP
|
epv.events = EPOLLIN or EPOLLRDHUP
|
||||||
if epoll_ctl(s.epollFD, EPOLL_CTL_ADD, fdi.cint, addr epv) == -1:
|
if epoll_ctl(s.epollFD, EPOLL_CTL_ADD, fdi.cint, addr epv) == -1:
|
||||||
raiseOSError(osLastError())
|
raiseOSError(osLastError())
|
||||||
s.setKey(fdi, pid, {Event.Process, Event.Oneshot}, pid, data)
|
s.setKey(fdi, pid, {Event.Process, Event.Oneshot}, pid, data)
|
||||||
inc(s.count)
|
inc(s.count)
|
||||||
result = fdi
|
result = fdi
|
||||||
|
|
||||||
proc registerEvent*[T](s: Selector[T], ev: SelectEvent, data: T) =
|
proc registerEvent*[T](s: Selector[T], ev: SelectEvent, data: T) =
|
||||||
let fdi = int(ev.efd)
|
let fdi = int(ev.efd)
|
||||||
|
|
@ -382,40 +412,60 @@ proc selectInto*[T](s: Selector[T], timeout: int,
|
||||||
events.incl(Event.Error)
|
events.incl(Event.Error)
|
||||||
if (pevents and EPOLLOUT) != 0:
|
if (pevents and EPOLLOUT) != 0:
|
||||||
events.incl(Event.Write)
|
events.incl(Event.Write)
|
||||||
if (pevents and EPOLLIN) != 0:
|
when not defined(android):
|
||||||
if Event.Read in skey.events:
|
if (pevents and EPOLLIN) != 0:
|
||||||
events.incl(Event.Read)
|
if Event.Read in skey.events:
|
||||||
elif Event.Timer in skey.events:
|
events.incl(Event.Read)
|
||||||
var data: uint64 = 0
|
elif Event.Timer in skey.events:
|
||||||
if posix.read(fdi.cint, addr data, sizeof(uint64)) != sizeof(uint64):
|
var data: uint64 = 0
|
||||||
raiseOSError(osLastError())
|
if posix.read(fdi.cint, addr data, sizeof(uint64)) != sizeof(uint64):
|
||||||
events = {Event.Timer}
|
raiseOSError(osLastError())
|
||||||
elif Event.Signal in skey.events:
|
events = {Event.Timer}
|
||||||
var data = SignalFdInfo()
|
elif Event.Signal in skey.events:
|
||||||
if posix.read(fdi.cint, addr data,
|
var data = SignalFdInfo()
|
||||||
sizeof(SignalFdInfo)) != sizeof(SignalFdInfo):
|
if posix.read(fdi.cint, addr data,
|
||||||
raiseOsError(osLastError())
|
sizeof(SignalFdInfo)) != sizeof(SignalFdInfo):
|
||||||
events = {Event.Signal}
|
raiseOsError(osLastError())
|
||||||
elif Event.Process in skey.events:
|
events = {Event.Signal}
|
||||||
var data = SignalFdInfo()
|
elif Event.Process in skey.events:
|
||||||
if posix.read(fdi.cint, addr data,
|
var data = SignalFdInfo()
|
||||||
sizeof(SignalFdInfo)) != sizeof(SignalFdInfo):
|
if posix.read(fdi.cint, addr data,
|
||||||
raiseOsError(osLastError())
|
sizeof(SignalFdInfo)) != sizeof(SignalFdInfo):
|
||||||
if cast[int](data.ssi_pid) == skey.param:
|
raiseOsError(osLastError())
|
||||||
events = {Event.Process}
|
if cast[int](data.ssi_pid) == skey.param:
|
||||||
else:
|
events = {Event.Process}
|
||||||
inc(i)
|
else:
|
||||||
continue
|
|
||||||
elif Event.User in skey.events:
|
|
||||||
var data: uint64 = 0
|
|
||||||
if posix.read(fdi.cint, addr data, sizeof(uint64)) != sizeof(uint64):
|
|
||||||
let err = osLastError()
|
|
||||||
if err == OSErrorCode(EAGAIN):
|
|
||||||
inc(i)
|
inc(i)
|
||||||
continue
|
continue
|
||||||
else:
|
elif Event.User in skey.events:
|
||||||
raiseOSError(err)
|
var data: uint64 = 0
|
||||||
events = {Event.User}
|
if posix.read(fdi.cint, addr data, sizeof(uint64)) != sizeof(uint64):
|
||||||
|
let err = osLastError()
|
||||||
|
if err == OSErrorCode(EAGAIN):
|
||||||
|
inc(i)
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
raiseOSError(err)
|
||||||
|
events = {Event.User}
|
||||||
|
else:
|
||||||
|
if (pevents and EPOLLIN) != 0:
|
||||||
|
if Event.Read in skey.events:
|
||||||
|
events.incl(Event.Read)
|
||||||
|
elif Event.Timer in skey.events:
|
||||||
|
var data: uint64 = 0
|
||||||
|
if posix.read(fdi.cint, addr data, sizeof(uint64)) != sizeof(uint64):
|
||||||
|
raiseOSError(osLastError())
|
||||||
|
events = {Event.Timer}
|
||||||
|
elif Event.User in skey.events:
|
||||||
|
var data: uint64 = 0
|
||||||
|
if posix.read(fdi.cint, addr data, sizeof(uint64)) != sizeof(uint64):
|
||||||
|
let err = osLastError()
|
||||||
|
if err == OSErrorCode(EAGAIN):
|
||||||
|
inc(i)
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
raiseOSError(err)
|
||||||
|
events = {Event.User}
|
||||||
|
|
||||||
skey.key.events = events
|
skey.key.events = events
|
||||||
results[k] = skey.key
|
results[k] = skey.key
|
||||||
|
|
|
||||||
|
|
@ -965,10 +965,16 @@ elif not defined(useNimRtl):
|
||||||
var ret : int
|
var ret : int
|
||||||
var status : cint = 1
|
var status : cint = 1
|
||||||
ret = waitpid(p.id, status, WNOHANG)
|
ret = waitpid(p.id, status, WNOHANG)
|
||||||
if WIFEXITED(status):
|
if ret == int(p.id):
|
||||||
p.exitStatus = status
|
if WIFEXITED(status):
|
||||||
if ret == 0: return true # Can't establish status. Assume running.
|
p.exitStatus = status
|
||||||
result = ret == int(p.id)
|
return false
|
||||||
|
else:
|
||||||
|
return true
|
||||||
|
elif ret == 0:
|
||||||
|
return true # Can't establish status. Assume running.
|
||||||
|
else:
|
||||||
|
return false
|
||||||
|
|
||||||
proc terminate(p: Process) =
|
proc terminate(p: Process) =
|
||||||
if kill(p.id, SIGTERM) != 0'i32:
|
if kill(p.id, SIGTERM) != 0'i32:
|
||||||
|
|
|
||||||
|
|
@ -76,7 +76,7 @@ to a variable (that was passed to the ``scanf`` macro) while ``$[]`` merely
|
||||||
optional tokens.
|
optional tokens.
|
||||||
|
|
||||||
|
|
||||||
In this example, we define a helper proc ``skipSep`` that skips some separators
|
In this example, we define a helper proc ``someSep`` that skips some separators
|
||||||
which we then use in our scanf pattern to help us in the matching process:
|
which we then use in our scanf pattern to help us in the matching process:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
|
@ -86,14 +86,14 @@ which we then use in our scanf pattern to help us in the matching process:
|
||||||
result = 0
|
result = 0
|
||||||
while input[start+result] in seps: inc result
|
while input[start+result] in seps: inc result
|
||||||
|
|
||||||
if scanf(input, "$w${someSep}$w", key, value):
|
if scanf(input, "$w$[someSep]$w", key, value):
|
||||||
...
|
...
|
||||||
|
|
||||||
It also possible to pass arguments to a user definable matcher:
|
It also possible to pass arguments to a user definable matcher:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
|
||||||
proc ndigits(input: string; start: int; intVal: var int; n: int): int =
|
proc ndigits(input: string; intVal: var int; start: int; n: int): int =
|
||||||
# matches exactly ``n`` digits. Matchers need to return 0 if nothing
|
# matches exactly ``n`` digits. Matchers need to return 0 if nothing
|
||||||
# matched or otherwise the number of processed chars.
|
# matched or otherwise the number of processed chars.
|
||||||
var x = 0
|
var x = 0
|
||||||
|
|
|
||||||
|
|
@ -436,6 +436,11 @@ when not defined(JS):
|
||||||
TimeInfoPtr = ptr StructTM
|
TimeInfoPtr = ptr StructTM
|
||||||
Clock {.importc: "clock_t".} = distinct int
|
Clock {.importc: "clock_t".} = distinct int
|
||||||
|
|
||||||
|
when not defined(windows):
|
||||||
|
# This is not ANSI C, but common enough
|
||||||
|
proc timegm(t: StructTM): Time {.
|
||||||
|
importc: "timegm", header: "<time.h>", tags: [].}
|
||||||
|
|
||||||
proc localtime(timer: ptr Time): TimeInfoPtr {.
|
proc localtime(timer: ptr Time): TimeInfoPtr {.
|
||||||
importc: "localtime", header: "<time.h>", tags: [].}
|
importc: "localtime", header: "<time.h>", tags: [].}
|
||||||
proc gmtime(timer: ptr Time): TimeInfoPtr {.
|
proc gmtime(timer: ptr Time): TimeInfoPtr {.
|
||||||
|
|
@ -515,20 +520,22 @@ when not defined(JS):
|
||||||
# the conversion is not expensive
|
# the conversion is not expensive
|
||||||
|
|
||||||
proc timeInfoToTime(timeInfo: TimeInfo): Time =
|
proc timeInfoToTime(timeInfo: TimeInfo): Time =
|
||||||
var cTimeInfo = timeInfo # for C++ we have to make a copy,
|
toTime(timeInfo)
|
||||||
# because the header of mktime is broken in my version of libc
|
|
||||||
result = mktime(timeInfoToTM(cTimeInfo))
|
|
||||||
# mktime is defined to interpret the input as local time. As timeInfoToTM
|
|
||||||
# does ignore the timezone, we need to adjust this here.
|
|
||||||
result = Time(TimeImpl(result) - getTimezone() + timeInfo.timezone)
|
|
||||||
|
|
||||||
proc toTime(timeInfo: TimeInfo): Time =
|
proc toTime(timeInfo: TimeInfo): Time =
|
||||||
var cTimeInfo = timeInfo # for C++ we have to make a copy,
|
var cTimeInfo = timeInfo # for C++ we have to make a copy
|
||||||
# because the header of mktime is broken in my version of libc
|
# because the header of mktime is broken in my version of libc
|
||||||
result = mktime(timeInfoToTM(cTimeInfo))
|
|
||||||
# mktime is defined to interpret the input as local time. As timeInfoToTM
|
when defined(windows):
|
||||||
# does ignore the timezone, we need to adjust this here.
|
# On Windows `mktime` is broken enough to make this work.
|
||||||
result = Time(TimeImpl(result) - getTimezone() + timeInfo.timezone)
|
result = mktime(timeInfoToTM(cTimeInfo))
|
||||||
|
# mktime is defined to interpret the input as local time. As timeInfoToTM
|
||||||
|
# does ignore the timezone, we need to adjust this here.
|
||||||
|
result = Time(TimeImpl(result) - getTimezone() + timeInfo.timezone)
|
||||||
|
else:
|
||||||
|
result = timegm(timeInfoToTM(cTimeInfo))
|
||||||
|
# As timeInfoToTM does ignore the timezone, we need to adjust this here.
|
||||||
|
result = Time(TimeImpl(result) + timeInfo.timezone)
|
||||||
|
|
||||||
const
|
const
|
||||||
epochDiff = 116444736000000000'i64
|
epochDiff = 116444736000000000'i64
|
||||||
|
|
|
||||||
|
|
@ -2595,6 +2595,14 @@ else:
|
||||||
if x < 0: -x else: x
|
if x < 0: -x else: x
|
||||||
{.pop.}
|
{.pop.}
|
||||||
|
|
||||||
|
type
|
||||||
|
FileSeekPos* = enum ## Position relative to which seek should happen
|
||||||
|
# The values are ordered so that they match with stdio
|
||||||
|
# SEEK_SET, SEEK_CUR and SEEK_END respectively.
|
||||||
|
fspSet ## Seek to absolute value
|
||||||
|
fspCur ## Seek relative to current position
|
||||||
|
fspEnd ## Seek relative to end
|
||||||
|
|
||||||
when not defined(JS): #and not defined(nimscript):
|
when not defined(JS): #and not defined(nimscript):
|
||||||
{.push stack_trace: off, profiler:off.}
|
{.push stack_trace: off, profiler:off.}
|
||||||
|
|
||||||
|
|
@ -2858,7 +2866,7 @@ when not defined(JS): #and not defined(nimscript):
|
||||||
## file `f`. Returns the number of actual written bytes, which may be less
|
## file `f`. Returns the number of actual written bytes, which may be less
|
||||||
## than `len` in case of an error.
|
## than `len` in case of an error.
|
||||||
|
|
||||||
proc setFilePos*(f: File, pos: int64) {.benign.}
|
proc setFilePos*(f: File, pos: int64, relativeTo: FileSeekPos = fspSet) {.benign.}
|
||||||
## sets the position of the file pointer that is used for read/write
|
## sets the position of the file pointer that is used for read/write
|
||||||
## operations. The file's first byte has the index zero.
|
## operations. The file's first byte has the index zero.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ const
|
||||||
alwaysCycleGC = defined(smokeCycles)
|
alwaysCycleGC = defined(smokeCycles)
|
||||||
alwaysGC = defined(fulldebug) # collect after every memory
|
alwaysGC = defined(fulldebug) # collect after every memory
|
||||||
# allocation (for debugging)
|
# allocation (for debugging)
|
||||||
leakDetector = false
|
leakDetector = defined(leakDetector)
|
||||||
overwriteFree = defined(nimBurnFree) # overwrite memory with 0xFF before free
|
overwriteFree = defined(nimBurnFree) # overwrite memory with 0xFF before free
|
||||||
trackAllocationSource = leakDetector
|
trackAllocationSource = leakDetector
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -336,8 +336,8 @@ proc open(f: var File, filehandle: FileHandle, mode: FileMode): bool =
|
||||||
f = c_fdopen(filehandle, FormatOpen[mode])
|
f = c_fdopen(filehandle, FormatOpen[mode])
|
||||||
result = f != nil
|
result = f != nil
|
||||||
|
|
||||||
proc setFilePos(f: File, pos: int64) =
|
proc setFilePos(f: File, pos: int64, relativeTo: FileSeekPos = fspSet) =
|
||||||
if c_fseek(f, clong(pos), 0) != 0:
|
if c_fseek(f, clong(pos), cint(relativeTo)) != 0:
|
||||||
raiseEIO("cannot set file position")
|
raiseEIO("cannot set file position")
|
||||||
|
|
||||||
proc getFilePos(f: File): int64 =
|
proc getFilePos(f: File): int64 =
|
||||||
|
|
|
||||||
|
|
@ -1092,11 +1092,6 @@ else:
|
||||||
import ioselectors
|
import ioselectors
|
||||||
from posix import EINTR, EAGAIN, EINPROGRESS, EWOULDBLOCK, MSG_PEEK,
|
from posix import EINTR, EAGAIN, EINPROGRESS, EWOULDBLOCK, MSG_PEEK,
|
||||||
MSG_NOSIGNAL
|
MSG_NOSIGNAL
|
||||||
|
|
||||||
const supportedPlatform = defined(linux) or defined(freebsd) or
|
|
||||||
defined(netbsd) or defined(openbsd) or
|
|
||||||
defined(macosx)
|
|
||||||
|
|
||||||
type
|
type
|
||||||
AsyncFD* = distinct cint
|
AsyncFD* = distinct cint
|
||||||
Callback = proc (fd: AsyncFD): bool {.closure,gcsafe.}
|
Callback = proc (fd: AsyncFD): bool {.closure,gcsafe.}
|
||||||
|
|
@ -1191,7 +1186,7 @@ else:
|
||||||
var keys: array[64, ReadyKey[AsyncData]]
|
var keys: array[64, ReadyKey[AsyncData]]
|
||||||
|
|
||||||
let p = getGlobalDispatcher()
|
let p = getGlobalDispatcher()
|
||||||
when supportedPlatform:
|
when ioselSupportedPlatform:
|
||||||
let customSet = {Event.Timer, Event.Signal, Event.Process,
|
let customSet = {Event.Timer, Event.Signal, Event.Process,
|
||||||
Event.Vnode, Event.User}
|
Event.Vnode, Event.User}
|
||||||
|
|
||||||
|
|
@ -1225,7 +1220,7 @@ else:
|
||||||
else:
|
else:
|
||||||
break
|
break
|
||||||
|
|
||||||
when supportedPlatform:
|
when ioselSupportedPlatform:
|
||||||
if (customSet * events) != {}:
|
if (customSet * events) != {}:
|
||||||
for node in keys[i].data.readCBs[].nodes():
|
for node in keys[i].data.readCBs[].nodes():
|
||||||
let cb = node.value
|
let cb = node.value
|
||||||
|
|
@ -1234,6 +1229,15 @@ else:
|
||||||
if cb(fd.AsyncFD):
|
if cb(fd.AsyncFD):
|
||||||
keys[i].data.readCBs[].remove(node)
|
keys[i].data.readCBs[].remove(node)
|
||||||
p.selector.unregister(fd)
|
p.selector.unregister(fd)
|
||||||
|
else:
|
||||||
|
if Event.User in events or events == {Event.Error}:
|
||||||
|
for node in keys[i].data.readCBs[].nodes():
|
||||||
|
let cb = node.value
|
||||||
|
custom = true
|
||||||
|
if cb != nil:
|
||||||
|
if cb(fd.AsyncFD):
|
||||||
|
keys[i].data.readCBs[].remove(node)
|
||||||
|
p.selector.unregister(fd)
|
||||||
|
|
||||||
# because state `data` can be modified in callback we need to update
|
# because state `data` can be modified in callback we need to update
|
||||||
# descriptor events with currently registered callbacks.
|
# descriptor events with currently registered callbacks.
|
||||||
|
|
@ -1496,7 +1500,7 @@ else:
|
||||||
addRead(socket, cb)
|
addRead(socket, cb)
|
||||||
return retFuture
|
return retFuture
|
||||||
|
|
||||||
when supportedPlatform:
|
when ioselSupportedPlatform:
|
||||||
|
|
||||||
proc addTimer*(timeout: int, oneshot: bool, cb: Callback) =
|
proc addTimer*(timeout: int, oneshot: bool, cb: Callback) =
|
||||||
## Start watching for timeout expiration, and then call the
|
## Start watching for timeout expiration, and then call the
|
||||||
|
|
|
||||||
|
|
@ -12,11 +12,10 @@ template processTest(t, x: untyped) =
|
||||||
if not x: echo(t & " FAILED\r\n")
|
if not x: echo(t & " FAILED\r\n")
|
||||||
|
|
||||||
when not defined(windows):
|
when not defined(windows):
|
||||||
import os, posix, osproc, nativesockets, times
|
import os, posix, nativesockets, times
|
||||||
|
|
||||||
const supportedPlatform = defined(macosx) or defined(freebsd) or
|
when ioselSupportedPlatform:
|
||||||
defined(netbsd) or defined(openbsd) or
|
import osproc
|
||||||
defined(linux)
|
|
||||||
|
|
||||||
proc socket_notification_test(): bool =
|
proc socket_notification_test(): bool =
|
||||||
proc create_test_socket(): SocketHandle =
|
proc create_test_socket(): SocketHandle =
|
||||||
|
|
@ -143,7 +142,7 @@ when not defined(windows):
|
||||||
selector.close()
|
selector.close()
|
||||||
result = true
|
result = true
|
||||||
|
|
||||||
when supportedPlatform:
|
when ioselSupportedPlatform:
|
||||||
proc timer_notification_test(): bool =
|
proc timer_notification_test(): bool =
|
||||||
var selector = newSelector[int]()
|
var selector = newSelector[int]()
|
||||||
var timer = selector.registerTimer(100, false, 0)
|
var timer = selector.registerTimer(100, false, 0)
|
||||||
|
|
@ -462,7 +461,7 @@ when not defined(windows):
|
||||||
when hasThreadSupport:
|
when hasThreadSupport:
|
||||||
processTest("Multithreaded user event notification test...",
|
processTest("Multithreaded user event notification test...",
|
||||||
mt_event_test())
|
mt_event_test())
|
||||||
when supportedPlatform:
|
when ioselSupportedPlatform:
|
||||||
processTest("Timer notification test...", timer_notification_test())
|
processTest("Timer notification test...", timer_notification_test())
|
||||||
processTest("Process notification test...", process_notification_test())
|
processTest("Process notification test...", process_notification_test())
|
||||||
processTest("Signal notification test...", signal_notification_test())
|
processTest("Signal notification test...", signal_notification_test())
|
||||||
|
|
|
||||||
|
|
@ -8,11 +8,12 @@ OK
|
||||||
"""
|
"""
|
||||||
|
|
||||||
when defined(upcoming):
|
when defined(upcoming):
|
||||||
import asyncdispatch, times, osproc, streams
|
import asyncdispatch, times, streams, posix
|
||||||
|
from ioselectors import ioselSupportedPlatform
|
||||||
|
|
||||||
const supportedPlatform = defined(linux) or defined(freebsd) or
|
proc delayedSet(ev: AsyncEvent, timeout: int): Future[void] {.async.} =
|
||||||
defined(netbsd) or defined(openbsd) or
|
await sleepAsync(timeout)
|
||||||
defined(macosx)
|
ev.setEvent()
|
||||||
|
|
||||||
proc waitEvent(ev: AsyncEvent, closeEvent = false): Future[void] =
|
proc waitEvent(ev: AsyncEvent, closeEvent = false): Future[void] =
|
||||||
var retFuture = newFuture[void]("waitEvent")
|
var retFuture = newFuture[void]("waitEvent")
|
||||||
|
|
@ -25,56 +26,55 @@ when defined(upcoming):
|
||||||
addEvent(ev, cb)
|
addEvent(ev, cb)
|
||||||
return retFuture
|
return retFuture
|
||||||
|
|
||||||
proc waitTimer(timeout: int): Future[void] =
|
|
||||||
var retFuture = newFuture[void]("waitTimer")
|
|
||||||
proc cb(fd: AsyncFD): bool =
|
|
||||||
retFuture.complete()
|
|
||||||
addTimer(timeout, true, cb)
|
|
||||||
return retFuture
|
|
||||||
|
|
||||||
proc waitProcess(p: Process): Future[void] =
|
|
||||||
var retFuture = newFuture[void]("waitProcess")
|
|
||||||
proc cb(fd: AsyncFD): bool =
|
|
||||||
retFuture.complete()
|
|
||||||
addProcess(p.processID(), cb)
|
|
||||||
return retFuture
|
|
||||||
|
|
||||||
proc delayedSet(ev: AsyncEvent, timeout: int): Future[void] {.async.} =
|
|
||||||
await waitTimer(timeout)
|
|
||||||
ev.setEvent()
|
|
||||||
|
|
||||||
proc timerTest() =
|
|
||||||
waitFor(waitTimer(200))
|
|
||||||
echo "OK"
|
|
||||||
|
|
||||||
proc eventTest() =
|
proc eventTest() =
|
||||||
var event = newAsyncEvent()
|
var event = newAsyncEvent()
|
||||||
var fut = waitEvent(event)
|
var fut = waitEvent(event)
|
||||||
asyncCheck(delayedSet(event, 500))
|
asyncCheck(delayedSet(event, 500))
|
||||||
waitFor(fut or waitTimer(1000))
|
waitFor(fut or sleepAsync(1000))
|
||||||
if fut.finished:
|
if fut.finished:
|
||||||
echo "OK"
|
echo "OK"
|
||||||
else:
|
else:
|
||||||
echo "eventTest: Timeout expired before event received!"
|
echo "eventTest: Timeout expired before event received!"
|
||||||
|
|
||||||
proc processTest() =
|
when ioselSupportedPlatform or defined(windows):
|
||||||
when defined(windows):
|
|
||||||
var process = startProcess("ping.exe", "",
|
|
||||||
["127.0.0.1", "-n", "2", "-w", "100"], nil,
|
|
||||||
{poStdErrToStdOut, poUsePath, poInteractive,
|
|
||||||
poDemon})
|
|
||||||
else:
|
|
||||||
var process = startProcess("/bin/sleep", "", ["1"], nil,
|
|
||||||
{poStdErrToStdOut, poUsePath})
|
|
||||||
var fut = waitProcess(process)
|
|
||||||
waitFor(fut or waitTimer(2000))
|
|
||||||
if fut.finished and process.peekExitCode() == 0:
|
|
||||||
echo "OK"
|
|
||||||
else:
|
|
||||||
echo "processTest: Timeout expired before process exited!"
|
|
||||||
|
|
||||||
when supportedPlatform:
|
import osproc
|
||||||
import posix
|
|
||||||
|
proc waitTimer(timeout: int): Future[void] =
|
||||||
|
var retFuture = newFuture[void]("waitTimer")
|
||||||
|
proc cb(fd: AsyncFD): bool =
|
||||||
|
retFuture.complete()
|
||||||
|
addTimer(timeout, true, cb)
|
||||||
|
return retFuture
|
||||||
|
|
||||||
|
proc waitProcess(p: Process): Future[void] =
|
||||||
|
var retFuture = newFuture[void]("waitProcess")
|
||||||
|
proc cb(fd: AsyncFD): bool =
|
||||||
|
retFuture.complete()
|
||||||
|
addProcess(p.processID(), cb)
|
||||||
|
return retFuture
|
||||||
|
|
||||||
|
proc timerTest() =
|
||||||
|
waitFor(waitTimer(200))
|
||||||
|
echo "OK"
|
||||||
|
|
||||||
|
proc processTest() =
|
||||||
|
when defined(windows):
|
||||||
|
var process = startProcess("ping.exe", "",
|
||||||
|
["127.0.0.1", "-n", "2", "-w", "100"], nil,
|
||||||
|
{poStdErrToStdOut, poUsePath, poInteractive,
|
||||||
|
poDemon})
|
||||||
|
else:
|
||||||
|
var process = startProcess("/bin/sleep", "", ["1"], nil,
|
||||||
|
{poStdErrToStdOut, poUsePath})
|
||||||
|
var fut = waitProcess(process)
|
||||||
|
waitFor(fut or waitTimer(2000))
|
||||||
|
if fut.finished and process.peekExitCode() == 0:
|
||||||
|
echo "OK"
|
||||||
|
else:
|
||||||
|
echo "processTest: Timeout expired before process exited!"
|
||||||
|
|
||||||
|
when ioselSupportedPlatform:
|
||||||
|
|
||||||
proc waitSignal(signal: int): Future[void] =
|
proc waitSignal(signal: int): Future[void] =
|
||||||
var retFuture = newFuture[void]("waitSignal")
|
var retFuture = newFuture[void]("waitSignal")
|
||||||
|
|
@ -97,7 +97,7 @@ when defined(upcoming):
|
||||||
else:
|
else:
|
||||||
echo "signalTest: Timeout expired before signal received!"
|
echo "signalTest: Timeout expired before signal received!"
|
||||||
|
|
||||||
when supportedPlatform:
|
when ioselSupportedPlatform:
|
||||||
timerTest()
|
timerTest()
|
||||||
eventTest()
|
eventTest()
|
||||||
processTest()
|
processTest()
|
||||||
|
|
|
||||||
|
|
@ -16,3 +16,8 @@ var running = true
|
||||||
while running:
|
while running:
|
||||||
running = running(p)
|
running = running(p)
|
||||||
doAssert(waitForExit(p) == QuitFailure)
|
doAssert(waitForExit(p) == QuitFailure)
|
||||||
|
|
||||||
|
# make sure that first call to running() after process exit returns false
|
||||||
|
p = startProcess(filename, dir)
|
||||||
|
os.sleep(500)
|
||||||
|
doAssert(not running(p))
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue