epoll selector starts with reasonable fd set size (1024) and increases in powers of two when needed. This prevents the selector to allocate large amounts of memory at startup on systems with a high RLIMIT_NOFILE setting (#10194)

This commit is contained in:
Ico Doornekamp 2019-01-09 10:46:23 +01:00 • committed by Andreas Rumpf
commit 0229dfd199
2 changed files with 22 additions and 3 deletions

View file

@ -53,6 +53,7 @@ when hasThreadSupport:
SelectorImpl[T] = object SelectorImpl[T] = object
epollFD: cint epollFD: cint
maxFD: int maxFD: int
numFD: int
fds: ptr SharedArray[SelectorKey[T]] fds: ptr SharedArray[SelectorKey[T]]
count: int count: int
Selector*[T] = ptr SelectorImpl[T] Selector*[T] = ptr SelectorImpl[T]
@ -61,6 +62,7 @@ else:
SelectorImpl[T] = object SelectorImpl[T] = object
epollFD: cint epollFD: cint
maxFD: int maxFD: int
numFD: int
fds: seq[SelectorKey[T]] fds: seq[SelectorKey[T]]
count: int count: int
Selector*[T] = ref SelectorImpl[T] Selector*[T] = ref SelectorImpl[T]
@ -76,6 +78,8 @@ proc newSelector*[T](): Selector[T] =
raiseOsError(osLastError()) raiseOsError(osLastError())
var maxFD = int(a.rlim_max) var maxFD = int(a.rlim_max)
doAssert(maxFD > 0) doAssert(maxFD > 0)
# Start with a reasonable size, checkFd() will grow this on demand
const numFD = 1024
var epollFD = epoll_create(MAX_EPOLL_EVENTS) var epollFD = epoll_create(MAX_EPOLL_EVENTS)
if epollFD < 0: if epollFD < 0:
@ -85,14 +89,16 @@ proc newSelector*[T](): Selector[T] =
result = cast[Selector[T]](allocShared0(sizeof(SelectorImpl[T]))) result = cast[Selector[T]](allocShared0(sizeof(SelectorImpl[T])))
result.epollFD = epollFD result.epollFD = epollFD
result.maxFD = maxFD result.maxFD = maxFD
result.fds = allocSharedArray[SelectorKey[T]](maxFD) result.numFD = numFD
result.fds = allocSharedArray[SelectorKey[T]](numFD)
else: else:
result = Selector[T]() result = Selector[T]()
result.epollFD = epollFD result.epollFD = epollFD
result.maxFD = maxFD result.maxFD = maxFD
result.fds = newSeq[SelectorKey[T]](maxFD) result.numFD = numFD
result.fds = newSeq[SelectorKey[T]](numFD)
for i in 0 ..< maxFD: for i in 0 ..< numFD:
result.fds[i].ident = InvalidIdent result.fds[i].ident = InvalidIdent
proc close*[T](s: Selector[T]) = proc close*[T](s: Selector[T]) =
@ -127,6 +133,16 @@ template checkFd(s, f) =
# FD if there is too many. -- DP # FD if there is too many. -- DP
if f >= s.maxFD: if f >= s.maxFD:
raiseIOSelectorsError("Maximum number of descriptors is exhausted!") raiseIOSelectorsError("Maximum number of descriptors is exhausted!")
if f >= s.numFD:
var numFD = s.numFD
while numFD <= f: numFD *= 2
when hasThreadSupport:
s.fds = reallocSharedArray(s.fds, numFD)
else:
s.fds.setLen(numFD)
for i in s.numFD ..< numFD:
s.fds[i].ident = InvalidIdent
s.numFD = numFD
proc registerHandle*[T](s: Selector[T], fd: int | SocketHandle, proc registerHandle*[T](s: Selector[T], fd: int | SocketHandle,
events: set[Event], data: T) = events: set[Event], data: T) =

View file

@ -239,6 +239,9 @@ else:
proc allocSharedArray[T](nsize: int): ptr SharedArray[T] = proc allocSharedArray[T](nsize: int): ptr SharedArray[T] =
result = cast[ptr SharedArray[T]](allocShared0(sizeof(T) * nsize)) 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]) = proc deallocSharedArray[T](sa: ptr SharedArray[T]) =
deallocShared(cast[pointer](sa)) deallocShared(cast[pointer](sa))
type type