diff options
author | bptato <nincsnevem662@gmail.com> | 2024-02-29 23:23:40 +0100 |
---|---|---|
committer | bptato <nincsnevem662@gmail.com> | 2024-02-29 23:31:38 +0100 |
commit | afafcaf1047f721c8d061d883329d0e556326511 (patch) | |
tree | 2abc62a06227c7aa9c573f9ce52424df638ccba8 /src/io | |
parent | f4b53af1261e6f9be16a315247ace80fcb816505 (diff) | |
download | chawan-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.nim | 54 | ||||
-rw-r--r-- | src/io/posixstream.nim | 5 |
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 = |