<rdar://problem/12446222>

Implement the ability for Python commands to be interrupted by pressing CTRL+C
Also add a new Mutex subclass that attempts to be helpful for debugging by logging actions performed on it

FYI of all interested - there is a separate deadlocking issue related to how LLDB dispatches CTRL+C that might cause LLDB to deadlock upon pressing CTRL+C while in a Python command.
This is not a regression, and was just previously masked by us not even trying to bail out of Python commands, so that it would not be clear from a user perspective whether we were
deadlocked or stuck in an inconsistent state within the Python interpreter.



git-svn-id: https://llvm.org/svn/llvm-project/lldb/trunk@170612 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/include/lldb/Host/Mutex.h b/include/lldb/Host/Mutex.h
index 38f4f31..63f759e 100644
--- a/include/lldb/Host/Mutex.h
+++ b/include/lldb/Host/Mutex.h
@@ -193,6 +193,9 @@
     /// @return
     ///     The error code from \c pthread_mutex_lock().
     //------------------------------------------------------------------
+#ifdef LLDB_CONFIGURATION_DEBUG
+    virtual
+#endif
     int
     Lock();
 
@@ -280,6 +283,27 @@
     pthread_t m_thread_that_tried;
     std::string m_failure_message;
 };
+
+class LoggingMutex : public Mutex
+{
+public:
+    LoggingMutex() : Mutex(),m_locked(false)  {}
+    LoggingMutex(Mutex::Type type) : Mutex (type),m_locked(false) {}
+    
+    virtual
+    ~LoggingMutex() {}
+    
+    virtual int
+    Lock ();
+    
+    virtual int
+    Unlock ();
+    
+    virtual int
+    TryLock (const char *failure_message = NULL);
+protected:
+    bool m_locked;
+};
 #endif
 
 } // namespace lldb_private
diff --git a/include/lldb/Interpreter/ScriptInterpreterPython.h b/include/lldb/Interpreter/ScriptInterpreterPython.h
index de87443..fba3c6a 100644
--- a/include/lldb/Interpreter/ScriptInterpreterPython.h
+++ b/include/lldb/Interpreter/ScriptInterpreterPython.h
@@ -366,6 +366,7 @@
     bool m_session_is_active;
     bool m_pty_slave_is_open;
     bool m_valid_session;
+    PyThreadState *m_command_thread_state;
 };
 } // namespace lldb_private
 
diff --git a/source/Host/common/Mutex.cpp b/source/Host/common/Mutex.cpp
index 4b0f2f9..fb666c5 100644
--- a/source/Host/common/Mutex.cpp
+++ b/source/Host/common/Mutex.cpp
@@ -27,6 +27,7 @@
 // Enable extra mutex error checking
 #ifdef LLDB_CONFIGURATION_DEBUG
 #define ENABLE_MUTEX_ERROR_CHECKING 1
+#include <inttypes.h>
 #endif
 
 #if ENABLE_MUTEX_ERROR_CHECKING
@@ -352,6 +353,38 @@
     assert (m_failure_message.empty());
     return Mutex::Unlock();
 }
+
+int
+LoggingMutex::Lock ()
+{
+    printf("locking mutex %p by [%4.4" PRIx64 "/%4.4" PRIx64 "]...", this, Host::GetCurrentProcessID(), Host::GetCurrentThreadID());
+    int x = Mutex::Lock();
+    m_locked = true;
+    printf("%d\n",x);
+    return x;
+}
+
+int
+LoggingMutex::Unlock ()
+{
+    printf("unlocking mutex %p by [%4.4" PRIx64 "/%4.4" PRIx64 "]...", this, Host::GetCurrentProcessID(), Host::GetCurrentThreadID());
+    int x = Mutex::Unlock();
+    m_locked = false;
+    printf("%d\n",x);
+    return x;
+}
+
+int
+LoggingMutex::TryLock (const char *failure_message)
+{
+    printf("trylocking mutex %p by [%4.4" PRIx64 "/%4.4" PRIx64 "]...", this, Host::GetCurrentProcessID(), Host::GetCurrentThreadID());
+    int x = Mutex::TryLock(failure_message);
+    if (x == 0)
+        m_locked = true;
+    printf("%d\n",x);
+    return x;
+}
+
 #endif
-    
+
 
diff --git a/source/Interpreter/ScriptInterpreterPython.cpp b/source/Interpreter/ScriptInterpreterPython.cpp
index a94f870..78f8524 100644
--- a/source/Interpreter/ScriptInterpreterPython.cpp
+++ b/source/Interpreter/ScriptInterpreterPython.cpp
@@ -371,7 +371,22 @@
             break;
             
         case eInputReaderInterrupt:
-            reader.SetIsDone(true);
+        {
+            PyThreadState* state = _PyThreadState_Current;
+            if (!state)
+                state = script_interpreter->m_command_thread_state;
+            if (state)
+            {
+                long tid = state->thread_id;
+                _PyThreadState_Current = state;
+                int num_threads = PyThreadState_SetAsyncExc(tid, PyExc_KeyboardInterrupt);
+                if (log)
+                    log->Printf("ScriptInterpreterPython::NonInteractiveInputReaderCallback, eInputReaderInterrupt, tid = %ld, num_threads = %d, state = %p",
+                                tid,num_threads,state);
+            }
+            else if (log)
+                log->Printf("ScriptInterpreterPython::NonInteractiveInputReaderCallback, eInputReaderInterrupt, state = NULL");
+        }
             break;
             
         case eInputReaderEndOfFile:
@@ -446,7 +461,8 @@
     m_dictionary_name (interpreter.GetDebugger().GetInstanceName().AsCString()),
     m_terminal_state (),
     m_session_is_active (false),
-    m_valid_session (true)
+    m_valid_session (true),
+    m_command_thread_state (NULL)
 {
 
     static int g_initialized = false;
@@ -2643,8 +2659,16 @@
         Locker py_lock(this,
                        Locker::AcquireLock | Locker::InitSession | Locker::InitGlobals,
                        Locker::FreeLock | Locker::TearDownSession);
+
         SynchronicityHandler synch_handler(debugger_sp,
                                            synchronicity);
+
+        // we need to save the thread state when we first start the command
+        // because we might decide to interrupt it while some action is taking
+        // place outside of Python (e.g. printing to screen, waiting for the network, ...)
+        // in that case, _PyThreadState_Current will be NULL - and we would be unable
+        // to set the asynchronous exception - not a desirable situation
+        m_command_thread_state = _PyThreadState_Current;
         
         PythonInputReaderManager py_input(this);