chzyer / readline

Readline is a pure go(golang) implementation for GNU-Readline kind library
MIT License
2.08k stars 275 forks source link

Deadlock issue when client closes TCP connection unexpectedly #194

Open govcert-ch opened 3 years ago

govcert-ch commented 3 years ago

I'm aware this project is no longer maintained, and I'm not sure if maybe another readline alternative could solve that issue. The problem I have is when a TCP connection is dropped unexpectedly while the handler is doing it's work (so not during a Readline() call itself), and the handler protects itself using a mutex to allow parallel TCP connections without interfering with each other. Here is a simple example code:

readlineTest0.txt

It just prints out each line given, stops at "close", and sleeps for 10 seconds at "pause" to simulate work. I'm using an anonymous function in a for loop that locks and unlocks (via defer) a mutex. Now, if a client connects, triggers the "pause" command, and then disconnects (by just terminating the client code), the lock is never freed. As far as I can say this happens because stdout := rl.Stdout() in line 47 returns a closed handle, so the subsequent fmt.Fprintln(stdout, "done: "+line) blocks forever; the code hangs, the lock is never freed, and any subsequent client won't be able to execute further commands.

I did not find any easy way to find out whether the stdout handle is closed ot not, which would be the easiest way to prevent that problem, or how to make the fmt.Fprintln(..) call non-blocking. Or if there is any other clean way to solve this issue.

I did find a solution using reflection, using the fact there is (unexported) information about the state of the handle in this case, using the function isClosed here:

readlineTest1.txt

This however is kind of an ugly workaround, and I hope there is a better way to reach the same?