From efed613172d902cf2ce49f68dd36f15a29a34dab Mon Sep 17 00:00:00 2001 From: Caroline Tice Date: Fri, 19 Nov 2010 20:47:54 +0000 Subject: [PATCH] Add the ability to catch and do the right thing with Interrupts (often control-c) and end-of-file (often control-d). llvm-svn: 119837 --- lldb/include/lldb/API/SBDebugger.h | 9 ++++ lldb/include/lldb/Core/Communication.h | 2 +- lldb/include/lldb/Core/Debugger.h | 6 +++ lldb/include/lldb/lldb-enumerations.h | 3 ++ lldb/source/API/SBDebugger.cpp | 22 ++++++++ .../CommandObjectBreakpointCommand.cpp | 23 ++++++++ .../Commands/CommandObjectExpression.cpp | 11 ++++ lldb/source/Core/Communication.cpp | 11 ++-- lldb/source/Core/ConnectionFileDescriptor.cpp | 14 ++++- lldb/source/Core/Debugger.cpp | 45 ++++++++++++++-- lldb/source/Core/InputReader.cpp | 4 ++ .../source/Interpreter/CommandInterpreter.cpp | 6 +++ .../Interpreter/ScriptInterpreterPython.cpp | 54 ++++++++----------- .../gdb-remote/GDBRemoteCommunication.cpp | 3 +- .../gdb-remote/GDBRemoteCommunication.h | 2 +- lldb/source/Target/Process.cpp | 14 +++-- lldb/tools/driver/Driver.cpp | 40 ++++++++++++++ lldb/tools/driver/IOChannel.cpp | 10 ++-- 18 files changed, 226 insertions(+), 53 deletions(-) diff --git a/lldb/include/lldb/API/SBDebugger.h b/lldb/include/lldb/API/SBDebugger.h index baeb162c9200..b1f99cde20ea 100644 --- a/lldb/include/lldb/API/SBDebugger.h +++ b/lldb/include/lldb/API/SBDebugger.h @@ -149,6 +149,12 @@ public: void DispatchInput (void *baton, const void *data, size_t data_len); + void + DispatchInputInterrupt (); + + void + DispatchInputEndOfFile (); + void PushInputReader (lldb::SBInputReader &reader); @@ -173,6 +179,9 @@ public: void SetTerminalWidth (uint32_t term_width); + lldb::user_id_t + GetID (); + const char * GetPrompt() const; diff --git a/lldb/include/lldb/Core/Communication.h b/lldb/include/lldb/Core/Communication.h index d7e307fa0450..834b25a0389d 100644 --- a/lldb/include/lldb/Core/Communication.h +++ b/lldb/include/lldb/Core/Communication.h @@ -376,7 +376,7 @@ protected: /// The number of bytes to append to the cache. //------------------------------------------------------------------ virtual void - AppendBytesToCache (const uint8_t *src, size_t src_len, bool broadcast); + AppendBytesToCache (const uint8_t *src, size_t src_len, bool broadcast, lldb::ConnectionStatus status); //------------------------------------------------------------------ /// Get any available bytes from our data cache. If this call diff --git a/lldb/include/lldb/Core/Debugger.h b/lldb/include/lldb/Core/Debugger.h index 02df798e7b33..b121ff9c11ab 100644 --- a/lldb/include/lldb/Core/Debugger.h +++ b/lldb/include/lldb/Core/Debugger.h @@ -330,6 +330,12 @@ public: TargetList& GetTargetList (); + void + DispatchInputInterrupt (); + + void + DispatchInputEndOfFile (); + void DispatchInput (const char *bytes, size_t bytes_len); diff --git a/lldb/include/lldb/lldb-enumerations.h b/lldb/include/lldb/lldb-enumerations.h index 0afc48433653..ef180119398a 100644 --- a/lldb/include/lldb/lldb-enumerations.h +++ b/lldb/include/lldb/lldb-enumerations.h @@ -287,6 +287,7 @@ typedef enum ReturnStatus typedef enum ConnectionStatus { eConnectionStatusSuccess, // Success + eConnectionStatusEndOfFile, // End-of-file encountered eConnectionStatusError, // Check GetError() for details eConnectionStatusTimedOut, // Request timed out eConnectionStatusNoConnection, // No connection @@ -397,6 +398,8 @@ typedef enum InputReaderAction eInputReaderReactivate, // reader is on top of the stack again after another reader was popped off eInputReaderDeactivate, // another reader was pushed on the stack eInputReaderGotToken, // reader got one of its tokens (granularity) + eInputReaderInterrupt, // reader received an interrupt signal (probably from a control-c) + eInputReaderEndOfFile, // reader received an EOF char (probably from a control-d) eInputReaderDone // reader was just popped off the stack and is done } InputReaderAction; diff --git a/lldb/source/API/SBDebugger.cpp b/lldb/source/API/SBDebugger.cpp index 1721d24264f4..2777741ee5ee 100644 --- a/lldb/source/API/SBDebugger.cpp +++ b/lldb/source/API/SBDebugger.cpp @@ -658,6 +658,20 @@ SBDebugger::DispatchInput (void *baton, const void *data, size_t data_len) m_opaque_sp->DispatchInput ((const char *) data, data_len); } +void +SBDebugger::DispatchInputInterrupt () +{ + if (m_opaque_sp) + m_opaque_sp->DispatchInputInterrupt (); +} + +void +SBDebugger::DispatchInputEndOfFile () +{ + if (m_opaque_sp) + m_opaque_sp->DispatchInputEndOfFile (); +} + void SBDebugger::PushInputReader (SBInputReader &reader) { @@ -837,3 +851,11 @@ SBDebugger::GetDescription (SBStream &description) return true; } + +lldb::user_id_t +SBDebugger::GetID() +{ + if (m_opaque_sp) + return m_opaque_sp->GetID(); + return LLDB_INVALID_UID; +} diff --git a/lldb/source/Commands/CommandObjectBreakpointCommand.cpp b/lldb/source/Commands/CommandObjectBreakpointCommand.cpp index e8bd399b131a..aad7158d28f8 100644 --- a/lldb/source/Commands/CommandObjectBreakpointCommand.cpp +++ b/lldb/source/Commands/CommandObjectBreakpointCommand.cpp @@ -468,6 +468,29 @@ CommandObjectBreakpointCommandAdd::GenerateBreakpointCommandCallback } break; + case eInputReaderInterrupt: + { + // Finish, and cancel the breakpoint command. + reader.SetIsDone (true); + BreakpointOptions *bp_options = (BreakpointOptions *) baton; + if (bp_options) + { + Baton *bp_options_baton = bp_options->GetBaton (); + if (bp_options_baton) + { + ((BreakpointOptions::CommandData *) bp_options_baton->m_data)->user_source.Clear(); + ((BreakpointOptions::CommandData *) bp_options_baton->m_data)->script_source.Clear(); + } + } + ::fprintf (out_fh, "Warning: No command attached to breakpoint.\n"); + ::fflush (out_fh); + } + break; + + case eInputReaderEndOfFile: + reader.SetIsDone (true); + break; + case eInputReaderDone: break; } diff --git a/lldb/source/Commands/CommandObjectExpression.cpp b/lldb/source/Commands/CommandObjectExpression.cpp index 436b2e7540c6..32236f8c9177 100644 --- a/lldb/source/Commands/CommandObjectExpression.cpp +++ b/lldb/source/Commands/CommandObjectExpression.cpp @@ -199,7 +199,18 @@ CommandObjectExpression::MultiLineExpressionCallback // ::fprintf (out_fh, "%3u: ", cmd_object_expr->m_expr_line_count); break; + case eInputReaderInterrupt: + cmd_object_expr->m_expr_lines.clear(); + reader.SetIsDone (true); + reader.GetDebugger().GetOutputStream().Printf("%s\n", "Expression evaluation cancelled."); + break; + + case eInputReaderEndOfFile: + reader.SetIsDone (true); + break; + case eInputReaderDone: + if (cmd_object_expr->m_expr_lines.size() > 0) { cmd_object_expr->EvaluateExpression (cmd_object_expr->m_expr_lines.c_str(), reader.GetDebugger().GetOutputStream(), diff --git a/lldb/source/Core/Communication.cpp b/lldb/source/Core/Communication.cpp index f6c689c7aa50..22a9f9ae293b 100644 --- a/lldb/source/Core/Communication.cpp +++ b/lldb/source/Core/Communication.cpp @@ -260,12 +260,13 @@ Communication::GetCachedBytes (void *dst, size_t dst_len) } void -Communication::AppendBytesToCache (const uint8_t * bytes, size_t len, bool broadcast) +Communication::AppendBytesToCache (const uint8_t * bytes, size_t len, bool broadcast, ConnectionStatus status) { lldb_private::LogIfAnyCategoriesSet (LIBLLDB_LOG_COMMUNICATION, "%p Communication::AppendBytesToCache (src = %p, src_len = %zu, broadcast = %i)", this, bytes, len, broadcast); - if (bytes == NULL || len == 0) + if ((bytes == NULL || len == 0) + && (status != eConnectionStatusEndOfFile)) return; if (m_callback) { @@ -319,12 +320,16 @@ Communication::ReadThread (void *p) { size_t bytes_read = comm->ReadFromConnection (buf, sizeof(buf), status, &error); if (bytes_read > 0) - comm->AppendBytesToCache (buf, bytes_read, true); + comm->AppendBytesToCache (buf, bytes_read, true, status); + else if ((bytes_read == 0) + && status == eConnectionStatusEndOfFile) + comm->AppendBytesToCache (buf, bytes_read, true, status); } switch (status) { case eConnectionStatusSuccess: + case eConnectionStatusEndOfFile: break; case eConnectionStatusNoConnection: // No connection diff --git a/lldb/source/Core/ConnectionFileDescriptor.cpp b/lldb/source/Core/ConnectionFileDescriptor.cpp index 96d70bc10125..bdf550e59537 100644 --- a/lldb/source/Core/ConnectionFileDescriptor.cpp +++ b/lldb/source/Core/ConnectionFileDescriptor.cpp @@ -156,8 +156,18 @@ ConnectionFileDescriptor::Read (void *dst, size_t dst_len, ConnectionStatus &sta ssize_t bytes_read = ::read (m_fd, dst, dst_len); if (bytes_read == 0) { - error.SetErrorStringWithFormat("End-of-file.\n"); - status = eConnectionStatusLostConnection; + // 'read' did not return an error, but it didn't return any bytes either ==> End-of-File. + // If the file descriptor is still valid, then we don't return an error; otherwise we do. + // This allows whoever called us to act on the end-of-file, with a valid file descriptor, if they wish. + if (fcntl (m_fd, F_GETFL, 0) >= 0) + { + error.Clear(); // End-of-file, but not an error. Pass along for the end-of-file handlers. + } + else + { + error.SetErrorStringWithFormat("End-of-file.\n"); + } + status = eConnectionStatusEndOfFile; } else if (bytes_read < 0) { diff --git a/lldb/source/Core/Debugger.cpp b/lldb/source/Core/Debugger.cpp index 617edd2fdf41..a06095fa80be 100644 --- a/lldb/source/Core/Debugger.cpp +++ b/lldb/source/Core/Debugger.cpp @@ -338,19 +338,56 @@ Debugger::GetTargetList () void Debugger::DispatchInputCallback (void *baton, const void *bytes, size_t bytes_len) { - ((Debugger *)baton)->DispatchInput ((char *)bytes, bytes_len); -} + if (bytes_len > 0) + ((Debugger *)baton)->DispatchInput ((char *)bytes, bytes_len); + else + ((Debugger *)baton)->DispatchInputEndOfFile (); +} void Debugger::DispatchInput (const char *bytes, size_t bytes_len) { -// if (bytes == NULL || bytes_len == 0) -// return; + if (bytes == NULL || bytes_len == 0) + return; WriteToDefaultReader (bytes, bytes_len); } +void +Debugger::DispatchInputInterrupt () +{ + m_input_reader_data.clear(); + + if (!m_input_readers.empty()) + { + while (CheckIfTopInputReaderIsDone ()) ; + + InputReaderSP reader_sp(m_input_readers.top()); + if (reader_sp) + reader_sp->Notify (eInputReaderInterrupt); + + while (CheckIfTopInputReaderIsDone ()) ; + } +} + +void +Debugger::DispatchInputEndOfFile () +{ + m_input_reader_data.clear(); + + if (!m_input_readers.empty()) + { + while (CheckIfTopInputReaderIsDone ()) ; + + InputReaderSP reader_sp(m_input_readers.top()); + if (reader_sp) + reader_sp->Notify (eInputReaderEndOfFile); + + while (CheckIfTopInputReaderIsDone ()) ; + } +} + void Debugger::WriteToDefaultReader (const char *bytes, size_t bytes_len) { diff --git a/lldb/source/Core/InputReader.cpp b/lldb/source/Core/InputReader.cpp index 06cfb5b5ff67..b8a61c299531 100644 --- a/lldb/source/Core/InputReader.cpp +++ b/lldb/source/Core/InputReader.cpp @@ -324,6 +324,10 @@ InputReader::Notify (InputReaderAction notification) m_active = false; break; + case eInputReaderInterrupt: + case eInputReaderEndOfFile: + break; + case eInputReaderGotToken: return; // We don't notify the tokens here, it is done in HandleRawBytes } diff --git a/lldb/source/Interpreter/CommandInterpreter.cpp b/lldb/source/Interpreter/CommandInterpreter.cpp index 0dc4a34aa7b2..86a635a6b2bf 100644 --- a/lldb/source/Interpreter/CommandInterpreter.cpp +++ b/lldb/source/Interpreter/CommandInterpreter.cpp @@ -860,6 +860,12 @@ CommandInterpreter::GetConfirmationInputReaderCallback (void *baton, } break; + case eInputReaderInterrupt: + case eInputReaderEndOfFile: + *response_ptr = false; // Assume ^C or ^D means cancel the proposed action + reader.SetIsDone (true); + break; + case eInputReaderDone: break; } diff --git a/lldb/source/Interpreter/ScriptInterpreterPython.cpp b/lldb/source/Interpreter/ScriptInterpreterPython.cpp index 1927531fb890..1e3a308e83c8 100644 --- a/lldb/source/Interpreter/ScriptInterpreterPython.cpp +++ b/lldb/source/Interpreter/ScriptInterpreterPython.cpp @@ -272,11 +272,6 @@ ScriptInterpreterPython::ScriptInterpreterPython (CommandInterpreter &interprete PyRun_SimpleString (run_string.GetData()); PyRun_SimpleString ("sys.stdin = new_stdin"); - PyRun_SimpleString ("new_mode = tcgetattr(new_stdin)"); - PyRun_SimpleString ("new_mode[3] = new_mode[3] | ECHO | ICANON"); - PyRun_SimpleString ("new_mode[6][VEOF] = 255"); - PyRun_SimpleString ("tcsetattr (new_stdin, TCSANOW, new_mode)"); - run_string.Clear(); run_string.Printf ("lldb.debugger_unique_id = %d", interpreter.GetDebugger().GetID()); PyRun_SimpleString (run_string.GetData()); @@ -302,26 +297,10 @@ ScriptInterpreterPython::ExecuteOneLine (const char *command, CommandReturnObjec { int success; - - // Save the current input file handle state before executing the command. - int input_fd; - struct termios tmp_termios; - bool valid_termios = false; - FILE *input_fh = m_interpreter.GetDebugger().GetInputFileHandle(); - if (input_fh != NULL) - { - input_fd = ::fileno (input_fh); - valid_termios = ::tcgetattr (input_fd, &tmp_termios) == 0; - } - success = PyRun_SimpleString (command); if (success == 0) return true; - // Restore the input file handle state after executing the command. - if (valid_termios) - ::tcsetattr (input_fd, TCSANOW, &tmp_termios); - // The one-liner failed. Append the error message. if (result) result->AppendErrorWithFormat ("python failed attempting to evaluate '%s'\n", command); @@ -366,12 +345,6 @@ ScriptInterpreterPython::InputReaderCallback script_interpreter->m_termios_valid = ::tcgetattr (input_fd, &script_interpreter->m_termios) == 0; - if (script_interpreter->m_termios_valid) - { - struct termios tmp_termios = script_interpreter->m_termios; - tmp_termios.c_cc[VEOF] = _POSIX_VDISABLE; - ::tcsetattr (input_fd, TCSANOW, &tmp_termios); - } char error_str[1024]; if (script_interpreter->m_embedded_python_pty.OpenFirstAvailableMaster (O_RDWR|O_NOCTTY, error_str, sizeof(error_str))) @@ -411,6 +384,14 @@ ScriptInterpreterPython::InputReaderCallback case eInputReaderReactivate: break; + + case eInputReaderInterrupt: + ::write (script_interpreter->m_embedded_python_pty.GetMasterFileDescriptor(), "raise KeyboardInterrupt\n", 24); + break; + + case eInputReaderEndOfFile: + ::write (script_interpreter->m_embedded_python_pty.GetMasterFileDescriptor(), "quit()\n", 7); + break; case eInputReaderGotToken: if (script_interpreter->m_embedded_python_pty.GetMasterFileDescriptor() != -1) @@ -724,6 +705,17 @@ ScriptInterpreterPython::GenerateBreakpointOptionsCommandCallback } break; + case eInputReaderEndOfFile: + case eInputReaderInterrupt: + // Control-c (SIGINT) & control-d both mean finish & exit. + reader.SetIsDone(true); + + // Control-c (SIGINT) ALSO means cancel; do NOT create a breakpoint command. + if (notification == eInputReaderInterrupt) + commands_in_progress.Clear(); + + // Fall through here... + case eInputReaderDone: { BreakpointOptions *bp_options = (BreakpointOptions *)baton; @@ -843,6 +835,10 @@ ScriptInterpreterPython::GenerateBreakpointCommandCallbackData (StringList &user int num_lines = user_input.GetSize (); StreamString sstr; + // Check to see if we have any data; if not, just return. + if (user_input.GetSize() == 0) + return false; + // Take what the user wrote, wrap it all up inside one big auto-generated Python function, passing in the // frame and breakpoint location as parameters to the function. @@ -933,10 +929,6 @@ ScriptInterpreterPython::RunEmbeddedPythonInterpreter (lldb::thread_arg_t baton) PyRun_SimpleString ("save_stdin = sys.stdin"); run_string.Printf ("sys.stdin = open ('%s', 'r')", pty_slave_name); PyRun_SimpleString (run_string.GetData()); - PyRun_SimpleString ("new_mode = tcgetattr(sys.stdin)"); - PyRun_SimpleString ("new_mode[3] = new_mode[3] | ECHO | ICANON"); - PyRun_SimpleString ("new_mode[6][VEOF] = 255"); - PyRun_SimpleString ("tcsetattr (sys.stdin, TCSANOW, new_mode)"); // The following call drops into the embedded interpreter loop and stays there until the // user chooses to exit from the Python interpreter. diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp index ee03424e528c..63fb35783ea5 100644 --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp @@ -551,7 +551,8 @@ GDBRemoteCommunication::WaitForPacketNoLock (StringExtractorGDBRemote &response, } void -GDBRemoteCommunication::AppendBytesToCache (const uint8_t *src, size_t src_len, bool broadcast) +GDBRemoteCommunication::AppendBytesToCache (const uint8_t *src, size_t src_len, bool broadcast, + ConnectionStatus status) { // Put the packet data into the buffer in a thread safe fashion Mutex::Locker locker(m_bytes_mutex); diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h index ac49bf271f1b..e7ef38ab1e0f 100644 --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h @@ -110,7 +110,7 @@ public: // Communication overrides //------------------------------------------------------------------ virtual void - AppendBytesToCache (const uint8_t *src, size_t src_len, bool broadcast); + AppendBytesToCache (const uint8_t *src, size_t src_len, bool broadcast, lldb::ConnectionStatus status); lldb::pid_t diff --git a/lldb/source/Target/Process.cpp b/lldb/source/Target/Process.cpp index e50ecf27ef0c..cebec5bbd31f 100644 --- a/lldb/source/Target/Process.cpp +++ b/lldb/source/Target/Process.cpp @@ -1478,10 +1478,6 @@ Process::Halt () } else { - // Since we are eating the event, we need to update our state - // otherwise the process state will not match reality... - SetPublicState(state); - if (StateIsStoppedState (state)) { // We caused the process to interrupt itself, so mark this @@ -1508,7 +1504,7 @@ Process::Halt () // stopped the process, intercepted the event and set the interrupted // bool in the event. if (event_sp) - BroadcastEvent(event_sp); + m_private_state_broadcaster.BroadcastEvent(event_sp); } return error; @@ -2175,6 +2171,14 @@ Process::ProcessInputReaderCallback (void *baton, } break; + case eInputReaderInterrupt: + process->Halt (); + break; + + case eInputReaderEndOfFile: + process->AppendSTDOUT ("^D", 2); + break; + case eInputReaderDone: break; diff --git a/lldb/tools/driver/Driver.cpp b/lldb/tools/driver/Driver.cpp index 1bc314882f6d..29b50f05a55d 100644 --- a/lldb/tools/driver/Driver.cpp +++ b/lldb/tools/driver/Driver.cpp @@ -40,6 +40,7 @@ static void reset_stdin_termios (); static struct termios g_old_stdin_termios; static char *g_debugger_name = (char *) ""; +static Driver *g_driver = NULL; // In the Driver::MainLoop, we change the terminal settings. This function is // added as an atexit handler to make sure we clean them up. @@ -98,10 +99,13 @@ Driver::Driver () : g_debugger_name = (char *) m_debugger.GetInstanceName(); if (g_debugger_name == NULL) g_debugger_name = (char *) ""; + g_driver = this; } Driver::~Driver () { + g_driver = NULL; + g_debugger_name = NULL; } void @@ -1091,6 +1095,23 @@ Driver::EditLineInputReaderCallback case eInputReaderDeactivate: break; + case eInputReaderInterrupt: + if (driver->m_io_channel_ap.get() != NULL) + { + driver->m_io_channel_ap->OutWrite ("^C\n", 3); + driver->m_io_channel_ap->RefreshPrompt(); + } + break; + + case eInputReaderEndOfFile: + if (driver->m_io_channel_ap.get() != NULL) + { + driver->m_io_channel_ap->OutWrite ("^D\n", 3); + driver->m_io_channel_ap->RefreshPrompt (); + } + write (driver->m_editline_pty.GetMasterFileDescriptor(), "quit\n", 5); + break; + case eInputReaderGotToken: write (driver->m_editline_pty.GetMasterFileDescriptor(), bytes, bytes_len); break; @@ -1370,6 +1391,24 @@ sigwinch_handler (int signo) } } +void +sigint_handler (int signo) +{ + static bool g_interrupt_sent = false; + if (g_driver) + { + if (!g_interrupt_sent) + { + g_interrupt_sent = true; + g_driver->GetDebugger().DispatchInputInterrupt(); + g_interrupt_sent = false; + return; + } + } + + exit (signo); +} + int main (int argc, char const *argv[]) { @@ -1379,6 +1418,7 @@ main (int argc, char const *argv[]) signal (SIGPIPE, SIG_IGN); signal (SIGWINCH, sigwinch_handler); + signal (SIGINT, sigint_handler); // Create a scope for driver so that the driver object will destroy itself // before SBDebugger::Terminate() is called. diff --git a/lldb/tools/driver/IOChannel.cpp b/lldb/tools/driver/IOChannel.cpp index 004e1da9140a..bf7891abdb72 100644 --- a/lldb/tools/driver/IOChannel.cpp +++ b/lldb/tools/driver/IOChannel.cpp @@ -448,12 +448,12 @@ IOChannel::RefreshPrompt () if (! IsGettingCommand()) return; - // Compare the current time versus the last time el_gets was called. If less than - // 10000 microseconds (10000000 nanoseconds) have elapsed, wait 10000 microseconds, to ensure el_gets had time - // to finish writing the prompt before we start writing here. + // Compare the current time versus the last time el_gets was called. If less than 40 milliseconds + // (40,0000 microseconds or 40,000,0000 nanoseconds) have elapsed, wait 40,0000 microseconds, to ensure el_gets had + // time to finish writing the prompt before we start writing here. - if (ElapsedNanoSecondsSinceEnteringElGets() < 10000000) - usleep (10000); + if (ElapsedNanoSecondsSinceEnteringElGets() < (40 * 1000 * 1000)) + usleep (40 * 1000); // Use the mutex to make sure OutWrite, ErrWrite and Refresh prompt do not interfere with // each other's output.