about summary refs log tree commit diff stats
path: root/src/io
diff options
context:
space:
mode:
authorbptato <nincsnevem662@gmail.com>2024-02-29 23:23:40 +0100
committerbptato <nincsnevem662@gmail.com>2024-02-29 23:31:38 +0100
commitafafcaf1047f721c8d061d883329d0e556326511 (patch)
tree2abc62a06227c7aa9c573f9ce52424df638ccba8 /src/io
parentf4b53af1261e6f9be16a315247ace80fcb816505 (diff)
downloadchawan-afafcaf1047f721c8d061d883329d0e556326511.tar.gz
buffer, client: fix deadlock with send() calls
This is an ancient bug, but it got much easier to trigger with mouse
scrolling support so it's time to fix it.

(The bug itself was that since both the client and buffer ends of the
controlling stream are blocking, they could get stuck when both were
trying to send() data to the other end but the buffer was full. So now
we set the client end to non-blocking.)
Diffstat (limited to 'src/io')
-rw-r--r--src/io/bufstream.nim54
-rw-r--r--src/io/posixstream.nim5
2 files changed, 58 insertions, 1 deletions
diff --git a/src/io/bufstream.nim b/src/io/bufstream.nim
new file mode 100644
index 00000000..eb9d6d6b
--- /dev/null
+++ b/src/io/bufstream.nim
@@ -0,0 +1,54 @@
+import io/posixstream
+
+type
+  BufStream* = ref object of PosixStream
+    source*: PosixStream
+    registerFun: proc(fd: int)
+    registered: bool
+    writeBuffer: string
+
+method recvData*(s: BufStream, buffer: pointer, len: int): int =
+  s.source.recvData(buffer, len)
+
+method sendData*(s: BufStream, buffer: pointer, len: int): int =
+  s.source.setBlocking(false)
+  block nobuf:
+    var n: int
+    if not s.registered:
+      try:
+        n = s.source.sendData(buffer, len)
+        if n == len:
+          break nobuf
+      except ErrorAgain:
+        discard
+      s.registerFun(s.source.fd)
+      s.registered = true
+    let olen = s.writeBuffer.len
+    s.writeBuffer.setLen(s.writeBuffer.len + len - n)
+    let buffer = cast[ptr UncheckedArray[uint8]](buffer)
+    copyMem(addr s.writeBuffer[olen], addr buffer[n], len - n)
+  s.source.setBlocking(true)
+  return len
+
+method sclose*(s: BufStream) =
+  s.source.sclose()
+
+proc flushWrite*(s: BufStream): bool =
+  s.source.setBlocking(false)
+  let n = s.source.sendData(s.writeBuffer)
+  s.source.setBlocking(true)
+  if n == s.writeBuffer.len:
+    s.writeBuffer = ""
+    s.registered = false
+    return true
+  s.writeBuffer = s.writeBuffer.substr(n)
+  return false
+
+proc newBufStream*(ps: PosixStream, registerFun: proc(fd: int)): BufStream =
+  result = BufStream(
+    fd: ps.fd,
+    source: ps,
+    blocking: ps.blocking,
+    registerFun: registerFun
+  )
+  result.addStreamIface()
diff --git a/src/io/posixstream.nim b/src/io/posixstream.nim
index da1a8a62..80407b27 100644
--- a/src/io/posixstream.nim
+++ b/src/io/posixstream.nim
@@ -56,6 +56,9 @@ method sendData*(s: PosixStream, buffer: pointer, len: int): int {.base.} =
     raisePosixIOError()
   return n
 
+proc sendData*(s: PosixStream, buffer: openArray[char]): int {.inline.} =
+  return s.sendData(unsafeAddr buffer[0], buffer.len)
+
 method setBlocking*(s: PosixStream, blocking: bool) {.base.} =
   s.blocking = blocking
   let ofl = fcntl(s.fd, F_GETFL, 0)
@@ -83,7 +86,7 @@ proc psReadData(s: Stream, buffer: pointer, len: int): int =
 
 proc psWriteData(s: Stream, buffer: pointer, len: int) =
   let s = PosixStream(s)
-  assert len != 0 and s.blocking
+  #TODO assert len != 0 and s.blocking
   discard s.sendData(buffer, len)
 
 proc psAtEnd(s: Stream): bool =