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:
Dominik Picheta
2015-09-29 23:28:13 +01:00
2 changed files with 155 additions and 10 deletions

71
lib/posix/kqueue.nim Normal file
View 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.

View File

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