Merge branch 'devel' of github.com:nim-lang/Nim into devel

This commit is contained in:
Andreas Rumpf 2016-12-17 14:22:58 +01:00
commit 9ac76b49c0
21 changed files with 440 additions and 277 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -14,6 +14,7 @@ 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
when not defined(android):
type type
SignalFdInfo* {.importc: "struct signalfd_siginfo", SignalFdInfo* {.importc: "struct signalfd_siginfo",
header: "<sys/signalfd.h>", pure, final.} = object header: "<sys/signalfd.h>", pure, final.} = object
@ -35,6 +36,7 @@ type
ssi_addr*: uint64 ssi_addr*: uint64
pad* {.importc: "__pad".}: array[0..47, uint8] 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,6 +209,7 @@ proc unregister*[T](s: Selector[T], fd: int|SocketHandle) =
doAssert(pkey.ident != 0) doAssert(pkey.ident != 0)
if pkey.events != {}: if pkey.events != {}:
when not defined(android):
if pkey.events * {Event.Read, Event.Write} != {}: if pkey.events * {Event.Read, Event.Write} != {}:
var epv = epoll_event() var epv = epoll_event()
if epoll_ctl(s.epollFD, EPOLL_CTL_DEL, fdi.cint, addr epv) == -1: if epoll_ctl(s.epollFD, EPOLL_CTL_DEL, fdi.cint, addr epv) == -1:
@ -227,6 +243,19 @@ proc unregister*[T](s: Selector[T], fd: int|SocketHandle) =
unblockSignals(nmask, omask) unblockSignals(nmask, omask)
discard posix.close(fdi.cint) discard posix.close(fdi.cint)
dec(s.count) 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,6 +309,7 @@ proc registerTimer*[T](s: Selector[T], timeout: int, oneshot: bool,
inc(s.count) inc(s.count)
result = fdi result = fdi
when not defined(android):
proc registerSignal*[T](s: Selector[T], signal: int, proc registerSignal*[T](s: Selector[T], signal: int,
data: T): int {.discardable.} = data: T): int {.discardable.} =
var var
@ -382,6 +412,7 @@ 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)
when not defined(android):
if (pevents and EPOLLIN) != 0: if (pevents and EPOLLIN) != 0:
if Event.Read in skey.events: if Event.Read in skey.events:
events.incl(Event.Read) events.incl(Event.Read)
@ -416,6 +447,25 @@ proc selectInto*[T](s: Selector[T], timeout: int,
else: else:
raiseOSError(err) raiseOSError(err)
events = {Event.User} 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

View file

@ -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 ret == int(p.id):
if WIFEXITED(status): if WIFEXITED(status):
p.exitStatus = status p.exitStatus = status
if ret == 0: return true # Can't establish status. Assume running. return false
result = ret == int(p.id) 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:

View file

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

View file

@ -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
when defined(windows):
# On Windows `mktime` is broken enough to make this work.
result = mktime(timeInfoToTM(cTimeInfo)) result = mktime(timeInfoToTM(cTimeInfo))
# mktime is defined to interpret the input as local time. As timeInfoToTM # mktime is defined to interpret the input as local time. As timeInfoToTM
# does ignore the timezone, we need to adjust this here. # does ignore the timezone, we need to adjust this here.
result = Time(TimeImpl(result) - getTimezone() + timeInfo.timezone) 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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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,6 +26,20 @@ when defined(upcoming):
addEvent(ev, cb) addEvent(ev, cb)
return retFuture return retFuture
proc eventTest() =
var event = newAsyncEvent()
var fut = waitEvent(event)
asyncCheck(delayedSet(event, 500))
waitFor(fut or sleepAsync(1000))
if fut.finished:
echo "OK"
else:
echo "eventTest: Timeout expired before event received!"
when ioselSupportedPlatform or defined(windows):
import osproc
proc waitTimer(timeout: int): Future[void] = proc waitTimer(timeout: int): Future[void] =
var retFuture = newFuture[void]("waitTimer") var retFuture = newFuture[void]("waitTimer")
proc cb(fd: AsyncFD): bool = proc cb(fd: AsyncFD): bool =
@ -39,24 +54,10 @@ when defined(upcoming):
addProcess(p.processID(), cb) addProcess(p.processID(), cb)
return retFuture return retFuture
proc delayedSet(ev: AsyncEvent, timeout: int): Future[void] {.async.} =
await waitTimer(timeout)
ev.setEvent()
proc timerTest() = proc timerTest() =
waitFor(waitTimer(200)) waitFor(waitTimer(200))
echo "OK" echo "OK"
proc eventTest() =
var event = newAsyncEvent()
var fut = waitEvent(event)
asyncCheck(delayedSet(event, 500))
waitFor(fut or waitTimer(1000))
if fut.finished:
echo "OK"
else:
echo "eventTest: Timeout expired before event received!"
proc processTest() = proc processTest() =
when defined(windows): when defined(windows):
var process = startProcess("ping.exe", "", var process = startProcess("ping.exe", "",
@ -73,8 +74,7 @@ when defined(upcoming):
else: else:
echo "processTest: Timeout expired before process exited!" echo "processTest: Timeout expired before process exited!"
when supportedPlatform: when ioselSupportedPlatform:
import posix
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()

View file

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