Index: test/tools/lldb-mi/TestMiExit.py =================================================================== --- test/tools/lldb-mi/TestMiExit.py +++ test/tools/lldb-mi/TestMiExit.py @@ -28,10 +28,7 @@ # Test -gdb-exit: try to exit and check that program is finished self.runCmd("-gdb-exit") - self.runCmd("") #FIXME hangs here on Linux; extra return is needed self.expect("\^exit") - import pexpect - self.expect(pexpect.EOF) @lldbmi_test @expectedFailureWindows("llvm.org/pr22274: need a pexpect replacement for windows") Index: tools/lldb-mi/MICmnStreamStdinLinux.h =================================================================== --- tools/lldb-mi/MICmnStreamStdinLinux.h +++ tools/lldb-mi/MICmnStreamStdinLinux.h @@ -69,4 +69,5 @@ const MIuint m_constBufferSize; FILE *m_pStdin; MIchar *m_pCmdBuffer; + bool m_waitForInput; }; Index: tools/lldb-mi/MICmnStreamStdinLinux.cpp =================================================================== --- tools/lldb-mi/MICmnStreamStdinLinux.cpp +++ tools/lldb-mi/MICmnStreamStdinLinux.cpp @@ -20,10 +20,8 @@ //-- // Third Party Headers: -#if defined(__APPLE__) #include #include // For STDIN_FILENO -#endif // defined( __APPLE__ ) #include // For std::strerror() // In-house headers: @@ -43,6 +41,7 @@ : m_constBufferSize(1024) , m_pStdin(nullptr) , m_pCmdBuffer(nullptr) + , m_waitForInput(false) { } @@ -153,27 +152,42 @@ bool CMICmnStreamStdinLinux::InputAvailable(bool &vwbAvail) { -#if defined(__APPLE__) // The code below is needed on OSX where lldb-mi hangs when doing -exec-run. - // The hang seems to come from calling fgets and fileno from different thread. - // Although this problem was not observed on Linux. + // Similar behavior has been observed on other systems too. See + // http://llvm.org/bugs/show_bug.cgi?id=22411 // A solution based on 'ioctl' was initially committed but it seems to make // lldb-mi takes much more processor time. The solution based on 'select' works // well but it seems to slow the execution of lldb-mi tests a lot on Linux. - // As a result, this code is #defined to run only on OSX. + // The reason for delay was behavior of select in multithreaded app. + // The man pages for select suggest that closing a fd which is monitored + // by select from another thread has no effect. It effectively caused the + // application to block in select forever and test cases timed out. + // To fix this issue, I have introduced the a timeout using 3rd arg of select. It + // gives us chance to see if other thread has asked us to stop waiting for + // input by setting m_waitForInput to false. fd_set setOfStdin; FD_ZERO(&setOfStdin); FD_SET(STDIN_FILENO, &setOfStdin); - - // Wait while input would be available - if (::select(STDIN_FILENO + 1, &setOfStdin, nullptr, nullptr, nullptr) == -1) - { - vwbAvail = false; - return MIstatus::failure; + struct timeval tv; + tv.tv_sec = 1; + tv.tv_usec = 0; + + while (m_waitForInput) + { + int ret = ::select(STDIN_FILENO + 1, &setOfStdin, nullptr, nullptr, &tv); + if (ret == 0) // Timeout. Loop back if m_waitForInput is true + continue; + else if (ret == -1) // Error condition. Return + { + vwbAvail = false; + return MIstatus::failure; + } + else // Have some valid input + { + vwbAvail = true; + return MIstatus::success; + } } - -#endif // defined( __APPLE__ ) - vwbAvail = true; return MIstatus::success; } @@ -221,5 +235,5 @@ void CMICmnStreamStdinLinux::InterruptReadLine(void) { - fclose(stdin); + m_waitForInput = true; } Index: tools/lldb-mi/MIDriver.cpp =================================================================== --- tools/lldb-mi/MIDriver.cpp +++ tools/lldb-mi/MIDriver.cpp @@ -518,9 +518,10 @@ // printf( "%s\n", vStdInBuffer.c_str() ); // fflush( stdout ); - // Special case look for the quit command here so stop monitoring stdin stream - // So we do not go back to fgetc() and wait and hang thread on exit - if (vStdInBuffer == "quit") + // Special case look for the quit or -gdb-exit command here to stop monitoring stdin stream. + // Otherwise the stdin monitoring thread will go and starts waiting for + // input and needs an extra return before exiting. + if ((vStdInBuffer == "quit") || (vStdInBuffer.find("-gdb-exit") != std::string::npos)) vrwbYesExit = true; // 1. Put new line in the queue container by stdin monitor thread