The motivation behind this commit is to make Foreman respect
GNU Readline 1.
As Foreman is often used with Pry (or at least many people want to), and
Pry is dependent on Readline, using these tools together is very
problematic 2,3.
The problem is that when you start Pry by means of Foreman, Foreman
supresses output from Pry, so you don't see what you type. The output
can only be seen after pressing carriage return. Demonstration (a bit
hard to follow, but you may want to try it yourself; clone the Pry repo
and execute the same command):
http://showterm.io/3eb716461d6602ac90b09
Although this problem was reported by Pry users, any Readline
application is affected. This commit uses IRB for tests, because it also
depends on Readline, hence it suits for testing perfectly. Other
Readline dependent applications that I tested were GHCi, the Lua REPL,
Eshell (erlang shell).
Previously, Pry was using a trick, which allowed us to bypass this
problem 4. The fix was featured in Pry v0.9.12.6, but then it was
removed by accident. It's been readded on HEAD recently (not released
yet). However, the fix we currently have is still not perfect. Although
you can see your input immediately, you can't see your output now :D
Demo:
http://showterm.io/f648de27568d96a02f1b3
The fix breaks correct piping. Now, when you pipe Pry's output, it
doesn't output Readline's prompt and user's input. What's being piped is
only return values of executed expressions.
Demo:
http://showterm.io/3651389faf1fdc0d18211
To fix the missing output and pipe everything, including the user's
input, we need to set Readline.output
correctly. However, if we do
that, we break minimal support for Foreman. So when I fixed Pry 5, I
figured it would be interesting to try to fix Foreman.
Now, it's time to talk about the changes here. I'm not very familiar
with Foreman, so take the code with a grain of salt. Basically, the
whole point of the fix is to read one character at a time and print it
immediately, instead of waiting for a newline character. This technique
allows us to prepend so-called markers (timestamps, if you wish) to a
Readline prompt and display the user's input. It works for all the
Readline apps that I mentioned above and doesn't break piping (that is,
tee).
Note. I had to remove $stdout.flush
from the output method, because it
was causing thread deadlocks with the flush in #handle_io
. The
deadlock appears when you use Foreman with Pry, pipe the output and
trigger SIGINT (control-c press). Example:
% foreman start -f Procfile | tee log
15:50:05 pryhere.1 | started with pid 26881
15:50:28 pryhere.1 | 1 pry(main)> <CTRL+C><CTRL+D>
15:50:49 pryhere.1 | 2 pry(main)> foreman/engine.rb:323:in
`synchronize': deadlock; recursive locking (ThreadError)
This is probably because of the race condition, when on SIGINT Foreman
prints stuff here 6, which flushes $stdout.