mirror of
https://github.com/nim-lang/Nim.git
synced 2026-01-21 03:50:43 +00:00
Merge pull request #3388 from nanoant/patch/kqueue-support-for-osx-and-freebsd
kqueue support for OS X and Free/Open/NetBSD
This commit is contained in:
71
lib/posix/kqueue.nim
Normal file
71
lib/posix/kqueue.nim
Normal file
@@ -0,0 +1,71 @@
|
||||
#
|
||||
#
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2015 Adam Strzelecki
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
{.deadCodeElim:on.}
|
||||
|
||||
from posix import Timespec
|
||||
|
||||
# Filters:
|
||||
const
|
||||
EVFILT_READ* = -1
|
||||
EVFILT_WRITE* = -2
|
||||
EVFILT_AIO* = -3
|
||||
EVFILT_VNODE* = -4
|
||||
EVFILT_PROC* = -5
|
||||
EVFILT_SIGNAL* = -6
|
||||
EVFILT_TIMER* = -7
|
||||
EVFILT_MACHPORT* = -8
|
||||
EVFILT_FS* = -9
|
||||
EVFILT_USER* = -10
|
||||
# -11 is unused
|
||||
EVFILT_VM* = -12
|
||||
|
||||
# Actions:
|
||||
const
|
||||
EV_ADD* = 0x0001 ## Add event to queue (implies enable).
|
||||
## Re-adding an existing element modifies it.
|
||||
EV_DELETE* = 0x0002 ## Delete event from queue.
|
||||
EV_ENABLE* = 0x0004 ## Enable event.
|
||||
EV_DISABLE* = 0x0008 ## Disable event (not reported).
|
||||
|
||||
# Flags:
|
||||
const
|
||||
EV_ONESHOT* = 0x0010 ## Only report one occurrence.
|
||||
EV_CLEAR* = 0x0020 ## Clear event state after reporting.
|
||||
EV_RECEIPT* = 0x0040 ## Force EV_ERROR on success, data == 0
|
||||
EV_DISPATCH* = 0x0080 ## Disable event after reporting.
|
||||
|
||||
# Return values:
|
||||
const
|
||||
EV_EOF* = 0x8000 ## EOF detected
|
||||
EV_ERROR* = 0x4000 ## Error, data contains errno
|
||||
|
||||
type
|
||||
KEvent* {.importc: "struct kevent",
|
||||
header: "<sys/event.h>", pure, final.} = object
|
||||
ident*: cuint ## identifier for this event (uintptr_t)
|
||||
filter*: cshort ## filter for event
|
||||
flags*: cushort ## general flags
|
||||
fflags*: cuint ## filter-specific flags
|
||||
data*: cuint ## filter-specific data (intptr_t)
|
||||
#udata*: ptr void ## opaque user data identifier
|
||||
|
||||
proc kqueue*(): cint {.importc: "kqueue", header: "<sys/event.h>".}
|
||||
## Creates new queue and returns its descriptor.
|
||||
|
||||
proc kevent*(kqFD: cint,
|
||||
changelist: ptr KEvent, nchanges: cint,
|
||||
eventlist: ptr KEvent, nevents: cint, timeout: ptr Timespec): cint
|
||||
{.importc: "kevent", header: "<sys/event.h>".}
|
||||
## Manipulates queue for given ``kqFD`` descriptor.
|
||||
|
||||
proc EV_SET*(event: ptr KEvent, ident: cuint, filter: cshort, flags: cushort,
|
||||
fflags: cuint, data: cuint, udata: ptr void)
|
||||
{.importc: "EV_SET", header: "<sys/event.h>".}
|
||||
## Fills event with given data.
|
||||
@@ -13,6 +13,8 @@ import os, unsigned, hashes
|
||||
|
||||
when defined(linux):
|
||||
import posix, epoll
|
||||
elif defined(macosx) or defined(freebsd) or defined(openbsd) or defined(netbsd):
|
||||
import posix, kqueue, times
|
||||
elif defined(windows):
|
||||
import winlean
|
||||
else:
|
||||
@@ -79,7 +81,6 @@ when defined(nimdoc):
|
||||
proc `[]`*(s: Selector, fd: SocketHandle): SelectorKey =
|
||||
## Retrieves the selector key for ``fd``.
|
||||
|
||||
|
||||
elif defined(linux):
|
||||
type
|
||||
Selector* = object
|
||||
@@ -99,15 +100,13 @@ elif defined(linux):
|
||||
result.data.fd = fd.cint
|
||||
|
||||
proc register*(s: var Selector, fd: SocketHandle, events: set[Event],
|
||||
data: SelectorData) =
|
||||
data: SelectorData) =
|
||||
var event = createEventStruct(events, fd)
|
||||
if events != {}:
|
||||
if epoll_ctl(s.epollFD, EPOLL_CTL_ADD, fd, addr(event)) != 0:
|
||||
raiseOSError(osLastError())
|
||||
|
||||
var key = SelectorKey(fd: fd, events: events, data: data)
|
||||
|
||||
s.fds[fd] = key
|
||||
s.fds[fd] = SelectorKey(fd: fd, events: events, data: data)
|
||||
|
||||
proc update*(s: var Selector, fd: SocketHandle, events: set[Event]) =
|
||||
if s.fds[fd].events != events:
|
||||
@@ -154,11 +153,6 @@ elif defined(linux):
|
||||
raiseOSError(err)
|
||||
|
||||
proc select*(s: var Selector, timeout: int): seq[ReadyInfo] =
|
||||
##
|
||||
## The ``events`` field of the returned ``key`` contains the original events
|
||||
## for which the ``fd`` was bound. This is contrary to the ``events`` field
|
||||
## of the ``TReadyInfo`` tuple which determines which events are ready
|
||||
## on the ``fd``.
|
||||
result = @[]
|
||||
let evNum = epoll_wait(s.epollFD, addr s.events[0], 64.cint, timeout.cint)
|
||||
if evNum < 0:
|
||||
@@ -204,6 +198,86 @@ elif defined(linux):
|
||||
## Retrieves the selector key for ``fd``.
|
||||
return s.fds[fd]
|
||||
|
||||
elif defined(macosx) or defined(freebsd) or defined(openbsd) or defined(netbsd):
|
||||
type
|
||||
Selector* = object
|
||||
kqFD: cint
|
||||
events: array[64, KEvent]
|
||||
when MultiThreaded:
|
||||
fds: SharedTable[SocketHandle, SelectorKey]
|
||||
else:
|
||||
fds: Table[SocketHandle, SelectorKey]
|
||||
|
||||
template modifyKQueue(kqFD: cint, fd: SocketHandle, event: Event,
|
||||
op: cushort) =
|
||||
var kev = KEvent(ident: fd.cuint,
|
||||
filter: if event == EvRead: EVFILT_READ else: EVFILT_WRITE,
|
||||
flags: op)
|
||||
if kevent(kqFD, addr kev, 1, nil, 0, nil) == -1:
|
||||
raiseOSError(osLastError())
|
||||
|
||||
proc register*(s: var Selector, fd: SocketHandle, events: set[Event],
|
||||
data: SelectorData) =
|
||||
for event in events:
|
||||
modifyKQueue(s.kqFD, fd, event, EV_ADD)
|
||||
s.fds[fd] = SelectorKey(fd: fd, events: events, data: data)
|
||||
|
||||
proc update*(s: var Selector, fd: SocketHandle, events: set[Event]) =
|
||||
let previousEvents = s.fds[fd].events
|
||||
if previousEvents != events:
|
||||
for event in events-previousEvents:
|
||||
modifyKQueue(s.kqFD, fd, event, EV_ADD)
|
||||
for event in previousEvents-events:
|
||||
modifyKQueue(s.kqFD, fd, event, EV_DELETE)
|
||||
s.fds.mget(fd).events = events
|
||||
|
||||
proc unregister*(s: var Selector, fd: SocketHandle) =
|
||||
for event in s.fds[fd].events:
|
||||
modifyKQueue(s.kqFD, fd, event, EV_DELETE)
|
||||
s.fds.del(fd)
|
||||
|
||||
proc close*(s: var Selector) =
|
||||
when MultiThreaded: deinitSharedTable(s.fds)
|
||||
if s.kqFD.close() != 0: raiseOSError(osLastError())
|
||||
|
||||
proc select*(s: var Selector, timeout: int): seq[ReadyInfo] =
|
||||
result = @[]
|
||||
var tv = Timespec(tv_sec: timeout.Time, tv_nsec: 0)
|
||||
let evNum = kevent(s.kqFD, nil, 0, addr s.events[0], 64.cint, addr tv)
|
||||
if evNum < 0:
|
||||
let err = osLastError()
|
||||
if err.cint == EINTR:
|
||||
return @[]
|
||||
raiseOSError(err)
|
||||
if evNum == 0: return @[]
|
||||
for i in 0 .. <evNum:
|
||||
let fd = s.events[i].ident.SocketHandle
|
||||
|
||||
var evSet: set[Event] = {}
|
||||
if (s.events[i].flags and EV_EOF) != 0: evSet = evSet + {EvError}
|
||||
if s.events[i].filter == EVFILT_READ: evSet = evSet + {EvRead}
|
||||
elif s.events[i].filter == EVFILT_WRITE: evSet = evSet + {EvWrite}
|
||||
let selectorKey = s.fds[fd]
|
||||
assert selectorKey.fd != 0.SocketHandle
|
||||
result.add((selectorKey, evSet))
|
||||
|
||||
proc newSelector*(): Selector =
|
||||
result.kqFD = kqueue()
|
||||
if result.kqFD < 0:
|
||||
raiseOSError(osLastError())
|
||||
when MultiThreaded:
|
||||
result.fds = initSharedTable[SocketHandle, SelectorKey]()
|
||||
else:
|
||||
result.fds = initTable[SocketHandle, SelectorKey]()
|
||||
|
||||
proc contains*(s: Selector, fd: SocketHandle): bool =
|
||||
## Determines whether selector contains a file descriptor.
|
||||
s.fds.hasKey(fd) # and s.fds[fd].events != {}
|
||||
|
||||
proc `[]`*(s: Selector, fd: SocketHandle): SelectorKey =
|
||||
## Retrieves the selector key for ``fd``.
|
||||
return s.fds[fd]
|
||||
|
||||
elif not defined(nimdoc):
|
||||
# TODO: kqueue for bsd/mac os x.
|
||||
type
|
||||
|
||||
Reference in New Issue
Block a user