mirror of
https://github.com/nim-lang/Nim.git
synced 2025-12-30 18:02:05 +00:00
* Porting Nim to run on Zephyr.
Includes changes to `std/net`.
Squashed commit of the following:
tweaking more memory / malloc things
revert back bitmasks
tweaking nim to use kernel heap as C malloc doesn't work
fixing socket polling on zephyr
cleanup getting maximum sockets for process or for rtos'es
reorganizing and fixing net for async / system
merge netlite changes back into nativesockets
merge netlite changes back into nativesockets
reverting native sockets back
tweaking nim / zephyr network
adding option to run 'net-lite' from linux
bridging zephyr's max connections
fixing net errors
fixing compilation with getAddrString
fixing compilation with getAddrString
experimenting with a nativesockets_lite ... getAddrString
experimenting with a nativesockets_lite ... getAddrString
experimenting with a nativesockets_lite ... getLocalAddr
experimenting with a nativesockets_lite ... getLocalAddr
experimenting with a nativesockets_lite ...
add note regarding incorrect FreeRTOS Sockadd_in fields
changing to NIM_STATIC_ASSERT
cleaning up the static_assert error messages
cleaning up the static_assert error messages
setting up static assert ftw!
testing compile time asserts
reworking Sockaddr objects to more closely match various platforms
reworking Sockaddr objects to more closely match various platforms
reworking Sockaddr objects to more closely match various platforms
finding missing items (issue #18684)
fixup posix constants (issue #18684)
adding plumbing for zephyr os (issue #18684)
adding plumbing for zephyr os (issue #18684)
* fixing constant capitalizations
* remove extra debug prints and fix TSa_Family/cint issue
* remove extra debug prints and fix TSa_Family/cint issue
* Porting Nim to run on Zephyr.
Includes changes to `std/net`.
Squashed commit of the following:
tweaking more memory / malloc things
revert back bitmasks
tweaking nim to use kernel heap as C malloc doesn't work
fixing socket polling on zephyr
cleanup getting maximum sockets for process or for rtos'es
reorganizing and fixing net for async / system
merge netlite changes back into nativesockets
merge netlite changes back into nativesockets
reverting native sockets back
tweaking nim / zephyr network
adding option to run 'net-lite' from linux
bridging zephyr's max connections
fixing net errors
fixing compilation with getAddrString
fixing compilation with getAddrString
experimenting with a nativesockets_lite ... getAddrString
experimenting with a nativesockets_lite ... getAddrString
experimenting with a nativesockets_lite ... getLocalAddr
experimenting with a nativesockets_lite ... getLocalAddr
experimenting with a nativesockets_lite ...
add note regarding incorrect FreeRTOS Sockadd_in fields
changing to NIM_STATIC_ASSERT
cleaning up the static_assert error messages
cleaning up the static_assert error messages
setting up static assert ftw!
testing compile time asserts
reworking Sockaddr objects to more closely match various platforms
reworking Sockaddr objects to more closely match various platforms
reworking Sockaddr objects to more closely match various platforms
finding missing items (issue #18684)
fixup posix constants (issue #18684)
adding plumbing for zephyr os (issue #18684)
adding plumbing for zephyr os (issue #18684)
* fixing constant capitalizations
* remove extra debug prints and fix TSa_Family/cint issue
* remove extra debug prints and fix TSa_Family/cint issue
* fixing PR issues
* Porting Nim to run on Zephyr.
Includes changes to `std/net`.
Squashed commit of the following:
tweaking more memory / malloc things
revert back bitmasks
tweaking nim to use kernel heap as C malloc doesn't work
fixing socket polling on zephyr
cleanup getting maximum sockets for process or for rtos'es
reorganizing and fixing net for async / system
merge netlite changes back into nativesockets
merge netlite changes back into nativesockets
reverting native sockets back
tweaking nim / zephyr network
adding option to run 'net-lite' from linux
bridging zephyr's max connections
fixing net errors
fixing compilation with getAddrString
fixing compilation with getAddrString
experimenting with a nativesockets_lite ... getAddrString
experimenting with a nativesockets_lite ... getAddrString
experimenting with a nativesockets_lite ... getLocalAddr
experimenting with a nativesockets_lite ... getLocalAddr
experimenting with a nativesockets_lite ...
add note regarding incorrect FreeRTOS Sockadd_in fields
changing to NIM_STATIC_ASSERT
cleaning up the static_assert error messages
cleaning up the static_assert error messages
setting up static assert ftw!
testing compile time asserts
reworking Sockaddr objects to more closely match various platforms
reworking Sockaddr objects to more closely match various platforms
reworking Sockaddr objects to more closely match various platforms
finding missing items (issue #18684)
fixup posix constants (issue #18684)
adding plumbing for zephyr os (issue #18684)
adding plumbing for zephyr os (issue #18684)
* fixing constant capitalizations
* remove extra debug prints and fix TSa_Family/cint issue
* remove extra debug prints and fix TSa_Family/cint issue
* Remerge
* fixing constant capitalizations
* remove extra debug prints and fix TSa_Family/cint issue
* remove extra debug prints and fix TSa_Family/cint issue
* fixing PR issues
* fix maxDescriptors on zephyr/freertos
* move maxDescriptors to selector.nim -- fixes compile issue
* change realloc impl on zephyr to match ansi c behavior
* change realloc impl on zephyr to match ansi c behavior
* force compileOnly mode for tlwip
Co-authored-by: Jaremy J. Creechley <jaremy.creechley@wavebaselabs.com>
Co-authored-by: Jaremy Creechley <jaremy.creechley@panthalassa.com>
361 lines
13 KiB
Nim
361 lines
13 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 allows high-level and efficient I/O multiplexing.
|
|
##
|
|
## 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.
|
|
##
|
|
## Supported features: files, sockets, pipes, timers, processes, signals
|
|
## and user events.
|
|
##
|
|
## Fully supported OS: MacOSX, FreeBSD, OpenBSD, NetBSD, Linux (except
|
|
## for Android).
|
|
##
|
|
## Partially supported OS: Windows (only sockets and user events),
|
|
## Solaris (files, sockets, handles and user events).
|
|
## Android (files, sockets, handles and user events).
|
|
##
|
|
## TODO: `/dev/poll`, `event ports` and filesystem events.
|
|
|
|
import os, nativesockets
|
|
|
|
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(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.
|
|
|
|
const bsdPlatform = defined(macosx) or defined(freebsd) or
|
|
defined(netbsd) or defined(openbsd) or
|
|
defined(dragonfly)
|
|
|
|
when defined(nimdoc):
|
|
type
|
|
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
|
|
Write, ## Descriptor is available for write
|
|
Timer, ## Timer descriptor is completed
|
|
Signal, ## Signal is raised
|
|
Process, ## Process is finished
|
|
Vnode, ## BSD specific file change
|
|
User, ## User event is raised
|
|
Error, ## Error occurred while waiting for descriptor
|
|
VnodeWrite, ## NOTE_WRITE (BSD specific, write to file occurred)
|
|
VnodeDelete, ## NOTE_DELETE (BSD specific, unlink of file occurred)
|
|
VnodeExtend, ## NOTE_EXTEND (BSD specific, file extended)
|
|
VnodeAttrib, ## NOTE_ATTRIB (BSD specific, file attributes changed)
|
|
VnodeLink, ## NOTE_LINK (BSD specific, file link count changed)
|
|
VnodeRename, ## NOTE_RENAME (BSD specific, file renamed)
|
|
VnodeRevoke ## NOTE_REVOKE (BSD specific, file revoke occurred)
|
|
|
|
ReadyKey* = object
|
|
## An object which holds result for descriptor
|
|
fd* : int ## file/socket descriptor
|
|
events*: set[Event] ## set of events
|
|
errorCode*: OSErrorCode ## additional error code information for
|
|
## Error events
|
|
|
|
SelectEvent* = object
|
|
## An object which holds user defined event
|
|
|
|
proc newSelector*[T](): Selector[T] =
|
|
## Creates a new selector
|
|
|
|
proc close*[T](s: Selector[T]) =
|
|
## Closes the selector.
|
|
|
|
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
|
|
## 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`.
|
|
|
|
proc registerTimer*[T](s: Selector[T], timeout: int, oneshot: bool,
|
|
data: T): int {.discardable.} =
|
|
## Registers timer notification with `timeout` (in milliseconds)
|
|
## to selector `s`.
|
|
##
|
|
## If `oneshot` is `true`, timer will be notified only once.
|
|
##
|
|
## Set `oneshot` to `false` if you want periodic notifications.
|
|
##
|
|
## 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`.
|
|
##
|
|
## 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`.
|
|
|
|
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`.
|
|
##
|
|
## 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`.
|
|
##
|
|
## 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
|
|
## vnode event happens.
|
|
##
|
|
## **Note:** This function is supported only by BSD and MacOSX.
|
|
|
|
proc newSelectEvent*(): SelectEvent =
|
|
## Creates a new user-defined event.
|
|
|
|
proc trigger*(ev: SelectEvent) =
|
|
## Trigger event `ev`.
|
|
|
|
proc close*(ev: SelectEvent) =
|
|
## Closes user-defined event `ev`.
|
|
|
|
proc unregister*[T](s: Selector[T], ev: SelectEvent) =
|
|
## 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`.
|
|
|
|
proc selectInto*[T](s: Selector[T], timeout: int,
|
|
results: var openArray[ReadyKey]): int =
|
|
## Waits for events registered in selector `s`.
|
|
##
|
|
## 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.
|
|
##
|
|
## Returns number of triggered events.
|
|
|
|
proc select*[T](s: Selector[T], timeout: int): seq[ReadyKey] =
|
|
## Waits for events registered in selector `s`.
|
|
##
|
|
## 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.
|
|
##
|
|
## 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
|
|
## will be returned.
|
|
|
|
proc setData*[T](s: Selector[T], fd: SocketHandle|int, data: var T): bool =
|
|
## Associate application-defined `data` with descriptor `fd`.
|
|
##
|
|
## 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
|
|
## 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
|
|
##
|
|
## s.withData(fd, value) do:
|
|
## # 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
|
|
##
|
|
## s.withData(fd, value) do:
|
|
## # block is executed only if `fd` registered in selector `s`.
|
|
## value.uid = 1000
|
|
## do:
|
|
## # 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.
|
|
|
|
proc getFd*[T](s: Selector[T]): int =
|
|
## Retrieves the underlying selector's file descriptor.
|
|
##
|
|
## For *poll* and *select* selectors `-1` is returned.
|
|
|
|
else:
|
|
import strutils
|
|
when hasThreadSupport:
|
|
import locks
|
|
|
|
type
|
|
SharedArray[T] = UncheckedArray[T]
|
|
|
|
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 deallocSharedArray[T](sa: ptr SharedArray[T]) =
|
|
deallocShared(cast[pointer](sa))
|
|
type
|
|
Event* {.pure.} = enum
|
|
Read, Write, Timer, Signal, Process, Vnode, User, Error, Oneshot,
|
|
Finished, VnodeWrite, VnodeDelete, VnodeExtend, VnodeAttrib, VnodeLink,
|
|
VnodeRename, VnodeRevoke
|
|
|
|
type
|
|
IOSelectorsException* = object of CatchableError
|
|
|
|
ReadyKey* = object
|
|
fd*: int
|
|
events*: set[Event]
|
|
errorCode*: OSErrorCode
|
|
|
|
SelectorKey[T] = object
|
|
ident: int
|
|
events: set[Event]
|
|
param: int
|
|
data: T
|
|
|
|
const
|
|
InvalidIdent = -1
|
|
|
|
proc raiseIOSelectorsError[T](message: T) =
|
|
var msg = ""
|
|
when T is string:
|
|
msg.add(message)
|
|
elif T is OSErrorCode:
|
|
msg.add(osErrorMsg(message) & " (code: " & $int(message) & ")")
|
|
else:
|
|
msg.add("Internal Error\n")
|
|
var err = newException(IOSelectorsException, msg)
|
|
raise err
|
|
|
|
proc setNonBlocking(fd: cint) {.inline.} =
|
|
setBlocking(fd.SocketHandle, false)
|
|
|
|
when not defined(windows):
|
|
import 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 = pdata
|
|
|
|
when ioselSupportedPlatform:
|
|
template blockSignals(newmask: var Sigset, oldmask: var Sigset) =
|
|
when hasThreadSupport:
|
|
if posix.pthread_sigmask(SIG_BLOCK, newmask, oldmask) == -1:
|
|
raiseIOSelectorsError(osLastError())
|
|
else:
|
|
if posix.sigprocmask(SIG_BLOCK, newmask, oldmask) == -1:
|
|
raiseIOSelectorsError(osLastError())
|
|
|
|
template unblockSignals(newmask: var Sigset, oldmask: var Sigset) =
|
|
when hasThreadSupport:
|
|
if posix.pthread_sigmask(SIG_UNBLOCK, newmask, oldmask) == -1:
|
|
raiseIOSelectorsError(osLastError())
|
|
else:
|
|
if posix.sigprocmask(SIG_UNBLOCK, newmask, oldmask) == -1:
|
|
raiseIOSelectorsError(osLastError())
|
|
|
|
template clearKey[T](key: ptr SelectorKey[T]) =
|
|
var empty: T
|
|
key.ident = InvalidIdent
|
|
key.events = {}
|
|
key.data = empty
|
|
|
|
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) or defined(windows) or defined(macosx) or defined(bsd) or
|
|
defined(zephyr) or defined(freertos):
|
|
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.
|
|
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(linux) and not defined(emscripten):
|
|
include ioselects/ioselectors_epoll
|
|
elif bsdPlatform:
|
|
include ioselects/ioselectors_kqueue
|
|
elif defined(windows):
|
|
include ioselects/ioselectors_select
|
|
elif defined(solaris):
|
|
include ioselects/ioselectors_poll # need to replace it with event ports
|
|
elif defined(genode):
|
|
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
|
|
else:
|
|
include ioselects/ioselectors_poll
|