diff --git a/src/common/sckipc.cpp b/src/common/sckipc.cpp index 9f7ae024f7..1942ee9ad8 100644 --- a/src/common/sckipc.cpp +++ b/src/common/sckipc.cpp @@ -36,8 +36,11 @@ #include "wx/log.h" #include "wx/event.h" #include "wx/module.h" + #include "wx/app.h" #endif +#include "wx/evtloop.h" + #include #include #include @@ -1700,6 +1703,25 @@ bool wxTCPEventHandler::SendAndGetReply(wxIPCMessageBase& send_msg, return SendAndGetReply_WorkerThread(send_msg, expected_code, return_msgptr); } +namespace +{ + +// RAII guard that leaves an already-entered critical section on destruction +// (the inverse of wxCRIT_SECT_LOCKER, which enters on construction). Used when +// the section was acquired with TryEnter() in a custom wait loop. +class CritSectLeaver +{ +public: + explicit CritSectLeaver(wxCriticalSection& cs) : m_cs(cs) {} + ~CritSectLeaver() { m_cs.Leave(); } + +private: + wxCriticalSection& m_cs; + wxDECLARE_NO_COPY_CLASS(CritSectLeaver); +}; + +} // anonymous namespace + // Take over socket processing from OnSocketInput until we find the // return message. bool wxTCPEventHandler::SendAndGetReply_MainThread(wxIPCMessageBase& send_msg, @@ -1707,7 +1729,29 @@ bool wxTCPEventHandler::SendAndGetReply_MainThread(wxIPCMessageBase& send_msg, wxSocketBase* socket, wxIPCMessageBase** return_msgptr) { - wxCRIT_SECT_LOCKER(find_message_lock, m_cs_awaiting_reply); + // A worker thread may have triggered m_cs_awaiting_reply while parked in + // RunOnMainThread(), waiting for *this* (the main) thread to run its + // marshalled socket I/O. Blocking on the lock here would stop us pumping the + // event loop and deadlock. So acquire it without blocking the loop: spin on + // TryEnter(), pumping pending events (the worker's RunOnMainThread() jobs) + // and socket I/O (its reply) between attempts so the worker can finish and + // release the lock. + while ( !m_cs_awaiting_reply.TryEnter() ) + { + wxEventLoopBase* const loop = wxEventLoopBase::GetActive(); + if ( !loop ) + { + // No event loop to pump, so no worker can be waiting on us; the only + // safe thing left is to block until the lock is free. + m_cs_awaiting_reply.Enter(); + break; + } + + if ( wxTheApp ) + wxTheApp->ProcessPendingEvents(); + loop->DispatchTimeout(1); + } + CritSectLeaver find_message_lock(m_cs_awaiting_reply); wxCRIT_SECT_LOCKER(socket_processing_lock, m_cs_socket_processing);