diff options
author | Charles Blake <charlechaud@gmail.com> | 2019-06-13 10:21:11 -0400 |
---|---|---|
committer | Charles Blake <charlechaud@gmail.com> | 2019-06-13 10:21:11 -0400 |
commit | e11d582fa1562128b74f597620958e40e849b855 (patch) | |
tree | d0be6597a78ff50d15666027f4f3811a22a98eb3 | |
parent | e7188cdfa84f1a09748b66e1edfcbd3245b9ac60 (diff) | |
parent | 06ef56ba72bbefc389263296e70617b70ffe09a6 (diff) | |
download | Nim-e11d582fa1562128b74f597620958e40e849b855.tar.gz |
Merge branch 'devel' of https://github.com/nim-lang/Nim into devel
-rw-r--r-- | appveyor.yml | 25 | ||||
-rw-r--r-- | compiler/layouter.nim | 6 | ||||
-rw-r--r-- | compiler/lexer.nim | 6 | ||||
-rw-r--r-- | compiler/nimconf.nim | 28 | ||||
-rw-r--r-- | compiler/parser.nim | 8 | ||||
-rw-r--r-- | doc/tut1.rst | 2 | ||||
-rw-r--r-- | lib/pure/asyncdispatch.nim | 117 | ||||
-rw-r--r-- | lib/pure/asyncfutures.nim | 12 | ||||
-rw-r--r-- | lib/pure/includes/oserr.nim | 15 | ||||
-rw-r--r-- | lib/system.nim | 7 | ||||
-rw-r--r-- | nimpretty/tests/exhaustive.nim | 23 | ||||
-rw-r--r-- | nimpretty/tests/expected/exhaustive.nim | 23 | ||||
-rw-r--r-- | nimsuggest/nimsuggest.nim | 4 | ||||
-rw-r--r-- | tests/async/tasyncclosestall.nim | 99 |
14 files changed, 277 insertions, 98 deletions
diff --git a/appveyor.yml b/appveyor.yml index 97be60951..45529cf00 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,14 +1,11 @@ version: '{build}' environment: + DLLS_URL: https://nim-lang.org/download/dlls.zip + DLLS_ARCHIVE: dlls.zip MINGW_DIR: mingw64 MINGW_URL: https://nim-lang.org/download/mingw64-6.3.0.7z MINGW_ARCHIVE: mingw64-6.3.0.7z - OPENBLAS_URL: https://sourceforge.net/projects/openblas/files/v0.3.5/OpenBLAS%200.3.5%20version.zip/download - OPENBLAS_ARCHIVE: OpenBLAS-0.3.5.zip - SQLITE_URL: http://www.sqlite.org/2017/sqlite-dll-win64-x64-3160200.zip - SQLITE_ARCHIVE: sqlite-dll-win64-x64-3160200.zip - platform: x64 matrix: - NIM_TEST_PACKAGES: false @@ -16,8 +13,7 @@ environment: cache: - '%MINGW_ARCHIVE%' - - '%SQLITE_ARCHIVE%' - - '%OPENBLAS_ARCHIVE%' + - '%DLLS_ARCHIVE%' matrix: #allow_failures: @@ -26,23 +22,14 @@ matrix: install: - ps: Install-Product node 8 # node 8 or later is required to test js async stuff - - MKDIR %CD%\DIST - - MKDIR %CD%\DIST\PCRE - - nuget install pcre -Verbosity quiet -Version 8.33.0.1 -OutputDirectory %CD%\DIST\PCRE - - IF not exist "%SQLITE_ARCHIVE%" appveyor DownloadFile "%SQLITE_URL%" -FileName "%SQLITE_ARCHIVE%" - - 7z x -y "%SQLITE_ARCHIVE%" -o"%CD%\DIST"> nul + - IF not exist "%DLLS_ARCHIVE%" appveyor DownloadFile "%DLLS_URL%" -FileName "%DLLS_ARCHIVE%" + - 7z x -y "%DLLS_ARCHIVE%" -o"%CD%\BIN"> nul - IF not exist "%MINGW_ARCHIVE%" appveyor DownloadFile "%MINGW_URL%" -FileName "%MINGW_ARCHIVE%" - 7z x -y "%MINGW_ARCHIVE%" -o"%CD%\DIST"> nul - - IF not exist "%OPENBLAS_ARCHIVE%" appveyor DownloadFile "%OPENBLAS_URL%" -FileName "%OPENBLAS_ARCHIVE%" - - 7z x -y "%OPENBLAS_ARCHIVE%" -o"%CD%\DIST"> nul - SET PATH=%CD%\DIST\%MINGW_DIR%\BIN;%CD%\BIN;%PATH% - - IF "%PLATFORM%" == "x64" ( copy C:\OpenSSL-Win64\libeay32.dll %CD%\BIN\libeay64.dll & copy C:\OpenSSL-Win64\libeay32.dll %CD%\BIN\libeay32.dll & copy C:\OpenSSL-Win64\libssl32.dll %CD%\BIN\libssl64.dll & copy C:\OpenSSL-Win64\libssl32.dll %CD%\BIN\libssl32.dll ) - ELSE ( copy C:\OpenSSL-Win32\libeay32.dll %CD%\BIN\libeay32.dll & copy C:\OpenSSL-Win32\libssl32.dll %CD%\BIN\libssl32.dll ) - - IF "%PLATFORM%" == "x64" ( copy %CD%\DIST\sqlite3.dll %CD%\BIN\sqlite3_64.dll ) ELSE ( copy %CD%\DIST\sqlite3.dll %CD%\BIN\sqlite3_32.dll ) - - IF "%PLATFORM%" == "x64" ( copy %CD%\DIST\PCRE\pcre.redist.8.33.0.1\build\native\bin\v100\x64\Release\dynamic\utf8\pcre8.dll %CD%\bin\pcre64.dll ) ELSE ( copy %CD%\DIST\PCRE\pcre.redist.8.33.0.1\build\native\bin\v100\Win32\Release\dynamic\utf8\pcre8.dll %CD%\bin\pcre32.dll ) - git clone --depth 1 https://github.com/nim-lang/csources - cd csources - - IF "%PLATFORM%" == "x64" ( build64.bat ) else ( build.bat ) + - build64.bat - cd .. build_script: diff --git a/compiler/layouter.nim b/compiler/layouter.nim index 3ac907f1d..057bad5ba 100644 --- a/compiler/layouter.nim +++ b/compiler/layouter.nim @@ -346,7 +346,7 @@ proc emitTok*(em: var Emitter; L: TLexer; tok: TToken) = if endsInAlpha(em): wrSpace em elif not em.inquote and not endsInWhite(em) and - em.lastTok notin openPars and not em.lastTokWasTerse: + em.lastTok notin (openPars+{tkOpr, tkDotDot}) and not em.lastTokWasTerse: #and tok.tokType in oprSet wrSpace em @@ -392,8 +392,8 @@ proc emitTok*(em: var Emitter; L: TLexer; tok: TToken) = wr(em, TokTypeToStr[tok.tokType], ltOther) if not em.inquote: wrSpace(em) of tkOpr, tkDotDot: - if ((tok.strongSpaceA == 0 and tok.strongSpaceB == 0) or em.inquote) and - tok.ident.s notin ["<", ">", "<=", ">=", "==", "!="]: + if em.inquote or ((tok.strongSpaceA == 0 and tok.strongSpaceB == 0) and + tok.ident.s notin ["<", ">", "<=", ">=", "==", "!="]): # bug #9504: remember to not spacify a keyword: lastTokWasTerse = true # if not surrounded by whitespace, don't produce any whitespace either: diff --git a/compiler/lexer.nim b/compiler/lexer.nim index 421a83c19..97fa91b3a 100644 --- a/compiler/lexer.nim +++ b/compiler/lexer.nim @@ -173,7 +173,7 @@ proc isNimIdentifier*(s: string): bool = inc(i) result = true -proc tokToStr*(tok: TToken): string = +proc `$`*(tok: TToken): string = case tok.tokType of tkIntLit..tkInt64Lit: result = $tok.iNumber of tkFloatLit..tkFloat64Lit: result = $tok.fNumber @@ -188,11 +188,11 @@ proc tokToStr*(tok: TToken): string = proc prettyTok*(tok: TToken): string = if isKeyword(tok.tokType): result = "keyword " & tok.ident.s - else: result = tokToStr(tok) + else: result = $tok proc printTok*(conf: ConfigRef; tok: TToken) = msgWriteln(conf, $tok.line & ":" & $tok.col & "\t" & - TokTypeToStr[tok.tokType] & " " & tokToStr(tok)) + TokTypeToStr[tok.tokType] & " " & $tok) proc initToken*(L: var TToken) = L.tokType = tkInvalid diff --git a/compiler/nimconf.nim b/compiler/nimconf.nim index 02d1e7d2a..f518b3abf 100644 --- a/compiler/nimconf.nim +++ b/compiler/nimconf.nim @@ -123,31 +123,31 @@ proc parseDirective(L: var TLexer, tok: var TToken; config: ConfigRef; condStack of wEnd: doEnd(L, tok, condStack) of wWrite: ppGetTok(L, tok) - msgs.msgWriteln(config, strtabs.`%`(tokToStr(tok), config.configVars, + msgs.msgWriteln(config, strtabs.`%`($tok, config.configVars, {useEnvironment, useKey})) ppGetTok(L, tok) else: case tok.ident.s.normalize of "putenv": ppGetTok(L, tok) - var key = tokToStr(tok) + var key = $tok ppGetTok(L, tok) - os.putEnv(key, tokToStr(tok)) + os.putEnv(key, $tok) ppGetTok(L, tok) of "prependenv": ppGetTok(L, tok) - var key = tokToStr(tok) + var key = $tok ppGetTok(L, tok) - os.putEnv(key, tokToStr(tok) & os.getEnv(key)) + os.putEnv(key, $tok & os.getEnv(key)) ppGetTok(L, tok) of "appendenv": ppGetTok(L, tok) - var key = tokToStr(tok) + var key = $tok ppGetTok(L, tok) - os.putEnv(key, os.getEnv(key) & tokToStr(tok)) + os.putEnv(key, os.getEnv(key) & $tok) ppGetTok(L, tok) else: - lexMessage(L, errGenerated, "invalid directive: '$1'" % tokToStr(tok)) + lexMessage(L, errGenerated, "invalid directive: '$1'" % $tok) proc confTok(L: var TLexer, tok: var TToken; config: ConfigRef; condStack: var seq[bool]) = ppGetTok(L, tok) @@ -156,7 +156,7 @@ proc confTok(L: var TLexer, tok: var TToken; config: ConfigRef; condStack: var s proc checkSymbol(L: TLexer, tok: TToken) = if tok.tokType notin {tkSymbol..tkInt64Lit, tkStrLit..tkTripleStrLit}: - lexMessage(L, errGenerated, "expected identifier, but got: " & tokToStr(tok)) + lexMessage(L, errGenerated, "expected identifier, but got: " & $tok) proc parseAssignment(L: var TLexer, tok: var TToken; config: ConfigRef; condStack: var seq[bool]) = @@ -164,21 +164,21 @@ proc parseAssignment(L: var TLexer, tok: var TToken; confTok(L, tok, config, condStack) # skip unnecessary prefix var info = getLineInfo(L, tok) # save for later in case of an error checkSymbol(L, tok) - var s = tokToStr(tok) + var s = $tok confTok(L, tok, config, condStack) # skip symbol var val = "" while tok.tokType == tkDot: add(s, '.') confTok(L, tok, config, condStack) checkSymbol(L, tok) - add(s, tokToStr(tok)) + add(s, $tok) confTok(L, tok, config, condStack) if tok.tokType == tkBracketLe: # BUGFIX: val, not s! confTok(L, tok, config, condStack) checkSymbol(L, tok) add(val, '[') - add(val, tokToStr(tok)) + add(val, $tok) confTok(L, tok, config, condStack) if tok.tokType == tkBracketRi: confTok(L, tok, config, condStack) else: lexMessage(L, errGenerated, "expected closing ']'") @@ -188,12 +188,12 @@ proc parseAssignment(L: var TLexer, tok: var TToken; if len(val) > 0: add(val, ':') confTok(L, tok, config, condStack) # skip ':' or '=' or '%' checkSymbol(L, tok) - add(val, tokToStr(tok)) + add(val, $tok) confTok(L, tok, config, condStack) # skip symbol while tok.ident != nil and tok.ident.s == "&": confTok(L, tok, config, condStack) checkSymbol(L, tok) - add(val, tokToStr(tok)) + add(val, $tok) confTok(L, tok, config, condStack) if percent: processSwitch(s, strtabs.`%`(val, config.configVars, diff --git a/compiler/parser.nim b/compiler/parser.nim index eda75f926..65fb2c5a3 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -343,13 +343,13 @@ proc parseSymbol(p: var TParser, mode = smNormal): PNode = var accm = "" while p.tok.tokType in {tkOpr, tkDot, tkDotDot, tkEquals, tkParLe..tkParDotRi}: - accm.add(tokToStr(p.tok)) + accm.add($p.tok) getTok(p) let node = newNodeI(nkIdent, lineinfo) node.ident = p.lex.cache.getIdent(accm) result.add(node) of tokKeywordLow..tokKeywordHigh, tkSymbol, tkIntLit..tkCharLit: - result.add(newIdentNodeP(p.lex.cache.getIdent(tokToStr(p.tok)), p)) + result.add(newIdentNodeP(p.lex.cache.getIdent($p.tok), p)) getTok(p) else: parMessage(p, errIdentifierExpected, p.tok) @@ -903,6 +903,8 @@ proc parsePragma(p: var TParser): PNode = #| pragma = '{.' optInd (exprColonExpr comma?)* optPar ('.}' | '}') result = newNodeP(nkPragma, p) inc p.inPragma + when defined(nimpretty): + inc p.em.keepIndents getTok(p) optInd(p, result) while p.tok.tokType notin {tkCurlyDotRi, tkCurlyRi, tkEof}: @@ -921,6 +923,8 @@ proc parsePragma(p: var TParser): PNode = else: parMessage(p, "expected '.}'") dec p.inPragma + when defined(nimpretty): + dec p.em.keepIndents proc identVis(p: var TParser; allowDot=false): PNode = #| identVis = symbol opr? # postfix position diff --git a/doc/tut1.rst b/doc/tut1.rst index 161b4b904..ff53d3f37 100644 --- a/doc/tut1.rst +++ b/doc/tut1.rst @@ -56,7 +56,7 @@ To compile a release version use:: nim c -d:release greetings.nim By default the Nim compiler generates a large amount of runtime checks -aiming for your debugging pleasure. With ``-d:release`` these checks are +aiming for your debugging pleasure. With ``-d:release`` some checks are `turned off and optimizations are turned on <nimc.html#compiler-usage-compile-time-symbols>`_. diff --git a/lib/pure/asyncdispatch.nim b/lib/pure/asyncdispatch.nim index ea35a444d..382d9d44a 100644 --- a/lib/pure/asyncdispatch.nim +++ b/lib/pure/asyncdispatch.nim @@ -568,7 +568,7 @@ when defined(windows) or defined(nimdoc): if flags.isDisconnectionError(errcode): retFuture.complete() else: - retFuture.fail(newException(OSError, osErrorMsg(errcode))) + retFuture.fail(newOSError(errcode)) ) let ret = WSASend(socket.SocketHandle, addr dataBuf, 1, addr bytesReceived, @@ -1134,11 +1134,6 @@ else: var data = newAsyncData() p.selector.registerHandle(fd.SocketHandle, {}, data) - proc closeSocket*(sock: AsyncFD) = - let disp = getGlobalDispatcher() - disp.selector.unregister(sock.SocketHandle) - sock.SocketHandle.close() - proc unregister*(fd: AsyncFD) = getGlobalDispatcher().selector.unregister(fd.SocketHandle) @@ -1174,7 +1169,9 @@ else: let p = getGlobalDispatcher() not p.selector.isEmpty() or p.timers.len != 0 or p.callbacks.len != 0 - template processBasicCallbacks(ident, rwlist: untyped) = + proc processBasicCallbacks( + fd: AsyncFD, event: Event + ): tuple[readCbListCount, writeCbListCount: int] = # Process pending descriptor and AsyncEvent callbacks. # # Invoke every callback stored in `rwlist`, until one @@ -1187,32 +1184,46 @@ else: # or it can be possible to fall into endless cycle. var curList: seq[Callback] - withData(p.selector, ident, adata) do: - shallowCopy(curList, adata.rwlist) - adata.rwlist = newSeqOfCap[Callback](InitCallbackListSize) + let selector = getGlobalDispatcher().selector + withData(selector, fd.int, fdData): + case event + of Event.Read: + shallowCopy(curList, fdData.readList) + fdData.readList = newSeqOfCap[Callback](InitCallbackListSize) + of Event.Write: + shallowCopy(curList, fdData.writeList) + fdData.writeList = newSeqOfCap[Callback](InitCallbackListSize) + else: + assert false, "Cannot process callbacks for " & $event let newLength = max(len(curList), InitCallbackListSize) var newList = newSeqOfCap[Callback](newLength) for cb in curList: - if len(newList) > 0: - # A callback has already returned with EAGAIN, don't call any others - # until next `poll`. + if not cb(fd): + # Callback wants to be called again. newList.add(cb) + # This callback has returned with EAGAIN, so we don't need to + # call any other callbacks as they are all waiting for the same event + # on the same fd. + break + + withData(selector, fd.int, fdData) do: + # Descriptor is still present in the queue. + case event + of Event.Read: + fdData.readList = newList & fdData.readList + of Event.Write: + fdData.writeList = newList & fdData.writeList else: - if not cb(fd.AsyncFD): - # Callback wants to be called again. - newList.add(cb) + assert false, "Cannot process callbacks for " & $event - withData(p.selector, ident, adata) do: - # descriptor still present in queue. - adata.rwlist = newList & adata.rwlist - rLength = len(adata.readList) - wLength = len(adata.writeList) + result.readCbListCount = len(fdData.readList) + result.writeCbListCount = len(fdData.writeList) do: - # descriptor was unregistered in callback via `unregister()`. - rLength = -1 - wLength = -1 + # Descriptor was unregistered in callback via `unregister()`. + result.readCbListCount = -1 + result.writeCbListCount = -1 template processCustomCallbacks(ident: untyped) = # Process pending custom event callbacks. Custom events are @@ -1221,7 +1232,7 @@ else: # so there is no need to iterate over list. var curList: seq[Callback] - withData(p.selector, ident, adata) do: + withData(p.selector, ident.int, adata) do: shallowCopy(curList, adata.readList) adata.readList = newSeqOfCap[Callback](InitCallbackListSize) @@ -1232,16 +1243,33 @@ else: if not cb(fd.AsyncFD): newList.add(cb) - withData(p.selector, ident, adata) do: + withData(p.selector, ident.int, adata) do: # descriptor still present in queue. adata.readList = newList & adata.readList if len(adata.readList) == 0: # if no callbacks registered with descriptor, unregister it. - p.selector.unregister(fd) + p.selector.unregister(fd.int) do: # descriptor was unregistered in callback via `unregister()`. discard + proc closeSocket*(sock: AsyncFD) = + let selector = getGlobalDispatcher().selector + if sock.SocketHandle notin selector: + raise newException(ValueError, "File descriptor not registered.") + + let data = selector.getData(sock.SocketHandle) + sock.unregister() + sock.SocketHandle.close() + # We need to unblock the read and write callbacks which could still be + # waiting for the socket to become readable and/or writeable. + for cb in data.readList & data.writeList: + if not cb(sock): + raise newException( + ValueError, "Expecting async operations to stop when fd has closed." + ) + + proc runOnce(timeout = 500): bool = let p = getGlobalDispatcher() when ioselSupportedPlatform: @@ -1257,41 +1285,42 @@ else: let nextTimer = processTimers(p, result) var count = p.selector.selectInto(adjustTimeout(timeout, nextTimer), keys) for i in 0..<count: - var custom = false - let fd = keys[i].fd + let fd = keys[i].fd.AsyncFD let events = keys[i].events - var rLength = 0 # len(data.readList) after callback - var wLength = 0 # len(data.writeList) after callback + var (readCbListCount, writeCbListCount) = (0, 0) if Event.Read in events or events == {Event.Error}: - processBasicCallbacks(fd, readList) + (readCbListCount, writeCbListCount) = + processBasicCallbacks(fd, Event.Read) result = true if Event.Write in events or events == {Event.Error}: - processBasicCallbacks(fd, writeList) + (readCbListCount, writeCbListCount) = + processBasicCallbacks(fd, Event.Write) result = true + var isCustomEvent = false if Event.User in events: - processBasicCallbacks(fd, readList) - custom = true - if rLength == 0: - p.selector.unregister(fd) + (readCbListCount, writeCbListCount) = + processBasicCallbacks(fd, Event.Read) + isCustomEvent = true + if readCbListCount == 0: + p.selector.unregister(fd.int) result = true when ioselSupportedPlatform: if (customSet * events) != {}: - custom = true + isCustomEvent = true processCustomCallbacks(fd) result = true # because state `data` can be modified in callback we need to update # descriptor events with currently registered callbacks. - if not custom: + if not isCustomEvent and (readCbListCount != -1 and writeCbListCount != -1): var newEvents: set[Event] = {} - if rLength != -1 and wLength != -1: - if rLength > 0: incl(newEvents, Event.Read) - if wLength > 0: incl(newEvents, Event.Write) - p.selector.updateHandle(SocketHandle(fd), newEvents) + if readCbListCount > 0: incl(newEvents, Event.Read) + if writeCbListCount > 0: incl(newEvents, Event.Write) + p.selector.updateHandle(SocketHandle(fd), newEvents) # Timer processing. discard processTimers(p, result) @@ -1370,7 +1399,7 @@ else: if flags.isDisconnectionError(lastError): retFuture.complete() else: - retFuture.fail(newException(OSError, osErrorMsg(lastError))) + retFuture.fail(newOSError(lastError)) else: result = false # We still want this callback to be called. else: diff --git a/lib/pure/asyncfutures.nim b/lib/pure/asyncfutures.nim index 9af72f8b3..e86a34d81 100644 --- a/lib/pure/asyncfutures.nim +++ b/lib/pure/asyncfutures.nim @@ -393,11 +393,13 @@ proc asyncCheck*[T](future: Future[T]) = ## This should be used instead of ``discard`` to discard void futures, ## or use ``waitFor`` if you need to wait for the future's completion. assert(not future.isNil, "Future is nil") - future.callback = - proc () = - if future.failed: - injectStacktrace(future) - raise future.error + # TODO: We can likely look at the stack trace here and inject the location + # where the `asyncCheck` was called to give a better error stack message. + proc asyncCheckCallback() = + if future.failed: + injectStacktrace(future) + raise future.error + future.callback = asyncCheckCallback proc `and`*[T, Y](fut1: Future[T], fut2: Future[Y]): Future[void] = ## Returns a future which will complete once both ``fut1`` and ``fut2`` diff --git a/lib/pure/includes/oserr.nim b/lib/pure/includes/oserr.nim index 68ce5d95f..34d8d0085 100644 --- a/lib/pure/includes/oserr.nim +++ b/lib/pure/includes/oserr.nim @@ -57,8 +57,10 @@ proc osErrorMsg*(errorCode: OSErrorCode): string = if errorCode != OSErrorCode(0'i32): result = $c_strerror(errorCode.int32) -proc raiseOSError*(errorCode: OSErrorCode; additionalInfo = "") {.noinline.} = - ## Raises an `OSError exception <system.html#OSError>`_. +proc newOSError*( + errorCode: OSErrorCode, additionalInfo = "" +): owned(ref OSError) {.noinline.} = + ## Creates a new `OSError exception <system.html#OSError>`_. ## ## The ``errorCode`` will determine the ## message, `osErrorMsg proc <#osErrorMsg,OSErrorCode>`_ will be used @@ -82,7 +84,14 @@ proc raiseOSError*(errorCode: OSErrorCode; additionalInfo = "") {.noinline.} = e.msg.addQuoted additionalInfo if e.msg == "": e.msg = "unknown OS error" - raise e + return e + +proc raiseOSError*(errorCode: OSErrorCode, additionalInfo = "") {.noinline.} = + ## Raises an `OSError exception <system.html#OSError>`_. + ## + ## Read the description of the `newOSError proc <#newOSError,OSErrorCode,string>`_ to learn + ## how the exception object is created. + raise newOSError(errorCode, additionalInfo) {.push stackTrace:off.} proc osLastError*(): OSErrorCode {.sideEffect.} = diff --git a/lib/system.nim b/lib/system.nim index 65f04a4ca..bebc9e336 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -3776,8 +3776,11 @@ proc quit*(errormsg: string, errorcode = QuitFailure) {.noReturn.} = when defined(nimscript) or defined(js) or (hostOS == "standalone"): echo errormsg else: - cstderr.rawWrite(errormsg) - cstderr.rawWrite("\n") + when nimvm: + echo errormsg + else: + cstderr.rawWrite(errormsg) + cstderr.rawWrite("\n") quit(errorcode) {.pop.} # checks diff --git a/nimpretty/tests/exhaustive.nim b/nimpretty/tests/exhaustive.nim index 2f8fe03f2..8280420c5 100644 --- a/nimpretty/tests/exhaustive.nim +++ b/nimpretty/tests/exhaustive.nim @@ -409,3 +409,26 @@ type ccFastCall, # fastcall (pass parameters in registers) ccClosure, # proc has a closure ccNoConvention # needed for generating proper C procs sometimes + + +proc isValid1*[A](s: HashSet[A]): bool {.deprecated: + "Deprecated since v0.20; sets are initialized by default".} = + ## Returns `true` if the set has been initialized (with `initHashSet proc + ## <#initHashSet,int>`_ or `init proc <#init,HashSet[A],int>`_). + result = s.data.len > 0 + # bug #11468 + +assert $type(a) == "Option[system.int]" +foo(a, $type(b), c) +foo(type(b), c) # this is ok + +proc `<`*[A](s, t: A): bool = discard +proc `==`*[A](s, t: HashSet[A]): bool = discard +proc `<=`*[A](s, t: HashSet[A]): bool = discard + +# these are ok: +proc `$`*[A](s: HashSet[A]): string = discard +proc `*`*[A](s1, s2: HashSet[A]): HashSet[A] {.inline.} = discard +proc `-+-`*[A](s1, s2: HashSet[A]): HashSet[A] {.inline.} = discard + +# bug #11470 diff --git a/nimpretty/tests/expected/exhaustive.nim b/nimpretty/tests/expected/exhaustive.nim index 75580081d..4e90faa25 100644 --- a/nimpretty/tests/expected/exhaustive.nim +++ b/nimpretty/tests/expected/exhaustive.nim @@ -419,3 +419,26 @@ type ccFastCall, # fastcall (pass parameters in registers) ccClosure, # proc has a closure ccNoConvention # needed for generating proper C procs sometimes + + +proc isValid1*[A](s: HashSet[A]): bool {.deprecated: + "Deprecated since v0.20; sets are initialized by default".} = + ## Returns `true` if the set has been initialized (with `initHashSet proc + ## <#initHashSet,int>`_ or `init proc <#init,HashSet[A],int>`_). + result = s.data.len > 0 + # bug #11468 + +assert $type(a) == "Option[system.int]" +foo(a, $type(b), c) +foo(type(b), c) # this is ok + +proc `<`*[A](s, t: A): bool = discard +proc `==`*[A](s, t: HashSet[A]): bool = discard +proc `<=`*[A](s, t: HashSet[A]): bool = discard + +# these are ok: +proc `$`*[A](s: HashSet[A]): string = discard +proc `*`*[A](s1, s2: HashSet[A]): HashSet[A] {.inline.} = discard +proc `-+-`*[A](s1, s2: HashSet[A]): HashSet[A] {.inline.} = discard + +# bug #11470 diff --git a/nimsuggest/nimsuggest.nim b/nimsuggest/nimsuggest.nim index 97dd487aa..98e2d89c3 100644 --- a/nimsuggest/nimsuggest.nim +++ b/nimsuggest/nimsuggest.nim @@ -224,8 +224,8 @@ proc executeEpc(cmd: IdeCmd, args: SexpNode; execute(cmd, file, dirtyfile, int(line), int(column), graph) proc returnEpc(socket: Socket, uid: BiggestInt, s: SexpNode|string, - return_symbol = "return") = - let response = $convertSexp([newSSymbol(return_symbol), uid, s]) + returnSymbol = "return") = + let response = $convertSexp([newSSymbol(returnSymbol), uid, s]) socket.send(toHex(len(response), 6)) socket.send(response) diff --git a/tests/async/tasyncclosestall.nim b/tests/async/tasyncclosestall.nim new file mode 100644 index 000000000..e10e23074 --- /dev/null +++ b/tests/async/tasyncclosestall.nim @@ -0,0 +1,99 @@ +discard """ + outputsub: "send has errored. As expected. All good!" + exitcode: 0 +""" +import asyncdispatch, asyncnet + +when defined(windows): + from winlean import ERROR_NETNAME_DELETED +else: + from posix import EBADF + +# This reproduces a case where a socket remains stuck waiting for writes +# even when the socket is closed. +const + port = Port(50726) + timeout = 5000 + +var sent = 0 + +proc keepSendingTo(c: AsyncSocket) {.async.} = + while true: + # This write will eventually get stuck because the client is not reading + # its messages. + let sendFut = c.send("Foobar" & $sent & "\n", flags = {}) + if not await withTimeout(sendFut, timeout): + # The write is stuck. Let's simulate a scenario where the socket + # does not respond to PING messages, and we close it. The above future + # should complete after the socket is closed, not continue stalling. + echo("Socket has stalled, closing it") + c.close() + + let timeoutFut = withTimeout(sendFut, timeout) + yield timeoutFut + if timeoutFut.failed: + let errCode = ((ref OSError)(timeoutFut.error)).errorCode + # The behaviour differs across platforms. On Windows ERROR_NETNAME_DELETED + # is raised which we classif as a "diconnection error", hence we overwrite + # the flags above in the `send` call so that this error is raised. + # + # On Linux the EBADF error code is raised, this is because the socket + # is closed. + # + # This means that by default the behaviours will differ between Windows + # and Linux. I think this is fine though, it makes sense mainly because + # Windows doesn't use a IO readiness model. We can fix this later if + # necessary to reclassify ERROR_NETNAME_DELETED as not a "disconnection + # error" (TODO) + when defined(windows): + if errCode == ERROR_NETNAME_DELETED: + echo("send has errored. As expected. All good!") + quit(QuitSuccess) + else: + raise newException(ValueError, "Test failed. Send failed with code " & $errCode) + else: + if errCode == EBADF: + echo("send has errored. As expected. All good!") + quit(QuitSuccess) + else: + raise newException(ValueError, "Test failed. Send failed with code " & $errCode) + + # The write shouldn't succeed and also shouldn't be stalled. + if timeoutFut.read(): + raise newException(ValueError, "Test failed. Send was expected to fail.") + else: + raise newException(ValueError, "Test failed. Send future is still stalled.") + sent.inc(1) + +proc startClient() {.async.} = + let client = newAsyncSocket() + await client.connect("localhost", port) + echo("Connected") + + let firstLine = await client.recvLine() + echo("Received first line as a client: ", firstLine) + echo("Now not reading anymore") + while true: await sleepAsync(1000) + +proc debug() {.async.} = + while true: + echo("Sent ", sent) + await sleepAsync(1000) + +proc server() {.async.} = + var s = newAsyncSocket() + s.setSockOpt(OptReuseAddr, true) + s.bindAddr(port) + s.listen() + + # We're now ready to accept connections, so start the client + asyncCheck startClient() + asyncCheck debug() + + while true: + let client = await accept(s) + asyncCheck keepSendingTo(client) + +when isMainModule: + waitFor server() + |