[Lldb-commits] [lldb] r191392 - Fixed a race condition where the prompt would randomly go missing.

Richard Mitton richard at codersnotes.com
Wed Sep 25 11:45:59 PDT 2013


Author: rmitton
Date: Wed Sep 25 13:45:59 2013
New Revision: 191392

URL: http://llvm.org/viewvc/llvm-project?rev=191392&view=rev
Log:
Fixed a race condition where the prompt would randomly go missing.

el_gets was using fflush to output it's string, but because we have our own filter running on the piped pty output, fflush only causes the prompt to be written into the pipe, and does not cause the filter code to run immediately.

The simplest fix is to manually block and wait for all editline output to be processed.

This fixes PR 14637.

Modified:
    lldb/trunk/tools/driver/IOChannel.cpp
    lldb/trunk/tools/driver/IOChannel.h

Modified: lldb/trunk/tools/driver/IOChannel.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/tools/driver/IOChannel.cpp?rev=191392&r1=191391&r2=191392&view=diff
==============================================================================
--- lldb/trunk/tools/driver/IOChannel.cpp (original)
+++ lldb/trunk/tools/driver/IOChannel.cpp Wed Sep 25 13:45:59 2013
@@ -205,6 +205,7 @@ IOChannel::IOChannel
     m_read_thread_should_exit (false),
     m_out_file (out),
     m_err_file (err),
+    m_editline_out (editline_out),
     m_command_queue (),
     m_completion_key ("\t"),
     m_edit_line (::el_init (SBHostOS::GetProgramFileSpec().GetFilename(), editline_in, editline_out,  editline_out)),
@@ -226,7 +227,7 @@ IOChannel::IOChannel
     el_set (m_edit_line, EL_BIND, m_completion_key, "lldb_complete", NULL);
     el_set (m_edit_line, EL_BIND, "^r", "em-inc-search-prev", NULL);  // Cycle through backwards search, entering string
     el_set (m_edit_line, EL_BIND, "^w", "ed-delete-prev-word", NULL); // Delete previous word, behave like bash does.
-    el_set (m_edit_line, EL_BIND, "\e[3~", "ed-delete-next-char", NULL); // Fix the delete key.
+    el_set (m_edit_line, EL_BIND, "\033[3~", "ed-delete-next-char", NULL); // Fix the delete key.
     el_set (m_edit_line, EL_CLIENTDATA, this);
 
     // Source $PWD/.editrc then $HOME/.editrc
@@ -252,6 +253,9 @@ IOChannel::IOChannel
     error = ::pthread_mutexattr_destroy (&attr);
     assert (error == 0);
 
+    error = ::pthread_cond_init (&m_output_cond, NULL);
+    assert (error == 0);
+
     // Initialize time that ::el_gets was last called.
 
     m_enter_elgets_time.tv_sec = 0;
@@ -275,6 +279,7 @@ IOChannel::~IOChannel ()
         m_edit_line = NULL;
     }
 
+    ::pthread_cond_destroy (&m_output_cond);
     ::pthread_mutex_destroy (&m_output_mutex);
 }
 
@@ -305,6 +310,15 @@ IOChannel::LibeditOutputBytesReceived (v
     IOLocker locker (io_channel->m_output_mutex);
     const char *bytes = (const char *) src;
 
+    bool flush = false;
+
+    // See if we have a 'flush' syncronization point in there.
+    if (src_len > 0 && bytes[src_len-1] == 0)
+    {
+        src_len--;
+        flush = true;
+    }
+
     if (io_channel->IsGettingCommand() && io_channel->m_expecting_prompt)
     {
         io_channel->m_prompt_str.append (bytes, src_len);
@@ -327,6 +341,14 @@ IOChannel::LibeditOutputBytesReceived (v
             io_channel->m_refresh_request_pending = false;
         io_channel->OutWrite (bytes, src_len, NO_ASYNC);
     }
+
+    if (flush)
+    {
+        // Signal that we have finished all data up to the sync point.
+        IOLocker locker (io_channel->m_output_mutex);
+        io_channel->m_output_flushed = true;
+        pthread_cond_signal (&io_channel->m_output_cond);
+    }
 }
 
 IOChannel::LibeditGetInputResult
@@ -344,6 +366,22 @@ IOChannel::LibeditGetInput (std::string
 
         // Call el_gets to prompt the user and read the user's input.
         const char *line = ::el_gets (m_edit_line, &line_len);
+
+        // Force the piped output from el_gets to finish processing.
+        // el_gets does an fflush internally, which is not sufficient here; it only
+        // writes the data into m_editline_out, but doesn't affect whether our worker
+        // thread will read that data yet. So we block here manually.
+        {
+            IOLocker locker (m_output_mutex);
+            m_output_flushed = false;
+
+            // Write a synchronization point into the stream, so we can guarantee
+            // LibeditOutputBytesReceived has processed everything up till that mark.
+            fputc (0, m_editline_out);
+
+            while (!m_output_flushed && pthread_cond_wait (&m_output_cond, &m_output_mutex))
+            { /* wait */ }
+        }
         
         // Re-set the boolean indicating whether or not el_gets is trying to get input.
         SetGettingCommand (false);

Modified: lldb/trunk/tools/driver/IOChannel.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/tools/driver/IOChannel.h?rev=191392&r1=191391&r2=191392&view=diff
==============================================================================
--- lldb/trunk/tools/driver/IOChannel.h (original)
+++ lldb/trunk/tools/driver/IOChannel.h Wed Sep 25 13:45:59 2013
@@ -128,6 +128,7 @@ protected:
 private:
 
     pthread_mutex_t m_output_mutex;
+    pthread_cond_t m_output_cond;
     struct timeval m_enter_elgets_time;
 
     Driver *m_driver;
@@ -135,6 +136,7 @@ private:
     bool m_read_thread_should_exit;
     FILE *m_out_file;
     FILE *m_err_file;
+    FILE *m_editline_out;
     std::queue<std::string> m_command_queue;
     const char *m_completion_key;
 
@@ -143,7 +145,8 @@ private:
     HistEvent m_history_event;
     bool m_getting_command;
     bool m_expecting_prompt;
-	std::string m_prompt_str;  // for accumlating the prompt as it gets written out by editline
+    bool m_output_flushed;
+    std::string m_prompt_str;  // for accumlating the prompt as it gets written out by editline
     bool m_refresh_request_pending;
 
     void





More information about the lldb-commits mailing list