about summary refs log tree commit diff stats
path: root/src/buffer
diff options
context:
space:
mode:
authorbptato <nincsnevem662@gmail.com>2022-11-24 20:03:21 +0100
committerbptato <nincsnevem662@gmail.com>2022-11-24 20:03:21 +0100
commit896489a6c500e28f13d0237ab691622cb5c5114f (patch)
tree91b92da01bc126c2489a3dd083df5f9de06927c6 /src/buffer
parentee930b0f5a587768d340c4204cf1f2e9fb818c89 (diff)
downloadchawan-896489a6c500e28f13d0237ab691622cb5c5114f.tar.gz
Avoid forking child processes from the main process
Caveat: this breaks piped streams.
Diffstat (limited to 'src/buffer')
-rw-r--r--src/buffer/buffer.nim50
-rw-r--r--src/buffer/container.nim80
2 files changed, 53 insertions, 77 deletions
diff --git a/src/buffer/buffer.nim b/src/buffer/buffer.nim
index 685fa4db..260fc8ea 100644
--- a/src/buffer/buffer.nim
+++ b/src/buffer/buffer.nim
@@ -30,6 +30,7 @@ import io/window
 import layout/box
 import render/renderdocument
 import render/rendertext
+import types/buffersource
 import types/color
 import types/url
 import utils/twtstr
@@ -43,21 +44,7 @@ type
   ContainerCommand* = enum
     SET_LINES, SET_NEEDS_AUTH, SET_CONTENT_TYPE, SET_REDIRECT, SET_TITLE,
     SET_HOVER, READ_LINE, LOAD_DONE, ANCHOR_FOUND, ANCHOR_FAIL, JUMP, OPEN,
-    SOURCE_READY, RESHAPE
-
-  BufferSourceType* = enum
-    CLONE, LOAD_REQUEST, LOAD_PIPE
-
-  BufferSource* = object
-    location*: URL
-    contenttype*: Option[string] # override
-    case t*: BufferSourceType
-    of CLONE:
-      clonepid*: Pid
-    of LOAD_REQUEST:
-      request*: Request
-    of LOAD_PIPE:
-      fd*: FileHandle
+    BUFFER_READY, SOURCE_READY, RESHAPE
 
   BufferMatch* = object
     success*: bool
@@ -84,6 +71,7 @@ type
     pistream: Stream # for input pipe
     postream: Stream # for output pipe
     streamclosed: bool
+    loaded: bool
     source: string
     prevnode: StyledNode
     loader: FileLoader
@@ -361,6 +349,7 @@ func getFd(buffer: Buffer): FileHandle =
     return buffer.bsource.fd
 
 proc setupSource(buffer: Buffer): int =
+  if buffer.loaded: return -2
   let source = buffer.bsource
   let setct = source.contenttype.isNone
   if not setct:
@@ -369,6 +358,7 @@ proc setupSource(buffer: Buffer): int =
   case source.t
   of CLONE:
     buffer.istream = connectSocketStream(source.clonepid)
+    if buffer.istream == nil: return -2
     if setct:
       buffer.contenttype = "text/plain"
   of LOAD_PIPE:
@@ -394,6 +384,7 @@ proc setupSource(buffer: Buffer): int =
   if setct:
     buffer.writeCommand(SET_CONTENT_TYPE, buffer.contenttype)
   buffer.selector.registerHandle(cast[int](buffer.getFd()), {Read}, 1)
+  buffer.loaded = true
 
 proc load(buffer: Buffer) =
   case buffer.contenttype
@@ -805,7 +796,7 @@ proc readCommand(buffer: Buffer) =
     istream.sread(cy)
     buffer.updateHover(cx, cy)
   of GET_SOURCE:
-    let ssock = initServerSocket(getpid())
+    let ssock = initServerSocket()
     buffer.writeCommand(SOURCE_READY)
     let stream = ssock.acceptSocketStream()
     if not buffer.streamclosed:
@@ -815,10 +806,7 @@ proc readCommand(buffer: Buffer) =
     stream.close()
     ssock.close()
 
-proc runBuffer(buffer: Buffer, readf, writef: File) =
-  buffer.pistream = newFileStream(readf)
-  buffer.postream = newFileStream(writef)
-  let rfd = readf.getFileHandle()
+proc runBuffer(buffer: Buffer, rfd: int) =
   block loop:
     while true:
       let events = buffer.selector.select(-1)
@@ -842,21 +830,25 @@ proc runBuffer(buffer: Buffer, readf, writef: File) =
         buffer.writeCommand(RESHAPE)
   buffer.pistream.close()
   buffer.postream.close()
-  when defined(posix):
-    #TODO remove this
-    if buffer.loader != nil:
-      assert kill(buffer.loader.process, cint(SIGTERM)) == 0
-      buffer.loader = nil
+  buffer.loader.quit()
   quit(0)
 
 proc launchBuffer*(config: BufferConfig, source: BufferSource,
-                   attrs: WindowAttributes, readf, writef: File) =
+                   attrs: WindowAttributes, loader: FileLoader,
+                   mainproc: Pid) =
   let buffer = new Buffer
   buffer.attrs = attrs
   buffer.windowChange()
   buffer.config = config
-  buffer.loader = newFileLoader()
+  buffer.loader = loader
   buffer.bsource = source
   buffer.selector = newSelector[int]()
-  buffer.selector.registerHandle(int(readf.getFileHandle()), {Read}, 0)
-  buffer.runBuffer(readf, writef)
+  let sstream = connectSocketStream(mainproc, false)
+  sstream.swrite(getpid())
+  sstream.swrite(BUFFER_READY)
+  sstream.flush()
+  buffer.pistream = sstream
+  buffer.postream = sstream
+  let rfd = int(sstream.source.getFd())
+  buffer.selector.registerHandle(rfd, {Read}, 0)
+  buffer.runBuffer(rfd)
diff --git a/src/buffer/container.nim b/src/buffer/container.nim
index 054424ce..4241e886 100644
--- a/src/buffer/container.nim
+++ b/src/buffer/container.nim
@@ -13,9 +13,12 @@ import config/bufferconfig
 import config/config
 import io/request
 import io/window
+import ips/forkserver
 import ips/serialize
 import js/javascript
 import js/regex
+import types/buffersource
+import types/dispatcher
 import types/url
 import utils/twtstr
 
@@ -64,8 +67,7 @@ type
     sourcepair*: Container
     istream*: Stream
     ostream*: Stream
-    ifd*: FileHandle
-    process: Pid
+    process*: Pid
     lines: SimpleFlexibleGrid
     lineshift: int
     numLines*: int
@@ -76,53 +78,28 @@ type
     ispipe: bool
     jump: bool
     hlon*: bool
+    waitfor: bool
     pipeto: Container
     redraw*: bool
+    sourceready*: bool
 
-proc c_setvbuf(f: File, buf: pointer, mode: cint, size: csize_t): cint {.
-  importc: "setvbuf", header: "<stdio.h>", tags: [].}
-
-proc newBuffer*(config: Config, source: BufferSource, ispipe = false): Container =
+proc newBuffer*(dispatcher: Dispatcher, config: Config, source: BufferSource, ispipe = false, autoload = true): Container =
   let attrs = getWindowAttributes(stdout)
-  when defined(posix):
-    var pipefd_in, pipefd_out: array[0..1, cint]
-    if pipe(pipefd_in) == -1:
-      raise newException(Defect, "Failed to open input pipe.")
-    if pipe(pipefd_out) == -1:
-      raise newException(Defect, "Failed to open output pipe.")
-    let pid = fork()
-    if pid == -1:
-      raise newException(Defect, "Failed to fork buffer process")
-    elif pid == 0:
-      let bconfig = config.loadBufferConfig()
-      discard close(stdout.getFileHandle())
-      # child process
-      discard close(pipefd_in[1]) # close write
-      discard close(pipefd_out[0]) # close read
-      var readf, writef: File
-      if not open(readf, pipefd_in[0], fmRead):
-        raise newException(Defect, "Failed to open input handle")
-      if not open(writef, pipefd_out[1], fmWrite):
-        raise newException(Defect, "Failed to open output handle")
-      discard c_setvbuf(readf, nil, IONBF, 0)
-      launchBuffer(bconfig, source, attrs, readf, writef)
-    else:
-      discard close(pipefd_in[0]) # close read
-      discard close(pipefd_out[1]) # close write
-      var readf, writef: File
-      if not open(writef, pipefd_in[1], fmWrite):
-        raise newException(Defect, "Failed to open output handle")
-      if not open(readf, pipefd_out[0], fmRead):
-        raise newException(Defect, "Failed to open input handle")
-      let istream = newFileStream(readf)
-      # Disable buffering of the read end so epoll doesn't get stuck
-      discard c_setvbuf(readf, nil, IONBF, 0)
-      let ostream = newFileStream(writef)
-      result = Container(istream: istream, ostream: ostream, source: source,
-                         ifd: pipefd_out[0], process: pid, attrs: attrs,
-                         width: attrs.width, height: attrs.height - 1,
-                         contenttype: source.contenttype, ispipe: ispipe)
-      result.pos.setx = -1
+  let ostream = dispatcher.forkserver.ostream
+  let istream = dispatcher.forkserver.istream
+  ostream.swrite(FORK_BUFFER)
+  ostream.swrite(source)
+  ostream.swrite(config.loadBufferConfig())
+  ostream.swrite(attrs)
+  ostream.swrite(dispatcher.mainproc)
+  ostream.flush()
+  result = Container(
+    source: source, attrs: attrs, width: attrs.width,
+    height: attrs.height - 1, contenttype: source.contenttype,
+    ispipe: ispipe, waitfor: true
+  )
+  istream.sread(result.process)
+  result.pos.setx = -1
 
 func lineLoaded(container: Container, y: int): bool =
   return y - container.lineshift in 0..container.lines.high
@@ -583,14 +560,14 @@ proc reshape*(container: Container, noreq = false) {.jsfunc.} =
   if not noreq:
     container.requestLines()
 
-proc dupeBuffer*(container: Container, config: Config, location = none(URL), contenttype = none(string)): Container =
+proc dupeBuffer*(dispatcher: Dispatcher, container: Container, config: Config, location = none(URL), contenttype = none(string)): Container =
   let source = BufferSource(
     t: CLONE,
     location: location.get(container.source.location),
     contenttype: if contenttype.isSome: contenttype else: container.contenttype,
     clonepid: container.process,
   )
-  container.pipeto = newBuffer(config, source, container.ispipe)
+  container.pipeto = dispatcher.newBuffer(config, source, container.ispipe)
   container.writeCommand(GET_SOURCE)
   return container.pipeto
 
@@ -639,6 +616,7 @@ proc handleCommand(container: Container, cmd: ContainerCommand): ContainerEvent
     container.istream.sread(container.hovertext)
   of LOAD_DONE:
     container.istream.sread(container.code)
+    if container.code == -2: return
     if container.code != 0:
       return ContainerEvent(t: FAIL)
     return ContainerEvent(t: SUCCESS)
@@ -667,7 +645,13 @@ proc handleCommand(container: Container, cmd: ContainerCommand): ContainerEvent
       container.setCursorXY(x, y)
       container.jump = false
   of OPEN:
-    return ContainerEvent(t: OPEN, request: container.istream.readRequest())
+    var request: Request
+    container.istream.sread(request)
+    return ContainerEvent(t: OPEN, request: request)
+  of BUFFER_READY:
+    if container.waitfor:
+      container.waitfor = false
+      container.load()
   of SOURCE_READY:
     if container.pipeto != nil:
       container.pipeto.load()