Fix emscripten compilation errors. (#46)

* Move initAPI to newDispatcher() call.
This commit is contained in:
Eugene Kabanov 2019-09-10 20:19:49 +03:00 committed by Jacek Sieka
parent ea9fa933dd
commit b41af14f86
4 changed files with 83 additions and 101 deletions

View File

@ -5,6 +5,5 @@
# Licensed under either of # Licensed under either of
# Apache License, version 2.0, (LICENSE-APACHEv2) # Apache License, version 2.0, (LICENSE-APACHEv2)
# MIT license (LICENSE-MIT) # MIT license (LICENSE-MIT)
import chronos/[asyncloop, asyncfutures2, asyncsync, handles, transport, import chronos/[asyncloop, asyncsync, handles, transport, timer]
timer] export asyncloop, asyncsync, handles, transport, timer
export asyncloop, asyncfutures2, asyncsync, handles, transport, timer

View File

@ -8,7 +8,7 @@
# Apache License, version 2.0, (LICENSE-APACHEv2) # Apache License, version 2.0, (LICENSE-APACHEv2)
# MIT license (LICENSE-MIT) # MIT license (LICENSE-MIT)
import os, tables, strutils, times, heapqueue, options, deques, cstrutils import os, tables, strutils, heapqueue, options, deques, cstrutils
import srcloc import srcloc
export srcloc export srcloc
@ -17,14 +17,6 @@ const
LocCompleteIndex = 1 LocCompleteIndex = 1
type type
CallbackFunc* = proc (arg: pointer = nil) {.gcsafe.}
CallSoonProc* = proc (c: CallbackFunc, u: pointer = nil) {.gcsafe.}
AsyncCallback* = object
function*: CallbackFunc
udata*: pointer
deleted*: bool
# ZAH: This can probably be stored with a cheaper representation # ZAH: This can probably be stored with a cheaper representation
# until the moment it needs to be printed to the screen # until the moment it needs to be printed to the screen
# (e.g. seq[StackTraceEntry]) # (e.g. seq[StackTraceEntry])
@ -69,22 +61,6 @@ type
var currentID* {.threadvar.}: int var currentID* {.threadvar.}: int
currentID = 0 currentID = 0
# ZAH: This seems unnecessary. Isn't it easy to introduce a seperate
# module for the dispatcher type, so it can be directly referenced here?
var callSoonHolder {.threadvar.}: CallSoonProc
proc getCallSoonProc*(): CallSoonProc {.gcsafe.} =
## Get current implementation of ``callSoon``.
return callSoonHolder
proc setCallSoonProc*(p: CallSoonProc) =
## Change current implementation of ``callSoon``.
callSoonHolder = p
proc callSoon*(c: CallbackFunc, u: pointer = nil) =
## Call ``cbproc`` "soon".
callSoonHolder(c, u)
template setupFutureBase(loc: ptr SrcLoc) = template setupFutureBase(loc: ptr SrcLoc) =
new(result) new(result)
result.state = FutureState.Pending result.state = FutureState.Pending

View File

@ -10,14 +10,12 @@
include "system/inclrtl" include "system/inclrtl"
import os, tables, strutils, heapqueue, lists, options import os, tables, strutils, heapqueue, lists, options, nativesockets, net,
deques
import timer import timer
import asyncfutures2 except callSoon
import nativesockets, net, deques
export Port, SocketFlag export Port, SocketFlag
export asyncfutures2, timer export timer
#{.injectStmt: newGcInvariant().} #{.injectStmt: newGcInvariant().}
@ -165,14 +163,28 @@ export asyncfutures2, timer
# TODO: Check if yielded future is nil and throw a more meaningful exception # TODO: Check if yielded future is nil and throw a more meaningful exception
const unixPlatform = defined(macosx) or defined(freebsd) or
defined(netbsd) or defined(openbsd) or
defined(dragonfly) or defined(macos) or
defined(linux) or defined(android) or
defined(solaris)
when defined(windows): when defined(windows):
import winlean, sets, hashes import winlean, sets, hashes
else: elif unixPlatform:
import selectors import selectors
from posix import EINTR, EAGAIN, EINPROGRESS, EWOULDBLOCK, MSG_PEEK, from posix import EINTR, EAGAIN, EINPROGRESS, EWOULDBLOCK, MSG_PEEK,
MSG_NOSIGNAL, SIGPIPE MSG_NOSIGNAL, SIGPIPE
type type
CallbackFunc* = proc (arg: pointer = nil) {.gcsafe.}
CallSoonProc* = proc (c: CallbackFunc, u: pointer = nil) {.gcsafe.}
AsyncCallback* = object
function*: CallbackFunc
udata*: pointer
deleted*: bool
AsyncError* = object of CatchableError AsyncError* = object of CatchableError
## Generic async exception ## Generic async exception
AsyncTimeoutError* = object of AsyncError AsyncTimeoutError* = object of AsyncError
@ -195,11 +207,7 @@ type
proc `<`(a, b: TimerCallback): bool = proc `<`(a, b: TimerCallback): bool =
result = a.finishAt < b.finishAt result = a.finishAt < b.finishAt
proc callSoon(cbproc: CallbackFunc, data: pointer = nil) {.gcsafe.} proc callSoon*(cbproc: CallbackFunc, data: pointer = nil) {.gcsafe.}
proc initCallSoonProc() =
if asyncfutures2.getCallSoonProc().isNil:
asyncfutures2.setCallSoonProc(callSoon)
func getAsyncTimestamp*(a: Duration): auto {.inline.} = func getAsyncTimestamp*(a: Duration): auto {.inline.} =
## Return rounded up value of duration with milliseconds resolution. ## Return rounded up value of duration with milliseconds resolution.
@ -304,6 +312,51 @@ when defined(windows) or defined(nimdoc):
proc hash(x: AsyncFD): Hash {.borrow.} proc hash(x: AsyncFD): Hash {.borrow.}
proc `==`*(x: AsyncFD, y: AsyncFD): bool {.borrow.} proc `==`*(x: AsyncFD, y: AsyncFD): bool {.borrow.}
proc getFunc(s: SocketHandle, fun: var pointer, guid: var GUID): bool =
var bytesRet: DWORD
fun = nil
result = WSAIoctl(s, SIO_GET_EXTENSION_FUNCTION_POINTER, addr guid,
sizeof(GUID).DWORD, addr fun, sizeof(pointer).DWORD,
addr bytesRet, nil, nil) == 0
proc initAPI(loop: PDispatcher) =
var
WSAID_TRANSMITFILE = GUID(
D1: 0xb5367df0'i32, D2: 0xcbac'i16, D3: 0x11cf'i16,
D4: [0x95'i8, 0xca'i8, 0x00'i8, 0x80'i8,
0x5f'i8, 0x48'i8, 0xa1'i8, 0x92'i8])
var wsa: WSAData
if wsaStartup(0x0202'i16, addr wsa) != 0:
raiseOSError(osLastError())
let sock = winlean.socket(winlean.AF_INET, 1, 6)
if sock == INVALID_SOCKET:
raiseOSError(osLastError())
var funcPointer: pointer = nil
if not getFunc(sock, funcPointer, WSAID_CONNECTEX):
let err = osLastError()
close(sock)
raiseOSError(err)
loop.connectEx = cast[WSAPROC_CONNECTEX](funcPointer)
if not getFunc(sock, funcPointer, WSAID_ACCEPTEX):
let err = osLastError()
close(sock)
raiseOSError(err)
loop.acceptEx = cast[WSAPROC_ACCEPTEX](funcPointer)
if not getFunc(sock, funcPointer, WSAID_GETACCEPTEXSOCKADDRS):
let err = osLastError()
close(sock)
raiseOSError(err)
loop.getAcceptExSockAddrs = cast[WSAPROC_GETACCEPTEXSOCKADDRS](funcPointer)
if not getFunc(sock, funcPointer, WSAID_TRANSMITFILE):
let err = osLastError()
close(sock)
raiseOSError(err)
loop.transmitFile = cast[WSAPROC_TRANSMITFILE](funcPointer)
close(sock)
proc newDispatcher*(): PDispatcher = proc newDispatcher*(): PDispatcher =
## Creates a new Dispatcher instance. ## Creates a new Dispatcher instance.
new result new result
@ -322,6 +375,7 @@ when defined(windows) or defined(nimdoc):
result.timers = newHeapQueue[TimerCallback]() result.timers = newHeapQueue[TimerCallback]()
result.callbacks = initDeque[AsyncCallback](64) result.callbacks = initDeque[AsyncCallback](64)
result.trackers = initTable[string, TrackerBase]() result.trackers = initTable[string, TrackerBase]()
initAPI(result)
var gDisp{.threadvar.}: PDispatcher ## Global dispatcher var gDisp{.threadvar.}: PDispatcher ## Global dispatcher
@ -330,7 +384,6 @@ when defined(windows) or defined(nimdoc):
if not gDisp.isNil: if not gDisp.isNil:
doAssert gDisp.callbacks.len == 0 doAssert gDisp.callbacks.len == 0
gDisp = disp gDisp = disp
initCallSoonProc()
proc getGlobalDispatcher*(): PDispatcher = proc getGlobalDispatcher*(): PDispatcher =
## Returns current thread's dispatcher instance. ## Returns current thread's dispatcher instance.
@ -394,53 +447,6 @@ when defined(windows) or defined(nimdoc):
# poll() call. # poll() call.
loop.processCallbacks() loop.processCallbacks()
proc getFunc(s: SocketHandle, fun: var pointer, guid: var GUID): bool =
var bytesRet: DWORD
fun = nil
result = WSAIoctl(s, SIO_GET_EXTENSION_FUNCTION_POINTER, addr guid,
sizeof(GUID).DWORD, addr fun, sizeof(pointer).DWORD,
addr bytesRet, nil, nil) == 0
proc initAPI() =
var
WSAID_TRANSMITFILE = GUID(
D1: 0xb5367df0'i32, D2: 0xcbac'i16, D3: 0x11cf'i16,
D4: [0x95'i8, 0xca'i8, 0x00'i8, 0x80'i8,
0x5f'i8, 0x48'i8, 0xa1'i8, 0x92'i8])
let loop = getGlobalDispatcher()
var wsa: WSAData
if wsaStartup(0x0202'i16, addr wsa) != 0:
raiseOSError(osLastError())
let sock = winlean.socket(winlean.AF_INET, 1, 6)
if sock == INVALID_SOCKET:
raiseOSError(osLastError())
var funcPointer: pointer = nil
if not getFunc(sock, funcPointer, WSAID_CONNECTEX):
let err = osLastError()
close(sock)
raiseOSError(err)
loop.connectEx = cast[WSAPROC_CONNECTEX](funcPointer)
if not getFunc(sock, funcPointer, WSAID_ACCEPTEX):
let err = osLastError()
close(sock)
raiseOSError(err)
loop.acceptEx = cast[WSAPROC_ACCEPTEX](funcPointer)
if not getFunc(sock, funcPointer, WSAID_GETACCEPTEXSOCKADDRS):
let err = osLastError()
close(sock)
raiseOSError(err)
loop.getAcceptExSockAddrs = cast[WSAPROC_GETACCEPTEXSOCKADDRS](funcPointer)
if not getFunc(sock, funcPointer, WSAID_TRANSMITFILE):
let err = osLastError()
close(sock)
raiseOSError(err)
loop.transmitFile = cast[WSAPROC_TRANSMITFILE](funcPointer)
close(sock)
proc closeSocket*(fd: AsyncFD, aftercb: CallbackFunc = nil) = proc closeSocket*(fd: AsyncFD, aftercb: CallbackFunc = nil) =
## Closes a socket and ensures that it is unregistered. ## Closes a socket and ensures that it is unregistered.
let loop = getGlobalDispatcher() let loop = getGlobalDispatcher()
@ -467,7 +473,10 @@ when defined(windows) or defined(nimdoc):
## Returns ``true`` if ``fd`` is registered in thread's dispatcher. ## Returns ``true`` if ``fd`` is registered in thread's dispatcher.
return fd in disp.handles return fd in disp.handles
else: elif unixPlatform:
const
SIG_IGN = cast[proc(x: cint) {.noconv,gcsafe.}](1)
type type
AsyncFD* = distinct cint AsyncFD* = distinct cint
@ -489,6 +498,10 @@ else:
proc `==`*(x, y: AsyncFD): bool {.borrow.} proc `==`*(x, y: AsyncFD): bool {.borrow.}
proc initAPI(disp: PDispatcher) =
# We are ignoring SIGPIPE signal, because we are working with EPIPE.
posix.signal(cint(SIGPIPE), SIG_IGN)
proc newDispatcher*(): PDispatcher = proc newDispatcher*(): PDispatcher =
## Create new dispatcher. ## Create new dispatcher.
new result new result
@ -497,6 +510,7 @@ else:
result.callbacks = initDeque[AsyncCallback](64) result.callbacks = initDeque[AsyncCallback](64)
result.keys = newSeq[ReadyKey](64) result.keys = newSeq[ReadyKey](64)
result.trackers = initTable[string, TrackerBase]() result.trackers = initTable[string, TrackerBase]()
initAPI(result)
var gDisp{.threadvar.}: PDispatcher ## Global dispatcher var gDisp{.threadvar.}: PDispatcher ## Global dispatcher
@ -505,7 +519,6 @@ else:
if not gDisp.isNil: if not gDisp.isNil:
doAssert gDisp.callbacks.len == 0 doAssert gDisp.callbacks.len == 0
gDisp = disp gDisp = disp
initCallSoonProc()
proc getGlobalDispatcher*(): PDispatcher = proc getGlobalDispatcher*(): PDispatcher =
## Returns current thread's dispatcher instance. ## Returns current thread's dispatcher instance.
@ -699,13 +712,8 @@ else:
# poll() call. # poll() call.
loop.processCallbacks() loop.processCallbacks()
const else:
SIG_IGN = cast[proc(x: cint) {.noconv,gcsafe.}](1) proc initAPI() = discard
proc initAPI() =
# We are ignoring SIGPIPE signal, because we are working with EPIPE.
posix.signal(cint(SIGPIPE), SIG_IGN)
discard getGlobalDispatcher()
proc addTimer*(at: Moment, cb: CallbackFunc, udata: pointer = nil) = proc addTimer*(at: Moment, cb: CallbackFunc, udata: pointer = nil) =
## Arrange for the callback ``cb`` to be called at the given absolute ## Arrange for the callback ``cb`` to be called at the given absolute
@ -745,6 +753,8 @@ proc removeTimer*(at: uint64, cb: CallbackFunc, udata: pointer = nil) {.
inline, deprecated: "Use removeTimer(Duration, cb, udata)".} = inline, deprecated: "Use removeTimer(Duration, cb, udata)".} =
removeTimer(Moment.init(int64(at), Millisecond), cb, udata) removeTimer(Moment.init(int64(at), Millisecond), cb, udata)
include asyncfutures2
proc sleepAsync*(duration: Duration): Future[void] = proc sleepAsync*(duration: Duration): Future[void] =
## Suspends the execution of the current async procedure for the next ## Suspends the execution of the current async procedure for the next
## ``duration`` time. ## ``duration`` time.
@ -895,7 +905,7 @@ proc wait*[T](fut: Future[T], timeout = -1): Future[T] {.
include asyncmacro2 include asyncmacro2
proc callSoon(cbproc: CallbackFunc, data: pointer = nil) = proc callSoon*(cbproc: CallbackFunc, data: pointer = nil) =
## Schedule `cbproc` to be called as soon as possible. ## Schedule `cbproc` to be called as soon as possible.
## The callback is called when control returns to the event loop. ## The callback is called when control returns to the event loop.
doAssert(not isNil(cbproc)) doAssert(not isNil(cbproc))
@ -924,6 +934,3 @@ proc getTracker*(id: string): TrackerBase =
## Get ``tracker`` from current thread dispatcher using identifier ``id``. ## Get ``tracker`` from current thread dispatcher using identifier ``id``.
let loop = getGlobalDispatcher() let loop = getGlobalDispatcher()
result = loop.trackers.getOrDefault(id, nil) result = loop.trackers.getOrDefault(id, nil)
# Global API and callSoon() initialization.
initAPI()

View File

@ -11,7 +11,7 @@
## ************* ## *************
## `asyncdispatch` module depends on the `asyncmacro` module to work properly. ## `asyncdispatch` module depends on the `asyncmacro` module to work properly.
import macros, strutils, asyncfutures2 import macros, strutils
proc skipUntilStmtList(node: NimNode): NimNode {.compileTime.} = proc skipUntilStmtList(node: NimNode): NimNode {.compileTime.} =
# Skips a nest of StmtList's. # Skips a nest of StmtList's.