mirror of
https://github.com/nim-lang/Nim.git
synced 2026-07-29 19:57:58 +00:00
Introduce addRead/addWrite for Windows IOCP.
This commit is contained in:
@@ -444,7 +444,16 @@ when defined(windows) or defined(nimdoc):
|
||||
|
||||
PCustomOverlapped* = ref CustomOverlapped
|
||||
|
||||
PCD = object
|
||||
ioPort: Handle
|
||||
handleFd: AsyncFD
|
||||
waitFd: Handle
|
||||
ovl: PCustomOverlapped
|
||||
PPCD = ptr PCD
|
||||
|
||||
AsyncFD* = distinct int
|
||||
|
||||
Callback = proc (fd: AsyncFD): bool {.closure,gcsafe.}
|
||||
{.deprecated: [TCompletionKey: CompletionKey, TAsyncFD: AsyncFD,
|
||||
TCustomOverlapped: CustomOverlapped, TCompletionData: CompletionData].}
|
||||
|
||||
@@ -953,6 +962,117 @@ when defined(windows) or defined(nimdoc):
|
||||
## Unregisters ``fd``.
|
||||
getGlobalDispatcher().handles.excl(fd)
|
||||
|
||||
{.push stackTrace:off.}
|
||||
proc waitableCallback(param: pointer,
|
||||
TimerOrWaitFired: WINBOOL): void {.stdcall.} =
|
||||
var p = cast[PPCD](param)
|
||||
discard postQueuedCompletionStatus(p.ioPort, Dword(TimerOrWaitFired),
|
||||
ULONG_PTR(p.handleFd),
|
||||
cast[pointer](p.ovl))
|
||||
{.pop.}
|
||||
|
||||
template registerWaitableEvent(mask) =
|
||||
let p = getGlobalDispatcher()
|
||||
var flags = (WT_EXECUTEINWAITTHREAD or WT_EXECUTEONLYONCE).Dword
|
||||
var hEvent = wsaCreateEvent()
|
||||
if hEvent == 0:
|
||||
raiseOSError(osLastError())
|
||||
var pcd = cast[PPCD](allocShared0(sizeof(PCD)))
|
||||
pcd.ioPort = p.ioPort
|
||||
pcd.handleFd = fd
|
||||
var ol = PCustomOverlapped()
|
||||
GC_ref(ol)
|
||||
|
||||
ol.data = CompletionData(fd: fd, cb:
|
||||
proc(fd: AsyncFD, bytesCount: Dword, errcode: OSErrorCode) =
|
||||
# we excluding our `fd` because cb(fd) can register own handler
|
||||
# for this `fd`
|
||||
p.handles.excl(fd)
|
||||
# unregisterWait() is called before callback, because appropriate
|
||||
# winsockets function can re-enable event.
|
||||
# https://msdn.microsoft.com/en-us/library/windows/desktop/ms741576(v=vs.85).aspx
|
||||
discard unregisterWait(pcd.waitFd)
|
||||
if cb(fd):
|
||||
# callback returned `true`, so we free all allocated resources
|
||||
discard wsaCloseEvent(hEvent)
|
||||
deallocShared(cast[pointer](pcd))
|
||||
# pcd.ovl will be unrefed in poll().
|
||||
else:
|
||||
# callback returned `false` we need to continue
|
||||
if p.handles.contains(fd):
|
||||
# new callback was already registered with `fd`, so we free all
|
||||
# allocated resources. This happens because in callback `cb`
|
||||
# addRead/addWrite was called with same `fd`.
|
||||
discard wsaCloseEvent(hEvent)
|
||||
deallocShared(cast[pointer](pcd))
|
||||
else:
|
||||
# we need to include `fd` again
|
||||
p.handles.incl(fd)
|
||||
# and register WaitForSingleObject again
|
||||
if not registerWaitForSingleObject(addr(pcd.waitFd), hEvent,
|
||||
cast[WAITORTIMERCALLBACK](waitableCallback),
|
||||
cast[pointer](pcd), INFINITE, flags):
|
||||
# pcd.ovl will be unrefed in poll()
|
||||
discard wsaCloseEvent(hEvent)
|
||||
deallocShared(cast[pointer](pcd))
|
||||
raiseOSError(osLastError())
|
||||
else:
|
||||
# we ref pcd.ovl one more time, because it will be unrefed in
|
||||
# poll()
|
||||
GC_ref(pcd.ovl)
|
||||
)
|
||||
# This is main part of `hacky way` is using WSAEventSelect, so `hEvent`
|
||||
# will be signaled when appropriate `mask` events will be triggered.
|
||||
if wsaEventSelect(fd.SocketHandle, hEvent, mask) != 0:
|
||||
GC_unref(ol)
|
||||
deallocShared(cast[pointer](pcd))
|
||||
discard wsaCloseEvent(hEvent)
|
||||
raiseOSError(osLastError())
|
||||
|
||||
pcd.ovl = ol
|
||||
if not registerWaitForSingleObject(addr(pcd.waitFd), hEvent,
|
||||
cast[WAITORTIMERCALLBACK](waitableCallback),
|
||||
cast[pointer](pcd), INFINITE, flags):
|
||||
GC_unref(ol)
|
||||
deallocShared(cast[pointer](pcd))
|
||||
discard wsaCloseEvent(hEvent)
|
||||
raiseOSError(osLastError())
|
||||
p.handles.incl(fd)
|
||||
|
||||
proc addRead*(fd: AsyncFD, cb: Callback) =
|
||||
## Start watching the file descriptor for read availability and then call
|
||||
## the callback ``cb``.
|
||||
##
|
||||
## This is not ``pure`` mechanism for Windows Completion Ports (IOCP),
|
||||
## so if you can avoid it, please do it. Use `addRead` only if really
|
||||
## need it (main usecase is adaptation of `unix like` libraries to be
|
||||
## asynchronous on Windows).
|
||||
## If you use this function, you dont need to use asyncdispatch.recv()
|
||||
## or asyncdispatch.accept(), because they are using IOCP, please use
|
||||
## nativesockets.recv() and nativesockets.accept() instead.
|
||||
##
|
||||
## Be sure your callback ``cb`` returns ``true``, if you want to remove
|
||||
## watch of `read` notifications, and ``false``, if you want to continue
|
||||
## receiving notifies.
|
||||
registerWaitableEvent(FD_READ or FD_ACCEPT or FD_OOB or FD_CLOSE)
|
||||
|
||||
proc addWrite*(fd: AsyncFD, cb: Callback) =
|
||||
## Start watching the file descriptor for write availability and then call
|
||||
## the callback ``cb``.
|
||||
##
|
||||
## This is not ``pure`` mechanism for Windows Completion Ports (IOCP),
|
||||
## so if you can avoid it, please do it. Use `addWrite` only if really
|
||||
## need it (main usecase is adaptation of `unix like` libraries to be
|
||||
## asynchronous on Windows).
|
||||
## If you use this function, you dont need to use asyncdispatch.send()
|
||||
## or asyncdispatch.connect(), because they are using IOCP, please use
|
||||
## nativesockets.send() and nativesockets.connect() instead.
|
||||
##
|
||||
## Be sure your callback ``cb`` returns ``true``, if you want to remove
|
||||
## watch of `write` notifications, and ``false``, if you want to continue
|
||||
## receiving notifies.
|
||||
registerWaitableEvent(FD_WRITE or FD_CONNECT or FD_CLOSE)
|
||||
|
||||
initAll()
|
||||
else:
|
||||
import selectors
|
||||
|
||||
@@ -442,6 +442,8 @@ type
|
||||
sa_family*: int16 # unsigned
|
||||
sa_data: array[0..13, char]
|
||||
|
||||
PSockAddr = ptr SockAddr
|
||||
|
||||
InAddr* {.importc: "IN_ADDR", header: "winsock2.h".} = object
|
||||
s_addr*: uint32 # IP address
|
||||
|
||||
@@ -889,3 +891,117 @@ proc inet_ntop*(family: cint, paddr: pointer, pStringBuffer: cstring,
|
||||
result = inet_ntop_real(family, paddr, pStringBuffer, stringBufSize)
|
||||
else:
|
||||
result = inet_ntop_emulated(family, paddr, pStringBuffer, stringBufSize)
|
||||
|
||||
type
|
||||
WSAPROC_ACCEPTEX* = proc (sListenSocket: SocketHandle,
|
||||
sAcceptSocket: SocketHandle,
|
||||
lpOutputBuffer: pointer, dwReceiveDataLength: DWORD,
|
||||
dwLocalAddressLength: DWORD,
|
||||
dwRemoteAddressLength: DWORD,
|
||||
lpdwBytesReceived: ptr DWORD,
|
||||
lpOverlapped: POVERLAPPED): bool {.
|
||||
stdcall,gcsafe.}
|
||||
|
||||
WSAPROC_CONNECTEX* = proc (s: SocketHandle, name: ptr SockAddr, namelen: cint,
|
||||
lpSendBuffer: pointer, dwSendDataLength: DWORD,
|
||||
lpdwBytesSent: ptr DWORD,
|
||||
lpOverlapped: POVERLAPPED): bool {.
|
||||
stdcall,gcsafe.}
|
||||
|
||||
WSAPROC_GETACCEPTEXSOCKADDRS* = proc(lpOutputBuffer: pointer,
|
||||
dwReceiveDataLength: DWORD,
|
||||
dwLocalAddressLength: DWORD,
|
||||
dwRemoteAddressLength: DWORD,
|
||||
LocalSockaddr: ptr PSockAddr,
|
||||
LocalSockaddrLength: ptr cint,
|
||||
RemoteSockaddr: ptr PSockAddr,
|
||||
RemoteSockaddrLength: ptr cint) {.
|
||||
stdcall,gcsafe.}
|
||||
|
||||
const
|
||||
WT_EXECUTEDEFAULT* = 0x00000000'i32
|
||||
WT_EXECUTEINIOTHREAD* = 0x00000001'i32
|
||||
WT_EXECUTEINUITHREAD* = 0x00000002'i32
|
||||
WT_EXECUTEINWAITTHREAD* = 0x00000004'i32
|
||||
WT_EXECUTEONLYONCE* = 0x00000008'i32
|
||||
WT_EXECUTELONGFUNCTION* = 0x00000010'i32
|
||||
WT_EXECUTEINTIMERTHREAD* = 0x00000020'i32
|
||||
WT_EXECUTEINPERSISTENTIOTHREAD* = 0x00000040'i32
|
||||
WT_EXECUTEINPERSISTENTTHREAD* = 0x00000080'i32
|
||||
WT_TRANSFER_IMPERSONATION* = 0x00000100'i32
|
||||
PROCESS_TERMINATE* = 0x00000001'i32
|
||||
PROCESS_CREATE_THREAD* = 0x00000002'i32
|
||||
PROCESS_SET_SESSIONID* = 0x00000004'i32
|
||||
PROCESS_VM_OPERATION* = 0x00000008'i32
|
||||
PROCESS_VM_READ* = 0x00000010'i32
|
||||
PROCESS_VM_WRITE* = 0x00000020'i32
|
||||
PROCESS_DUP_HANDLE* = 0x00000040'i32
|
||||
PROCESS_CREATE_PROCESS* = 0x00000080'i32
|
||||
PROCESS_SET_QUOTA* = 0x00000100'i32
|
||||
PROCESS_SET_INFORMATION* = 0x00000200'i32
|
||||
PROCESS_QUERY_INFORMATION* = 0x00000400'i32
|
||||
PROCESS_SUSPEND_RESUME* = 0x00000800'i32
|
||||
PROCESS_QUERY_LIMITED_INFORMATION* = 0x00001000'i32
|
||||
PROCESS_SET_LIMITED_INFORMATION* = 0x00002000'i32
|
||||
type
|
||||
WAITORTIMERCALLBACK* = proc(para1: pointer, para2: int32): void {.stdcall.}
|
||||
|
||||
proc postQueuedCompletionStatus*(CompletionPort: HANDLE,
|
||||
dwNumberOfBytesTransferred: DWORD,
|
||||
dwCompletionKey: ULONG_PTR,
|
||||
lpOverlapped: pointer): bool
|
||||
{.stdcall, dynlib: "kernel32", importc: "PostQueuedCompletionStatus".}
|
||||
|
||||
proc registerWaitForSingleObject*(phNewWaitObject: ptr Handle, hObject: Handle,
|
||||
Callback: WAITORTIMERCALLBACK,
|
||||
Context: pointer,
|
||||
dwMilliseconds: ULONG,
|
||||
dwFlags: ULONG): bool
|
||||
{.stdcall, dynlib: "kernel32", importc: "RegisterWaitForSingleObject".}
|
||||
|
||||
proc unregisterWait*(WaitHandle: HANDLE): DWORD
|
||||
{.stdcall, dynlib: "kernel32", importc: "UnregisterWait".}
|
||||
|
||||
proc openProcess*(dwDesiredAccess: DWORD, bInheritHandle: WINBOOL,
|
||||
dwProcessId: DWORD): Handle
|
||||
{.stdcall, dynlib: "kernel32", importc: "OpenProcess".}
|
||||
|
||||
when defined(useWinAnsi):
|
||||
proc createEvent*(lpEventAttributes: ptr SECURITY_ATTRIBUTES,
|
||||
bManualReset: DWORD, bInitialState: DWORD,
|
||||
lpName: cstring): Handle
|
||||
{.stdcall, dynlib: "kernel32", importc: "CreateEventA".}
|
||||
else:
|
||||
proc createEvent*(lpEventAttributes: ptr SECURITY_ATTRIBUTES,
|
||||
bManualReset: DWORD, bInitialState: DWORD,
|
||||
lpName: ptr Utf16Char): Handle
|
||||
{.stdcall, dynlib: "kernel32", importc: "CreateEventW".}
|
||||
|
||||
proc setEvent*(hEvent: Handle): cint
|
||||
{.stdcall, dynlib: "kernel32", importc: "SetEvent".}
|
||||
|
||||
const
|
||||
FD_READ* = 0x00000001'i32
|
||||
FD_WRITE* = 0x00000002'i32
|
||||
FD_OOB* = 0x00000004'i32
|
||||
FD_ACCEPT* = 0x00000008'i32
|
||||
FD_CONNECT* = 0x00000010'i32
|
||||
FD_CLOSE* = 0x00000020'i32
|
||||
FD_QQS* = 0x00000040'i32
|
||||
FD_GROUP_QQS* = 0x00000080'i32
|
||||
FD_ROUTING_INTERFACE_CHANGE* = 0x00000100'i32
|
||||
FD_ADDRESS_LIST_CHANGE* = 0x00000200'i32
|
||||
FD_ALL_EVENTS* = 0x000003FF'i32
|
||||
|
||||
proc wsaEventSelect*(s: SocketHandle, hEventObject: Handle,
|
||||
lNetworkEvents: clong): cint
|
||||
{.stdcall, importc: "WSAEventSelect", dynlib: "ws2_32.dll".}
|
||||
|
||||
proc wsaCreateEvent*(): Handle
|
||||
{.stdcall, importc: "WSACreateEvent", dynlib: "ws2_32.dll".}
|
||||
|
||||
proc wsaCloseEvent*(hEvent: Handle): bool
|
||||
{.stdcall, importc: "WSACloseEvent", dynlib: "ws2_32.dll".}
|
||||
|
||||
proc wsaResetEvent*(hEvent: Handle): bool
|
||||
{.stdcall, importc: "WSAResetEvent", dynlib: "ws2_32.dll".}
|
||||
Reference in New Issue
Block a user