nim-chronos/chronos/ioselects/ioselectors_poll.nim

344 lines
10 KiB
Nim

#
#
# Nim's Runtime Library
# (c) Copyright 2016 Eugene Kabanov
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
# This module implements Posix poll().
import std/tables
import stew/base10
when (NimMajor, NimMinor) < (1, 4):
{.push raises: [Defect].}
else:
{.push raises: [].}
type
SelectorImpl[T] = object
fds: Table[int32, SelectorKey[T]]
pollfds: seq[TPollFd]
Selector*[T] = ref SelectorImpl[T]
type
SelectEventImpl = object
rfd: cint
wfd: cint
SelectEvent* = ptr SelectEventImpl
proc toString(key: int32): string =
Base10.toString(uint32(key))
template addKey[T](s: Selector[T], key: int32, skey: SelectorKey[T]) =
if s.fds.hasKeyOrPut(key, skey):
raiseAssert "Descriptor [" & key.toString() &
"] is already registered in the selector!"
template getKey[T](s: Selector[T], key: int32): SelectorKey[T] =
let
defaultKey = SelectorKey[T](ident: InvalidIdent)
pkey = s.fds.getOrDefault(key, defaultKey)
doAssert(pkey.ident != InvalidIdent,
"Descriptor [" & key.toString() &
"] is not registered in the selector!")
pkey
template checkKey[T](s: Selector[T], key: int32): bool =
s.fds.contains(key)
proc freeKey[T](s: Selector[T], key: int32) =
s.fds.del(key)
proc new*(t: typedesc[Selector], T: typedesc): SelectResult[Selector[T]] =
let selector = Selector[T](
fds: initTable[int32, SelectorKey[T]](asyncInitialSize)
)
ok(selector)
proc close2*[T](s: Selector[T]): SelectResult[void] =
s.fds.clear()
s.pollfds.clear()
proc new*(t: typedesc[SelectEvent]): SelectResult[SelectEvent] =
let flags = {DescriptorFlag.NonBlock, DescriptorFlag.CloseOnExec}
let pipes = ? createOsPipe(flags, flags)
var res = cast[SelectEvent](allocShared0(sizeof(SelectEventImpl)))
res.rfd = pipes.read
res.wfd = pipes.write
ok(res)
proc trigger2*(event: SelectEvent): SelectResult[void] =
var data: uint64 = 1
let res = handleEintr(osdefs.write(event.wfd, addr data, sizeof(uint64)))
if res == -1:
err(osLastError())
elif res != sizeof(uint64):
err(OSErrorCode(osdefs.EINVAL))
else:
ok()
proc close2*(event: SelectEvent): SelectResult[void] =
let
rfd = event.rfd
wfd = event.wfd
deallocShared(cast[pointer](event))
let rres = handleEintr(osdefs.close(rfd))
if rres == -1:
discard osdefs.close(wfd)
return err(osLastError())
let wres = handleEintr(osdefs.close(wfd))
if wres == -1:
err(osLastError())
else:
ok()
template toPollEvents(events: set[Event]): cshort =
var res = cshort(0)
if Event.Read in events: res = res or POLLIN
if Event.Write in events: res = res or POLLOUT
res
template pollAdd[T](s: Selector[T], sock: cint, events: set[Event]) =
s.pollfds.add(TPollFd(fd: sock, events: toPollEvents(events), revents: 0))
template pollUpdate[T](s: Selector[T], sock: cint, events: set[Event]) =
var updated = false
for mitem in s.pollfds.mitems():
if mitem.fd == sock:
mitem.events = toPollEvents(events)
break
if not(updated):
raiseAssert "Descriptor [" & $sock & "] is not registered in the queue!"
template pollRemove[T](s: Selector[T], sock: cint) =
let index =
block:
var res = -1
for key, item in s.pollfds.pairs():
if item.fd == sock:
res = key
break
res
if index < 0:
raiseAssert "Descriptor [" & $sock & "] is not registered in the queue!"
else:
s.pollfds.del(index)
proc registerHandle2*[T](s: Selector[T], fd: cint, events: set[Event],
data: T): SelectResult[void] =
let skey = SelectorKey[T](ident: fd, events: events, param: 0, data: data)
s.addKey(fd, skey)
if events != {}:
s.pollAdd(fd, events)
ok()
proc updateHandle2*[T](s: Selector[T], fd: cint,
events: set[Event]): SelectResult[void] =
const EventsMask = {Event.Timer, Event.Signal, Event.Process, Event.Vnode,
Event.User, Event.Oneshot, Event.Error}
s.fds.withValue(int32(fd), pkey):
doAssert(pkey[].events * EventsMask == {},
"Descriptor [" & fd.toString() & "] could not be updated!")
if pkey[].events != events:
if pkey[].events == {}:
s.pollAdd(fd, events)
else:
if events != {}:
s.pollUpdate(fd, events)
else:
s.pollRemove(fd)
pkey.events = events
do:
raiseAssert "Descriptor [" & fd.toString() &
"] is not registered in the selector!"
ok()
proc registerEvent2*[T](s: Selector[T], ev: SelectEvent,
data: T): SelectResult[cint] =
doAssert(not(isNil(ev)))
let
key = SelectorKey[T](ident: ev.rfd, events: {Event.User},
param: 0, data: data)
s.addKey(ev.rfd, key)
s.pollAdd(ev.rfd, {Event.Read}.toPollEvents())
ok(ev.rfd)
proc unregister2*[T](s: Selector[T], fd: cint): SelectResult[void] =
let pkey = s.getKey(fd)
if pkey.events != {}:
if {Event.Read, Event.Write, Event.User} * pkey.events != {}:
s.pollRemove(fd)
s.freeKey(fd)
ok()
proc unregister2*[T](s: Selector[T], event: SelectEvent): SelectResult[void] =
s.unregister2(event.rfd)
proc prepareKey[T](s: Selector[T], event: var TPollfd): Opt[ReadyKey] =
let
defaultKey = SelectorKey[T](ident: InvalidIdent)
fdi32 = int32(event.fd)
revents = event.revents
var
pkey = s.getKey(fdi32)
rkey = ReadyKey(fd: event.fd)
# Cleanup all the received events.
event.revents = 0
if (revents and POLLIN) != 0:
if Event.User in pkey.events:
var data: uint64 = 0
let res = handleEintr(osdefs.read(event.fd, addr data, sizeof(uint64)))
if res != sizeof(uint64):
let errorCode = osLastError()
if errorCode == EAGAIN:
return Opt.none(ReadyKey)
else:
rkey.events.incl({Event.User, Event.Error})
rkey.errorCode = errorCode
else:
rkey.events.incl(Event.User)
else:
rkey.events.incl(Event.Read)
if (revents and POLLOUT) != 0:
rkey.events.incl(Event.Write)
if (revents and POLLERR) != 0 or (revents and POLLHUP) != 0 or
(revents and POLLNVAL) != 0:
rkey.events.incl(Event.Error)
ok(rkey)
proc selectInto2*[T](s: Selector[T], timeout: int,
readyKeys: var openArray[ReadyKey]): SelectResult[int] =
var k = 0
verifySelectParams(timeout, -1, int(high(cint)))
let
maxEventsCount = min(len(s.pollfds), len(readyKeys))
eventsCount =
if maxEventsCount > 0:
let res = handleEintr(poll(addr(s.pollfds[0]), Tnfds(maxEventsCount),
timeout))
if res < 0:
return err(osLastError())
res
else:
0
for i in 0 ..< len(s.pollfds):
if s.pollfds[i].revents != 0:
let rkey = s.prepareKey(s.pollfds[i]).valueOr: continue
readyKeys[k] = rkey
inc(k)
if k == eventsCount: break
ok(k)
proc select2*[T](s: Selector[T], timeout: int): SelectResult[seq[ReadyKey]] =
var res = newSeq[ReadyKey](asyncEventsCount)
let count = ? selectInto2(s, timeout, res)
res.setLen(count)
ok(res)
proc newSelector*[T](): Selector[T] {.
raises: [Defect, OSError].} =
let res = Selector.new(T)
if res.isErr(): raiseOSError(res.error)
res.get()
proc close*[T](s: Selector[T]) {.
raises: [Defect, IOSelectorsException].} =
let res = s.close2()
if res.isErr(): raiseIOSelectorsError(res.error())
proc newSelectEvent*(): SelectEvent {.
raises: [Defect, IOSelectorsException].} =
let res = SelectEvent.new()
if res.isErr(): raiseIOSelectorsError(res.error())
res.get()
proc trigger*(event: SelectEvent) {.
raises: [Defect, IOSelectorsException].} =
let res = event.trigger2()
if res.isErr(): raiseIOSelectorsError(res.error())
proc close*(event: SelectEvent) {.
raises: [Defect, IOSelectorsException].} =
let res = event.close2()
if res.isErr(): raiseIOSelectorsError(res.error())
proc registerHandle*[T](s: Selector[T], fd: cint | SocketHandle,
events: set[Event], data: T) {.
raises: [Defect, IOSelectorsException].} =
let res = registerHandle2(s, cint(fd), events, data)
if res.isErr(): raiseIOSelectorsError(res.error())
proc updateHandle*[T](s: Selector[T], fd: cint | SocketHandle,
events: set[Event]) {.
raises: [Defect, IOSelectorsException].} =
let res = updateHandle2(s, cint(fd), events)
if res.isErr(): raiseIOSelectorsError(res.error())
proc unregister*[T](s: Selector[T], fd: cint | SocketHandle) {.
raises: [Defect, IOSelectorsException].} =
let res = unregister2(s, cint(fd))
if res.isErr(): raiseIOSelectorsError(res.error())
proc unregister*[T](s: Selector[T], event: SelectEvent) {.
raises: [Defect, IOSelectorsException].} =
let res = unregister2(s, event)
if res.isErr(): raiseIOSelectorsError(res.error())
proc registerEvent*[T](s: Selector[T], event: SelectEvent,
data: T) {.
raises: [Defect, IOSelectorsException].} =
let res = registerEvent2(s, event, data)
if res.isErr(): raiseIOSelectorsError(res.error())
proc selectInto*[T](s: Selector[T], timeout: int,
readyKeys: var openArray[ReadyKey]): int {.
raises: [Defect, IOSelectorsException].} =
let res = selectInto2(s, timeout, readyKeys)
if res.isErr(): raiseIOSelectorsError(res.error())
res.get()
proc select*[T](s: Selector[T], timeout: int): seq[ReadyKey] =
let res = select2(s, timeout)
if res.isErr(): raiseIOSelectorsError(res.error())
res.get()
proc contains*[T](s: Selector[T], fd: SocketHandle|cint): bool {.inline.} =
s.checkKey(int32(fd))
proc setData*[T](s: Selector[T], fd: SocketHandle|cint, data: T): bool =
s.fds.withValue(int32(fd), skey):
skey[].data = data
return true
do:
return false
template withData*[T](s: Selector[T], fd: SocketHandle|cint, value,
body: untyped) =
s.fds.withValue(int32(fd), skey):
var value = addr(skey[].data)
body
template withData*[T](s: Selector[T], fd: SocketHandle|cint, value, body1,
body2: untyped) =
s.fds.withValue(int32(fd), skey):
var value = addr(skey[].data)
body1
do:
body2
proc getFd*[T](s: Selector[T]): int = -1