Hi,

I have attached a patch which addresses 3 related race conditions that
cause the command line (lldb) prompt to get displayed inappropriately and
make it appear it is not working correctly.  This issue can be seen on
linux and FreeBSD.  I can also artificailly induce the problem on OSX.

The issue happens when the command handler (in the main thread) issues a
command such as run, step or continue.  After the command finishes
initiating its action, it returns up the call stack and goes back into the
main command loop waiting for user input.  Simultaneously, as the inferior
process starts up, the MonitorChildProcess thread picks up the change and
posts to the PrivateEvent thread.  HandePrivateEvent() then calls
PushProcessIOHandler() which will disable the command IO handler and give
the inferior control of the TTY.  To observe this on OSX, put a

usleep(100);

immediately prior the PushProcessIOHandler() in HandlePrivateEvent.


My proposed solution is that after a 'run', 'step', or 'continue' command,
insert a synchronization point and wait until HandlePrivateEvent knows the
inferior process is running and has pushed the IO handler.  One context
switch (<100us) is usually all the time it takes on my machine.  As an
additional safety, I have a timeout (currently 1ms) so it will never hang
the main thread.

Any thoughts, or suggestions would be appreciated.

Regards,
Shawn.
Index: include/lldb/Target/Process.h
===================================================================
--- include/lldb/Target/Process.h       (revision 213941)
+++ include/lldb/Target/Process.h       (working copy)
@@ -2706,7 +2706,36 @@
                           bool wait_always = true,
                           Listener *hijack_listener = NULL);
 
+    //------------------------------------------------------------------
+    /// Used in conjuction with WaitForProcessRunning to synchronize the main 
thread to
+    /// HandlePrivateEvent thread.
+    //------------------------------------------------------------------
+    void
+    ClearProcessRunningCounter()
+    {
+        m_process_running_counter = 0;
+    }
+
+    //------------------------------------------------------------------
+    /// Waits for the process state to be running within a given usec timeout.
+    ///
+    /// The main purpose of this is to implement an interlock waiting for 
HandlePrivateEvent
+    /// to push an IOHandler.
+    ///
+    /// @param[in] timeout_usec
+    ///     The maximum time length to wait for the process to transition to 
the
+    ///     eStateRunning state, specified in microseconds.
+    ///
+    /// @return
+    ///     A running state if the process did transition into a running state
+    ///     within the timeout interval; otherwise, the most recent state the 
process was
+    ///     in at the time of the timeout.
+    //------------------------------------------------------------------
     lldb::StateType
+    WaitForProcessRunning (uint64_t timeout_usec);
+
+
+    lldb::StateType
     WaitForStateChangedEvents (const TimeValue *timeout,
                                lldb::EventSP &event_sp,
                                Listener *hijack_listener); // Pass NULL to use 
builtin listener
@@ -3091,6 +3120,7 @@
     std::vector<PreResumeCallbackAndBaton> m_pre_resume_actions;
     ProcessRunLock              m_public_run_lock;
     ProcessRunLock              m_private_run_lock;
+    int                         m_process_running_counter;    // used as 
synchronization between HandlePrivateEvent pushing IOHandler and main thread
     Predicate<bool>             m_currently_handling_event; // This predicate 
is set in HandlePrivateEvent while all its business is being done.
     bool                        m_currently_handling_do_on_removals;
     bool                        m_resume_requested;         // If 
m_currently_handling_event or m_currently_handling_do_on_removals are true, 
Resume will only request a resume, using this flag to check.
Index: source/Commands/CommandObjectProcess.cpp
===================================================================
--- source/Commands/CommandObjectProcess.cpp    (revision 213941)
+++ source/Commands/CommandObjectProcess.cpp    (working copy)
@@ -766,8 +766,15 @@
                     
process->GetThreadList().GetThreadAtIndex(idx)->SetResumeState (eStateRunning, 
override_suspend);
                 }
             }
-            
+            process->ClearProcessRunningCounter();
+
             Error error(process->Resume());
+
+            // There is a race condition where this thread will return up the 
call stack to the main command
+            // handler and show an (lldb) prompt before HandlePrivateEvent 
(from PrivateStateThread) has
+            // a chance to call PushProcessIOHandler().
+            process->WaitForProcessRunning(1000);
+
             if (error.Success())
             {
                 result.AppendMessageWithFormat ("Process %" PRIu64 " 
resuming\n", process->GetID());
Index: source/Commands/CommandObjectThread.cpp
===================================================================
--- source/Commands/CommandObjectThread.cpp     (revision 213941)
+++ source/Commands/CommandObjectThread.cpp     (working copy)
@@ -623,9 +623,15 @@
             }
 
             process->GetThreadList().SetSelectedThreadByID (thread->GetID());
+            process->ClearProcessRunningCounter();
+
             process->Resume ();
-        
 
+            // There is a race condition where this thread will return up the 
call stack to the main command handler
+            // and show an (lldb) prompt before HandlePrivateEvent (from 
PrivateStateThread) has
+            // a chance to call PushProcessIOHandler().
+            process->WaitForProcessRunning(1000);
+
             if (synchronous_execution)
             {
                 StateType state = process->WaitForProcessToStop (NULL);
Index: source/Target/Process.cpp
===================================================================
--- source/Target/Process.cpp   (revision 213941)
+++ source/Target/Process.cpp   (working copy)
@@ -697,6 +697,7 @@
     m_next_event_action_ap(),
     m_public_run_lock (),
     m_private_run_lock (),
+    m_process_running_counter (0),
     m_currently_handling_event(false),
     m_finalize_called(false),
     m_clear_thread_plans_on_stop (false),
@@ -889,7 +890,32 @@
     return state;
 }
 
+lldb::StateType
+Process::WaitForProcessRunning (uint64_t timeout_usec)
+{
+    Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS));
+    TimeValue timeout (TimeValue::Now ());
+    timeout.OffsetWithMicroSeconds(timeout_usec);
 
+    // Make sure the loop only runs at least once.
+    do
+    {
+        // it usually only takes a context switch or two for the flag to get 
set
+        usleep(0);
+
+        if(m_process_running_counter > 0)
+        {
+            if (log)
+                log->Printf ("Process::%s pid %" PRIu64 ": SUCCESS", 
__FUNCTION__, GetID ());
+            return eStateRunning;
+        }
+    } while (TimeValue::Now () < timeout);
+
+    if (log)
+        log->Printf ("Process::%s pid %" PRIu64 " (timeout=%" PRIu64 "): 
FAIL", __FUNCTION__, GetID (), timeout_usec);
+    return GetState();
+}
+
 StateType
 Process::WaitForProcessToStop (const TimeValue *timeout, lldb::EventSP 
*event_sp_ptr, bool wait_always, Listener *hijack_listener)
 {
@@ -3878,6 +3904,7 @@
             // as this means the curses GUI is in use...
             if (!GetTarget().GetDebugger().IsForwardingEvents())
                 PushProcessIOHandler ();
+            m_process_running_counter++;
         }
         else if (StateIsStoppedState(new_state, false))
         {
Index: source/Target/Target.cpp
===================================================================
--- source/Target/Target.cpp    (revision 213941)
+++ source/Target/Target.cpp    (working copy)
@@ -2419,8 +2419,15 @@
                 if (!synchronous_execution)
                     m_process_sp->RestoreProcessEvents ();
 
+                m_process_sp->ClearProcessRunningCounter();
+
                 error = m_process_sp->PrivateResume();
-    
+
+                // there is a race condition where this thread will return up 
the call stack to the main command
+                // handler and show an (lldb) prompt before HandlePrivateEvent 
(from PrivateStateThread) has
+                // a chance to call PushProcessIOHandler()
+                m_process_sp->WaitForProcessRunning(1000);
+
                 if (error.Success())
                 {
                     if (synchronous_execution)
_______________________________________________
lldb-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/lldb-commits

Reply via email to