diff options
author | bptato <nincsnevem662@gmail.com> | 2022-11-24 20:03:21 +0100 |
---|---|---|
committer | bptato <nincsnevem662@gmail.com> | 2022-11-24 20:03:21 +0100 |
commit | 896489a6c500e28f13d0237ab691622cb5c5114f (patch) | |
tree | 91b92da01bc126c2489a3dd083df5f9de06927c6 /src/display | |
parent | ee930b0f5a587768d340c4204cf1f2e9fb818c89 (diff) | |
download | chawan-896489a6c500e28f13d0237ab691622cb5c5114f.tar.gz |
Avoid forking child processes from the main process
Caveat: this breaks piped streams.
Diffstat (limited to 'src/display')
-rw-r--r-- | src/display/client.nim | 90 | ||||
-rw-r--r-- | src/display/pager.nim | 34 |
2 files changed, 79 insertions, 45 deletions
diff --git a/src/display/client.nim b/src/display/client.nim index 9ef501ab..bd7f3977 100644 --- a/src/display/client.nim +++ b/src/display/client.nim @@ -1,3 +1,5 @@ +import nativesockets +import net import options import os import selectors @@ -22,14 +24,20 @@ import io/loader import io/request import io/term import io/window +import ips/forkserver +import ips/serialize +import ips/serversocket +import ips/socketstream import js/javascript import types/cookie +import types/dispatcher import types/url type Client* = ref ClientObj ClientObj* = object attrs: WindowAttributes + dispatcher: Dispatcher feednext: bool s: string errormessage: string @@ -46,6 +54,9 @@ type intervals: Table[int, tuple[handler: (proc()), fdi: int, tofree: JSValue]] timeout_fdis: Table[int, int] interval_fdis: Table[int, int] + fdmap: Table[int, Container] + ssock: ServerSocket + selector: Selector[Container] Console = ref object err: Stream @@ -110,8 +121,6 @@ proc command(client: Client, src: string) = proc quit(client: Client, code = 0) {.jsfunc.} = client.pager.quit() - when defined(posix): - assert kill(client.loader.process, cint(SIGTERM)) == 0 quit(code) proc feedNext(client: Client) {.jsfunc.} = @@ -152,7 +161,7 @@ proc input(client: Client) = proc setTimeout[T: JSObject|string](client: Client, handler: T, timeout = 0): int {.jsfunc.} = let id = client.timeoutid inc client.timeoutid - let fdi = client.pager.selector.registerTimer(timeout, true, nil) + let fdi = client.selector.registerTimer(timeout, true, nil) client.timeout_fdis[fdi] = id when T is string: client.timeouts[id] = ((proc() = @@ -172,7 +181,7 @@ proc setTimeout[T: JSObject|string](client: Client, handler: T, timeout = 0): in proc setInterval[T: JSObject|string](client: Client, handler: T, interval = 0): int {.jsfunc.} = let id = client.timeoutid inc client.timeoutid - let fdi = client.pager.selector.registerTimer(interval, false, nil) + let fdi = client.selector.registerTimer(interval, false, nil) client.interval_fdis[fdi] = id when T is string: client.intervals[id] = ((proc() = @@ -192,26 +201,46 @@ proc setInterval[T: JSObject|string](client: Client, handler: T, interval = 0): proc clearTimeout(client: Client, id: int) {.jsfunc.} = if id in client.timeouts: let timeout = client.timeouts[id] - client.pager.selector.unregister(timeout.fdi) + client.selector.unregister(timeout.fdi) client.timeout_fdis.del(timeout.fdi) client.timeouts.del(id) proc clearInterval(client: Client, id: int) {.jsfunc.} = if id in client.intervals: let interval = client.intervals[id] - client.pager.selector.unregister(interval.fdi) + client.selector.unregister(interval.fdi) JS_FreeValue(client.jsctx, interval.tofree) client.interval_fdis.del(interval.fdi) client.intervals.del(id) let SIGWINCH {.importc, header: "<signal.h>", nodecl.}: cint +proc acceptBuffers(client: Client) = + var i = 0 + while i < client.pager.procmap.len: + let stream = client.ssock.acceptSocketStream() + var pid: Pid + stream.sread(pid) + if pid in client.pager.procmap: + let container = client.pager.procmap[pid] + client.pager.procmap.del(pid) + container.istream = stream + container.ostream = stream + let fd = stream.source.getFd() + client.fdmap[int(fd)] = container + client.selector.registerHandle(fd, {Read}, nil) + inc i + else: + #TODO print an error? + stream.close() + proc inputLoop(client: Client) = - let selector = client.pager.selector + let selector = client.selector selector.registerHandle(int(client.console.tty.getFileHandle()), {Read}, nil) let sigwinch = selector.registerSignal(int(SIGWINCH), nil) while true: - let events = client.pager.selector.select(-1) + client.acceptBuffers() + let events = client.selector.select(-1) for event in events: if event.fd == client.console.tty.getFileHandle(): client.input() @@ -227,7 +256,7 @@ proc inputLoop(client: Client) = client.attrs = getWindowAttributes(client.console.tty) client.pager.windowChange(client.attrs) else: - let container = client.pager.fdmap[FileHandle(event.fd)] + let container = client.fdmap[event.fd] if not client.pager.handleEvent(container): disableRawMode() for msg in client.pager.status: @@ -257,7 +286,6 @@ proc newConsole(pager: Pager, tty: File): Console = raise newException(Defect, "Failed to open console pipe.") let url = newURL("javascript:console.show()") result.container = pager.readPipe0(some("text/plain"), pipefd[0], option(url)) - pager.registerContainer(result.container) discard close(pipefd[0]) var f: File if not open(f, pipefd[1], fmWrite): @@ -265,9 +293,22 @@ proc newConsole(pager: Pager, tty: File): Console = result.err = newFileStream(f) result.pager = pager result.tty = tty + pager.registerContainer(result.container) else: result.err = newFileStream(stderr) +proc dumpBuffers(client: Client) = + client.acceptBuffers() + for container in client.pager.containers: + container.load() + for msg in client.pager.status: + eprint msg + let ostream = newFileStream(stdout) + for container in client.pager.containers: + container.reshape(true) + client.pager.drawBuffer(container, ostream) + stdout.close() + proc launchClient*(client: Client, pages: seq[string], ctype: Option[string], dump: bool) = var tty: File var dump = dump @@ -278,12 +319,11 @@ proc launchClient*(client: Client, pages: seq[string], ctype: Option[string], du discard open(tty, "/dev/tty", fmRead) else: dump = true + client.ssock = initServerSocket(false) + client.selector = newSelector[Container]() client.pager.launchPager(tty) client.console = newConsole(client.pager, tty) - let pid = getpid() - addExitProc((proc() = - if pid == getpid(): - client.quit())) + addExitProc((proc() = client.quit())) if client.config.startup != "": let s = if fileExists(client.config.startup): readFile(client.config.startup) @@ -303,13 +343,7 @@ proc launchClient*(client: Client, pages: seq[string], ctype: Option[string], du if not dump: client.inputLoop() else: - for msg in client.pager.status: - eprint msg - let ostream = newFileStream(stdout) - for container in client.pager.containers: - container.reshape(true) - client.pager.drawBuffer(container, ostream) - stdout.close() + client.dumpBuffers() client.quit() proc nimGCStats(client: Client): string {.jsfunc.} = @@ -338,16 +372,16 @@ proc hide(console: Console) {.jsfunc.} = proc sleep(client: Client, millis: int) {.jsfunc.} = sleep millis -proc newClient*(config: Config): Client = +proc newClient*(config: Config, dispatcher: Dispatcher): Client = new(result) result.config = config + result.dispatcher = dispatcher result.attrs = getWindowAttributes(stdout) - result.loader = newFileLoader() - result.pager = newPager(config, result.attrs) - let rt = newJSRuntime() - rt.setInterruptHandler(interruptHandler, cast[pointer](result)) - let ctx = rt.newJSContext() - result.jsrt = rt + result.loader = dispatcher.forkserver.newFileLoader() + result.pager = newPager(config, result.attrs, dispatcher) + result.jsrt = newJSRuntime() + result.jsrt.setInterruptHandler(interruptHandler, cast[pointer](result)) + let ctx = result.jsrt.newJSContext() result.jsctx = ctx var global = ctx.getGlobalObject() ctx.registerType(Client, asglobal = true) diff --git a/src/display/pager.nim b/src/display/pager.nim index 62137805..3fdcd504 100644 --- a/src/display/pager.nim +++ b/src/display/pager.nim @@ -1,10 +1,12 @@ import options import os -import selectors import streams import tables import unicode +when defined(posix): + import posix + import buffer/buffer import buffer/cell import buffer/container @@ -13,9 +15,12 @@ import io/lineedit import io/request import io/term import io/window +import ips/forkserver import js/javascript import js/regex +import types/buffersource import types/color +import types/dispatcher import types/url import utils/twtstr @@ -28,6 +33,7 @@ type attrs: WindowAttributes commandMode*: bool container*: Container + dispatcher*: Dispatcher lineedit*: Option[LineEdit] linemode*: LineMode username: string @@ -39,8 +45,7 @@ type status*: seq[string] statusmsg*: FixedGrid tty: File - selector*: Selector[Container] - fdmap*: Table[FileHandle, Container] + procmap*: Table[Pid, Container] icpos: CursorPosition display: FixedGrid redraw*: bool @@ -139,11 +144,11 @@ proc isearchBackward(pager: Pager) {.jsfunc.} = func attrs*(pager: Pager): WindowAttributes = pager.term.attrs -proc newPager*(config: Config, attrs: WindowAttributes): Pager = +proc newPager*(config: Config, attrs: WindowAttributes, dispatcher: Dispatcher): Pager = let pager = Pager( + dispatcher: dispatcher, config: config, attrs: attrs, - selector: newSelector[Container](), display: newFixedGrid(attrs.width, attrs.height - 1), statusmsg: newFixedGrid(attrs.width), term: newTerminal(stdout, config) @@ -308,19 +313,17 @@ proc draw*(pager: Pager) = pager.container.redraw = false proc registerContainer*(pager: Pager, container: Container) = - pager.fdmap[container.ifd] = container - pager.selector.registerHandle(int(container.ifd), {Read}, pager.container) + pager.procmap[container.process] = container proc addContainer*(pager: Pager, container: Container) = container.parent = pager.container if pager.container != nil: pager.container.children.add(container) - pager.setContainer(container) - assert int(container.ifd) != 0 pager.registerContainer(container) + pager.setContainer(container) proc dupeContainer(pager: Pager, container: Container, location: Option[URL]): Container = - return container.dupeBuffer(pager.config, location) + return pager.dispatcher.dupeBuffer(container, pager.config, location) proc dupeBuffer*(pager: Pager, location = none(URL)) {.jsfunc.} = pager.addContainer(pager.dupeContainer(pager.container, location)) @@ -396,10 +399,9 @@ proc deleteContainer(pager: Pager, container: Container) = pager.setContainer(nil) container.parent = nil container.children.setLen(0) - pager.fdmap.del(container.ifd) - pager.selector.unregister(int(container.ifd)) container.istream.close() container.ostream.close() + pager.dispatcher.forkserver.removeChild(container.process) proc discardBuffer*(pager: Pager) {.jsfunc.} = if pager.container == nil or pager.container.parent == nil and @@ -416,7 +418,7 @@ proc toggleSource*(pager: Pager) {.jsfunc.} = some("text/plain") else: some("text/html") - let container = pager.container.dupeBuffer(pager.config, contenttype = contenttype) + let container = pager.dispatcher.dupeBuffer(pager.container, pager.config, contenttype = contenttype) container.sourcepair = pager.container pager.container.sourcepair = container pager.addContainer(container) @@ -445,10 +447,9 @@ proc gotoURL*(pager: Pager, request: Request, prevurl = none(URL), ctype = none( contenttype: ctype, location: request.url ) - let container = newBuffer(pager.config, source) + let container = pager.dispatcher.newBuffer(pager.config, source) container.replace = replace pager.addContainer(container) - container.load() else: pager.container.redirect = some(request.url) pager.container.gotoAnchor(request.url.anchor) @@ -496,8 +497,7 @@ proc readPipe0*(pager: Pager, ctype: Option[string], fd: FileHandle, location: O contenttype: some(ctype.get("text/plain")), location: location.get(newURL("file://-")) ) - let container = newBuffer(pager.config, source, ispipe = true) - container.load() + let container = pager.dispatcher.newBuffer(pager.config, source, ispipe = true) return container proc readPipe*(pager: Pager, ctype: Option[string], fd: FileHandle) = |