diff options
Diffstat (limited to 'lib/pure/selectors.nim')
-rw-r--r-- | lib/pure/selectors.nim | 181 |
1 files changed, 110 insertions, 71 deletions
diff --git a/lib/pure/selectors.nim b/lib/pure/selectors.nim index 12a134ef8..ac180e2bd 100644 --- a/lib/pure/selectors.nim +++ b/lib/pure/selectors.nim @@ -9,11 +9,11 @@ ## This module allows high-level and efficient I/O multiplexing. ## -## Supported OS primitives: ``epoll``, ``kqueue``, ``poll`` and -## Windows ``select``. +## Supported OS primitives: `epoll`, `kqueue`, `poll` and +## Windows `select`. ## ## To use threadsafe version of this module, it needs to be compiled -## with both ``-d:threadsafe`` and ``--threads:on`` options. +## with both `-d:threadsafe` and `--threads:on` options. ## ## Supported features: files, sockets, pipes, timers, processes, signals ## and user events. @@ -25,18 +25,22 @@ ## Solaris (files, sockets, handles and user events). ## Android (files, sockets, handles and user events). ## -## TODO: ``/dev/poll``, ``event ports`` and filesystem events. +## TODO: `/dev/poll`, `event ports` and filesystem events. -import os, nativesockets +import std/nativesockets +import std/oserrors + +when defined(nimPreviewSlimSystem): + import std/assertions const hasThreadSupport = compileOption("threads") and defined(threadsafe) const ioselSupportedPlatform* = defined(macosx) or defined(freebsd) or defined(netbsd) or defined(openbsd) or - defined(dragonfly) or + defined(dragonfly) or defined(nuttx) or (defined(linux) and not defined(android) and not defined(emscripten)) ## This constant is used to determine whether the destination platform is - ## fully supported by ``ioselectors`` module. + ## fully supported by `ioselectors` module. const bsdPlatform = defined(macosx) or defined(freebsd) or defined(netbsd) or defined(openbsd) or @@ -47,6 +51,9 @@ when defined(nimdoc): Selector*[T] = ref object ## An object which holds descriptors to be checked for read/write status + IOSelectorsException* = object of CatchableError + ## Exception that is raised if an IOSelectors error occurs. + Event* {.pure.} = enum ## An enum which hold event types Read, ## Descriptor is available for read @@ -83,62 +90,62 @@ when defined(nimdoc): proc registerHandle*[T](s: Selector[T], fd: int | SocketHandle, events: set[Event], data: T) = - ## Registers file/socket descriptor ``fd`` to selector ``s`` - ## with events set in ``events``. The ``data`` is application-defined + ## Registers file/socket descriptor `fd` to selector `s` + ## with events set in `events`. The `data` is application-defined ## data, which will be passed when an event is triggered. proc updateHandle*[T](s: Selector[T], fd: int | SocketHandle, events: set[Event]) = - ## Update file/socket descriptor ``fd``, registered in selector - ## ``s`` with new events set ``event``. + ## Update file/socket descriptor `fd`, registered in selector + ## `s` with new events set `event`. proc registerTimer*[T](s: Selector[T], timeout: int, oneshot: bool, data: T): int {.discardable.} = - ## Registers timer notification with ``timeout`` (in milliseconds) - ## to selector ``s``. + ## Registers timer notification with `timeout` (in milliseconds) + ## to selector `s`. ## - ## If ``oneshot`` is ``true``, timer will be notified only once. + ## If `oneshot` is `true`, timer will be notified only once. ## - ## Set ``oneshot`` to ``false`` if you want periodic notifications. + ## Set `oneshot` to `false` if you want periodic notifications. ## - ## The ``data`` is application-defined data, which will be passed, when + ## The `data` is application-defined data, which will be passed, when ## the timer is triggered. ## ## Returns the file descriptor for the registered timer. proc registerSignal*[T](s: Selector[T], signal: int, data: T): int {.discardable.} = - ## Registers Unix signal notification with ``signal`` to selector - ## ``s``. + ## Registers Unix signal notification with `signal` to selector + ## `s`. ## - ## The ``data`` is application-defined data, which will be + ## The `data` is application-defined data, which will be ## passed when signal raises. ## ## Returns the file descriptor for the registered signal. ## - ## **Note:** This function is not supported on ``Windows``. + ## **Note:** This function is not supported on `Windows`. proc registerProcess*[T](s: Selector[T], pid: int, data: T): int {.discardable.} = ## Registers a process id (pid) notification (when process has - ## exited) in selector ``s``. + ## exited) in selector `s`. ## - ## The ``data`` is application-defined data, which will be passed when - ## process with ``pid`` has exited. + ## The `data` is application-defined data, which will be passed when + ## process with `pid` has exited. ## ## Returns the file descriptor for the registered signal. proc registerEvent*[T](s: Selector[T], ev: SelectEvent, data: T) = - ## Registers selector event ``ev`` in selector ``s``. + ## Registers selector event `ev` in selector `s`. ## - ## The ``data`` is application-defined data, which will be passed when - ## ``ev`` happens. + ## The `data` is application-defined data, which will be passed when + ## `ev` happens. proc registerVnode*[T](s: Selector[T], fd: cint, events: set[Event], data: T) = ## Registers selector BSD/MacOSX specific vnode events for file - ## descriptor ``fd`` and events ``events``. - ## ``data`` application-defined data, which to be passed, when + ## descriptor `fd` and events `events`. + ## `data` application-defined data, which to be passed, when ## vnode event happens. ## ## **Note:** This function is supported only by BSD and MacOSX. @@ -147,79 +154,77 @@ when defined(nimdoc): ## Creates a new user-defined event. proc trigger*(ev: SelectEvent) = - ## Trigger event ``ev``. + ## Trigger event `ev`. proc close*(ev: SelectEvent) = - ## Closes user-defined event ``ev``. + ## Closes user-defined event `ev`. proc unregister*[T](s: Selector[T], ev: SelectEvent) = - ## Unregisters user-defined event ``ev`` from selector ``s``. + ## Unregisters user-defined event `ev` from selector `s`. proc unregister*[T](s: Selector[T], fd: int|SocketHandle|cint) = - ## Unregisters file/socket descriptor ``fd`` from selector ``s``. + ## Unregisters file/socket descriptor `fd` from selector `s`. proc selectInto*[T](s: Selector[T], timeout: int, - results: var openarray[ReadyKey]): int = - ## Waits for events registered in selector ``s``. + results: var openArray[ReadyKey]): int = + ## Waits for events registered in selector `s`. ## - ## The ``timeout`` argument specifies the maximum number of milliseconds + ## The `timeout` argument specifies the maximum number of milliseconds ## the function will be blocked for if no events are ready. Specifying a - ## timeout of ``-1`` causes the function to block indefinitely. - ## All available events will be stored in ``results`` array. + ## timeout of `-1` causes the function to block indefinitely. + ## All available events will be stored in `results` array. ## ## Returns number of triggered events. proc select*[T](s: Selector[T], timeout: int): seq[ReadyKey] = - ## Waits for events registered in selector ``s``. + ## Waits for events registered in selector `s`. ## - ## The ``timeout`` argument specifies the maximum number of milliseconds + ## The `timeout` argument specifies the maximum number of milliseconds ## the function will be blocked for if no events are ready. Specifying a - ## timeout of ``-1`` causes the function to block indefinitely. + ## timeout of `-1` causes the function to block indefinitely. ## ## Returns a list of triggered events. proc getData*[T](s: Selector[T], fd: SocketHandle|int): var T = - ## Retrieves application-defined ``data`` associated with descriptor ``fd``. - ## If specified descriptor ``fd`` is not registered, empty/default value + ## Retrieves application-defined `data` associated with descriptor `fd`. + ## If specified descriptor `fd` is not registered, empty/default value ## will be returned. proc setData*[T](s: Selector[T], fd: SocketHandle|int, data: var T): bool = - ## Associate application-defined ``data`` with descriptor ``fd``. + ## Associate application-defined `data` with descriptor `fd`. ## - ## Returns ``true``, if data was successfully updated, ``false`` otherwise. + ## Returns `true`, if data was successfully updated, `false` otherwise. template isEmpty*[T](s: Selector[T]): bool = # TODO: Why is this a template? - ## Returns ``true``, if there are no registered events or descriptors + ## Returns `true`, if there are no registered events or descriptors ## in selector. template withData*[T](s: Selector[T], fd: SocketHandle|int, value, body: untyped) = - ## Retrieves the application-data assigned with descriptor ``fd`` - ## to ``value``. This ``value`` can be modified in the scope of - ## the ``withData`` call. - ## - ## .. code-block:: nim + ## Retrieves the application-data assigned with descriptor `fd` + ## to `value`. This `value` can be modified in the scope of + ## the `withData` call. ## + ## ```nim ## s.withData(fd, value) do: - ## # block is executed only if ``fd`` registered in selector ``s`` + ## # block is executed only if `fd` registered in selector `s` ## value.uid = 1000 - ## + ## ``` template withData*[T](s: Selector[T], fd: SocketHandle|int, value, body1, body2: untyped) = - ## Retrieves the application-data assigned with descriptor ``fd`` - ## to ``value``. This ``value`` can be modified in the scope of - ## the ``withData`` call. - ## - ## .. code-block:: nim + ## Retrieves the application-data assigned with descriptor `fd` + ## to `value`. This `value` can be modified in the scope of + ## the `withData` call. ## + ## ```nim ## s.withData(fd, value) do: - ## # block is executed only if ``fd`` registered in selector ``s``. + ## # block is executed only if `fd` registered in selector `s`. ## value.uid = 1000 ## do: - ## # block is executed if ``fd`` not registered in selector ``s``. + ## # block is executed if `fd` not registered in selector `s`. ## raise - ## + ## ``` proc contains*[T](s: Selector[T], fd: SocketHandle|int): bool {.inline.} = ## Determines whether selector contains a file descriptor. @@ -227,12 +232,12 @@ when defined(nimdoc): proc getFd*[T](s: Selector[T]): int = ## Retrieves the underlying selector's file descriptor. ## - ## For *poll* and *select* selectors ``-1`` is returned. + ## For *poll* and *select* selectors `-1` is returned. else: - import strutils + import std/strutils when hasThreadSupport: - import locks + import std/locks type SharedArray[T] = UncheckedArray[T] @@ -240,8 +245,8 @@ else: proc allocSharedArray[T](nsize: int): ptr SharedArray[T] = result = cast[ptr SharedArray[T]](allocShared0(sizeof(T) * nsize)) - proc reallocSharedArray[T](sa: ptr SharedArray[T], nsize: int): ptr SharedArray[T] = - result = cast[ptr SharedArray[T]](reallocShared(sa, sizeof(T) * nsize)) + proc reallocSharedArray[T](sa: ptr SharedArray[T], oldsize, nsize: int): ptr SharedArray[T] = + result = cast[ptr SharedArray[T]](reallocShared0(sa, oldsize * sizeof(T), sizeof(T) * nsize)) proc deallocSharedArray[T](sa: ptr SharedArray[T]) = deallocShared(cast[pointer](sa)) @@ -255,7 +260,7 @@ else: IOSelectorsException* = object of CatchableError ReadyKey* = object - fd* : int + fd*: int events*: set[Event] errorCode*: OSErrorCode @@ -283,14 +288,14 @@ else: setBlocking(fd.SocketHandle, false) when not defined(windows): - import posix + import std/posix template setKey(s, pident, pevents, pparam, pdata: untyped) = var skey = addr(s.fds[pident]) skey.ident = pident skey.events = pevents skey.param = pparam - skey.data = data + skey.data = pdata when ioselSupportedPlatform: template blockSignals(newmask: var Sigset, oldmask: var Sigset) = @@ -318,9 +323,37 @@ else: proc verifySelectParams(timeout: int) = # Timeout of -1 means: wait forever # Anything higher is the time to wait in milliseconds. - doAssert(timeout >= -1, "Cannot select with a negative value, got " & $timeout) - - when defined(linux) and not defined(emscripten): + doAssert(timeout >= -1, "Cannot select with a negative value, got: " & $timeout) + + when defined(linux) or defined(windows) or defined(macosx) or defined(bsd) or + defined(solaris) or defined(zephyr) or defined(freertos) or defined(nuttx) or defined(haiku): + template maxDescriptors*(): int = + ## Returns the maximum number of active file descriptors for the current + ## process. This involves a system call. For now `maxDescriptors` is + ## supported on the following OSes: Windows, Linux, OSX, BSD, Solaris. + when defined(windows): + 16_700_000 + elif defined(zephyr) or defined(freertos): + FD_MAX + else: + var fdLim: RLimit + var res = int(getrlimit(RLIMIT_NOFILE, fdLim)) + if res >= 0: + res = int(fdLim.rlim_cur) - 1 + res + + when defined(nimIoselector): + when nimIoselector == "epoll": + include ioselects/ioselectors_epoll + elif nimIoselector == "kqueue": + include ioselects/ioselectors_kqueue + elif nimIoselector == "poll": + include ioselects/ioselectors_poll + elif nimIoselector == "select": + include ioselects/ioselectors_select + else: + {.fatal: "Unknown nimIoselector specified by define.".} + elif defined(linux) and not defined(emscripten): include ioselects/ioselectors_epoll elif bsdPlatform: include ioselects/ioselectors_kqueue @@ -332,5 +365,11 @@ else: include ioselects/ioselectors_select # TODO: use the native VFS layer elif defined(nintendoswitch): include ioselects/ioselectors_select + elif defined(freertos) or defined(lwip): + include ioselects/ioselectors_select + elif defined(zephyr): + include ioselects/ioselectors_poll + elif defined(nuttx): + include ioselects/ioselectors_epoll else: include ioselects/ioselectors_poll |