# # # Nim's Runtime Library # (c) Copyright 2014 Dominik Picheta # # See the file "copying.txt", included in this # distribution, for details about the copyright. # # TODO: Docs. import tables, os, unsigned, hashes when defined(linux): import posix, epoll elif defined(windows): import winlean else: import posix proc hash*(x: SocketHandle): THash {.borrow.} proc `$`*(x: SocketHandle): string {.borrow.} type Event* = enum EvRead, EvWrite SelectorKey* = ref object fd*: SocketHandle events*: set[Event] ## The events which ``fd`` listens for. data*: RootRef ## User object. ReadyInfo* = tuple[key: SelectorKey, events: set[Event]] when defined(nimdoc): type Selector* = ref object ## An object which holds file descripters to be checked for read/write ## status. fds: Table[SocketHandle, SelectorKey] proc register*(s: Selector, fd: SocketHandle, events: set[Event], data: RootRef): SelectorKey {.discardable.} = ## Registers file descriptor ``fd`` to selector ``s`` with a set of TEvent ## ``events``. proc update*(s: Selector, fd: SocketHandle, events: set[Event]): SelectorKey {.discardable.} = ## Updates the events which ``fd`` wants notifications for. proc select*(s: 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``. proc contains*(s: Selector, fd: SocketHandle): bool = ## Determines whether selector contains a file descriptor. proc `[]`*(s: Selector, fd: SocketHandle): SelectorKey = ## Retrieves the selector key for ``fd``. elif defined(linux): type Selector* = ref object epollFD: cint events: array[64, epoll_event] fds: Table[SocketHandle, SelectorKey] proc createEventStruct(events: set[Event], fd: SocketHandle): epoll_event = if EvRead in events: result.events = EPOLLIN if EvWrite in events: result.events = result.events or EPOLLOUT result.events = result.events or EPOLLRDHUP result.data.fd = fd.cint proc register*(s: Selector, fd: SocketHandle, events: set[Event], data: RootRef): SelectorKey {.discardable.} = ## Registers file descriptor ``fd`` to selector ``s`` with a set of TEvent ## ``events``. 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 result = key proc update*(s: Selector, fd: SocketHandle, events: set[Event]): SelectorKey {.discardable.} = ## Updates the events which ``fd`` wants notifications for. if s.fds[fd].events != events: if events == {}: # This fd is idle -- it should not be registered to epoll. # But it should remain a part of this selector instance. # This is to prevent epoll_wait from returning immediately # because its got fds which are waiting for no events and # are therefore constantly ready. (leading to 100% CPU usage). if epoll_ctl(s.epollFD, EPOLL_CTL_DEL, fd, nil) != 0: raiseOSError(osLastError()) s.fds[fd].events = events else: var event = createEventStruct(events, fd) if s.fds[fd].events == {}: # This fd is idle. It's not a member of this epoll instance and must # be re-registered. if epoll_ctl(s.epollFD, EPOLL_CTL_ADD, fd, addr(event)) != 0: raiseOSError(osLastError()) else: if epoll_ctl(s.epollFD, EPOLL_CTL_MOD, fd, addr(event)) != 0: raiseOSError(osLastError()) s.fds[fd].events = events result = s.fds[fd] proc unregister*(s: Selector, fd: SocketHandle): SelectorKey {.discardable.} = if epoll_ctl(s.epollFD, EPOLL_CTL_DEL, fd, nil) != 0: let err = osLastError() if err.cint notin {ENOENT, EBADF}: # TODO: Why do we sometimes get an EBADF? Is this normal? raiseOSError(err) result = s.fds[fd] s.fds.del(fd) proc close*(s: Selector) = if s.epollFD.close() != 0: raiseOSError(osLastError()) dealloc(addr s.events) # TODO: Test this proc epollHasFd(s: Selector, fd: SocketHandle): bool = result = true var event = createEventStruct(s.fds[fd].events, fd) if epoll_ctl(s.epollFD, EPOLL_CTL_MOD, fd, addr(event)) != 0: let err = osLastError() if err.cint in {ENOENT, EBADF}: return false raiseOSError(osLastError()) proc select*(s: 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: raiseOSError(osLastError()) if evNum == 0: return @[] for i in 0 .. 0: echo ready[0].events i.inc if i == 6: assert selector.unregister(sock.getFD).fd == sock.getFD selector.close() break