Skip to content

Commit db8cffb

Browse files
committed
Ticket MidnightCommander#4625: Fix slow start with zsh on macOS, part 2/2
Avoid a deadlock arising by zsh draining its output channel (i.e. waiting for mc to read them), while mc is waiting over a different channel for zsh's response to some magic keypresses to test the persistent command line feature. Fix this by making mc behave as it's expected from a terminal master: still consuming incoming data while performing that other task. The bug did not affect Linux because there draining the channel, at least over local pseudoterminals, is a no-op, it does not actually wait for the master side to read that data. Signed-off-by: Egmont Koblinger <[email protected]>
1 parent 273cd4e commit db8cffb

File tree

1 file changed

+37
-7
lines changed

1 file changed

+37
-7
lines changed

src/subshell/common.c

Lines changed: 37 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -614,7 +614,7 @@ read_command_line_buffer (gboolean test_mode)
614614
FD_ZERO (&read_set);
615615
FD_SET (command_buffer_pipe[READ], &read_set);
616616

617-
const int maxfdp = command_buffer_pipe[READ];
617+
const int maxfdp = MAX (command_buffer_pipe[READ], mc_global.tty.subshell_pty);
618618

619619
/* First, flush the command buffer pipe. This pipe shouldn't be written
620620
* to under normal circumstances, but if it somehow does get written
@@ -643,11 +643,15 @@ read_command_line_buffer (gboolean test_mode)
643643
sizeof (ESC_STR SHELL_BUFFER_KEYBINDING) - 1);
644644

645645
subshell_prompt_timer.tv_sec = 1;
646-
FD_ZERO (&read_set);
647-
FD_SET (command_buffer_pipe[READ], &read_set);
648646

649-
while ((rc = select (maxfdp + 1, &read_set, NULL, NULL, &subshell_prompt_timer)) != 1)
647+
while (TRUE)
650648
{
649+
FD_ZERO (&read_set);
650+
FD_SET (command_buffer_pipe[READ], &read_set);
651+
FD_SET (mc_global.tty.subshell_pty, &read_set);
652+
653+
rc = select (maxfdp + 1, &read_set, NULL, NULL, &subshell_prompt_timer);
654+
651655
if (rc == -1)
652656
{
653657
if (errno == EINTR)
@@ -658,6 +662,17 @@ read_command_line_buffer (gboolean test_mode)
658662

659663
if (rc == 0)
660664
return FALSE;
665+
666+
if (FD_ISSET (mc_global.tty.subshell_pty, &read_set))
667+
{
668+
/* Keep reading the pty to avoid possible deadlock with the shell. Throw away the data.
669+
*/
670+
char buf[BUF_SMALL];
671+
read_nonblock (mc_global.tty.subshell_pty, buf, sizeof (buf));
672+
}
673+
674+
if (FD_ISSET (command_buffer_pipe[READ], &read_set))
675+
break;
661676
}
662677

663678
bytes =
@@ -676,11 +691,15 @@ read_command_line_buffer (gboolean test_mode)
676691

677692
subshell_prompt_timer.tv_sec = 1;
678693
subshell_prompt_timer.tv_usec = 0;
679-
FD_ZERO (&read_set);
680-
FD_SET (command_buffer_pipe[READ], &read_set);
681694

682-
while ((rc = select (maxfdp + 1, &read_set, NULL, NULL, &subshell_prompt_timer)) != 1)
695+
while (TRUE)
683696
{
697+
FD_ZERO (&read_set);
698+
FD_SET (command_buffer_pipe[READ], &read_set);
699+
FD_SET (mc_global.tty.subshell_pty, &read_set);
700+
701+
rc = select (maxfdp + 1, &read_set, NULL, NULL, &subshell_prompt_timer);
702+
684703
if (rc == -1)
685704
{
686705
if (errno == EINTR)
@@ -691,6 +710,17 @@ read_command_line_buffer (gboolean test_mode)
691710

692711
if (rc == 0)
693712
return FALSE;
713+
714+
if (FD_ISSET (mc_global.tty.subshell_pty, &read_set))
715+
{
716+
/* Keep reading the pty to avoid possible deadlock with the shell. Throw away the data.
717+
*/
718+
char buf[BUF_SMALL];
719+
read_nonblock (mc_global.tty.subshell_pty, buf, sizeof (buf));
720+
}
721+
722+
if (FD_ISSET (command_buffer_pipe[READ], &read_set))
723+
break;
694724
}
695725

696726
bytes =

0 commit comments

Comments
 (0)