diff options
author | bptato <nincsnevem662@gmail.com> | 2024-09-23 19:51:20 +0200 |
---|---|---|
committer | bptato <nincsnevem662@gmail.com> | 2024-09-23 19:58:54 +0200 |
commit | fcd9aa9f9c604ed5d104343542962a26b2acda62 (patch) | |
tree | 7b0eea9b63bacc27cdc6471e2b2409b7b5d15c9e /src/loader | |
parent | 8e8c7f0911f4a20446a83090d722fecaf203f6f3 (diff) | |
download | chawan-fcd9aa9f9c604ed5d104343542962a26b2acda62.tar.gz |
Replace std/selectors with poll
std/selectors uses OS-specific selector APIs, which sounds good in theory (faster than poll!), but sucks for portability in practice. Sure, you can fix portability bugs, but who knows how many there are on untested platforms... poll is standard, so if it works on one computer it should work on all other ones. (I hope.) As a bonus, I rewrote the timeout API for poll, which incidentally fixes setTimeout across forks. Also, SIGWINCH should now work on all platforms (as we self-pipe instead of signalfd/kqueue magic).
Diffstat (limited to 'src/loader')
-rw-r--r-- | src/loader/loader.nim | 63 |
1 files changed, 19 insertions, 44 deletions
diff --git a/src/loader/loader.nim b/src/loader/loader.nim index 85490b84..e8d29ee2 100644 --- a/src/loader/loader.nim +++ b/src/loader/loader.nim @@ -26,13 +26,13 @@ import std/net import std/options import std/os import std/posix -import std/selectors import std/strutils import std/tables import io/bufreader import io/bufwriter import io/dynstream +import io/poll import io/serversocket import io/stdio import io/tempfile @@ -42,7 +42,6 @@ import loader/headers import loader/loaderhandle import loader/loaderiface import loader/request -import loader/response import monoucha/javascript import types/cookie import types/formdata @@ -52,9 +51,6 @@ import types/urimethodmap import types/url import utils/twtstr -export request -export response - type CachedItem = ref object id: int @@ -77,7 +73,7 @@ type alive: bool config: LoaderConfig handleMap: seq[LoaderHandle] - selector: Selector[int] + pollData: PollData # List of existing clients (buffer or pager) that may make requests. clientData: Table[int, ClientData] # pid -> data # ID of next output. TODO: find a better allocation scheme @@ -145,40 +141,22 @@ type PushBufferResult = enum proc register(ctx: LoaderContext; handle: InputHandle) = assert not handle.registered - ctx.selector.registerHandle(int(handle.stream.fd), {Read}, 0) + ctx.pollData.register(handle.stream.fd, cshort(POLLIN)) handle.registered = true proc unregister(ctx: LoaderContext; handle: InputHandle) = assert handle.registered - ctx.selector.unregister(int(handle.stream.fd)) + ctx.pollData.unregister(int(handle.stream.fd)) handle.registered = false proc register(ctx: LoaderContext; output: OutputHandle) = assert not output.registered - ctx.selector.registerHandle(int(output.stream.fd), {Write}, 0) + ctx.pollData.register(int(output.stream.fd), cshort(POLLOUT)) output.registered = true -const bsdPlatform = defined(macosx) or defined(freebsd) or defined(netbsd) or - defined(openbsd) or defined(dragonfly) proc unregister(ctx: LoaderContext; output: OutputHandle) = assert output.registered - # so kqueue-based selectors raise when we try to unregister a pipe whose - # reader is at EOF. "solution": clean up this mess ourselves. - let fd = int(output.stream.fd) - when bsdPlatform: - let oc = ctx.selector.count - try: - ctx.selector.unregister(fd) - except IOSelectorsException: - # ???? - for name, f in ctx.selector[].fieldPairs: - when name == "fds": - cast[ptr int](addr f[fd])[] = -1 - elif name == "changes": - f.setLen(0) - ctx.selector.count = oc - 1 - else: - ctx.selector.unregister(fd) + ctx.pollData.unregister(int(output.stream.fd)) output.registered = false # Either write data to the target output, or append it to the list of buffers to @@ -1178,17 +1156,13 @@ proc exitLoader(ctx: LoaderContext) = var gctx: LoaderContext proc initLoaderContext(fd: cint; config: LoaderConfig): LoaderContext = - var ctx = LoaderContext( - alive: true, - config: config, - selector: newSelector[int]() - ) + var ctx = LoaderContext(alive: true, config: config) gctx = ctx let myPid = getCurrentProcessId() # we don't capsicumize loader, so -1 is appropriate here ctx.ssock = initServerSocket(config.sockdir, -1, myPid, blocking = true) let sfd = int(ctx.ssock.sock.getFd()) - ctx.selector.registerHandle(sfd, {Read}, 0) + ctx.pollData.register(sfd, POLLIN) if sfd >= ctx.handleMap.len: ctx.handleMap.setLen(sfd + 1) ctx.handleMap[sfd] = LoaderHandle() # pseudo handle @@ -1302,25 +1276,26 @@ proc finishCycle(ctx: LoaderContext; unregRead: var seq[InputHandle]; proc runFileLoader*(fd: cint; config: LoaderConfig) = var ctx = initLoaderContext(fd, config) let fd = int(ctx.ssock.sock.getFd()) - var keys: array[64, ReadyKey] while ctx.alive: - let count = ctx.selector.selectInto(-1, keys) + ctx.pollData.poll(-1) var unregRead: seq[InputHandle] = @[] var unregWrite: seq[OutputHandle] = @[] - for event in keys.toOpenArray(0, count - 1): - let handle = ctx.handleMap[event.fd] - if Read in event.events: - if event.fd == fd: # incoming connection + for event in ctx.pollData.events: + let efd = int(event.fd) + if (event.revents and POLLIN) != 0: + if efd == fd: # incoming connection ctx.acceptConnection() else: - let handle = InputHandle(ctx.handleMap[event.fd]) + let handle = InputHandle(ctx.handleMap[efd]) case ctx.handleRead(handle, unregWrite) of hrrDone: discard of hrrUnregister, hrrBrokenPipe: unregRead.add(handle) - if Write in event.events: + if (event.revents and POLLOUT) != 0: + let handle = ctx.handleMap[efd] ctx.handleWrite(OutputHandle(handle), unregWrite) - if Error in event.events: - assert event.fd != fd + if (event.revents and POLLERR) != 0 or (event.revents and POLLHUP) != 0: + assert efd != fd + let handle = ctx.handleMap[efd] if handle of InputHandle: # istream died unregRead.add(InputHandle(handle)) else: # ostream died |