diff --git a/src/lib/base/EventQueue.cpp b/src/lib/base/EventQueue.cpp index 2afa2fb0..b12c0736 100644 --- a/src/lib/base/EventQueue.cpp +++ b/src/lib/base/EventQueue.cpp @@ -18,12 +18,15 @@ #include "base/EventQueue.h" +#include "mt/Mutex.h" +#include "mt/Lock.h" #include "arch/Arch.h" -#include "base/Log.h" #include "base/SimpleEventQueueBuffer.h" #include "base/Stopwatch.h" #include "base/IEventJob.h" #include "base/EventTypes.h" +#include "base/Log.h" +#include "base/XBase.h" EVENT_TYPE_ACCESSOR(CClient) EVENT_TYPE_ACCESSOR(IStream) @@ -78,7 +81,9 @@ CEventQueue::CEventQueue() : m_typesForCServerApp(NULL), m_typesForIKeyState(NULL), m_typesForIPrimaryScreen(NULL), - m_typesForIScreen(NULL) + m_typesForIScreen(NULL), + m_readyMutex(new CMutex), + m_readyCondVar(new CCondVar(m_readyMutex, false)) { m_mutex = ARCH->newMutex(); ARCH->setSignalHandler(CArch::kINTERRUPT, &interrupt, this); @@ -89,6 +94,9 @@ CEventQueue::CEventQueue() : CEventQueue::~CEventQueue() { delete m_buffer; + delete m_readyCondVar; + delete m_readyMutex; + ARCH->setSignalHandler(CArch::kINTERRUPT, NULL, NULL); ARCH->setSignalHandler(CArch::kTERMINATE, NULL, NULL); ARCH->closeMutex(m_mutex); @@ -98,6 +106,16 @@ void CEventQueue::loop() { m_buffer->init(); + *m_readyCondVar = true; + m_readyCondVar->broadcast(); + + LOG((CLOG_DEBUG "event queue is ready")); + while (!m_pending.empty()) { + LOG((CLOG_DEBUG "add pending events to buffer")); + CEvent& event = m_pending.front(); + addEventToBuffer(event); + m_pending.pop(); + } CEvent event; getEvent(event); @@ -268,18 +286,27 @@ CEventQueue::addEvent(const CEvent& event) dispatchEvent(event); CEvent::deleteData(event); } + else if (!(*m_readyCondVar)) { + m_pending.push(event); + } else { - CArchMutexLock lock(m_mutex); - - // store the event's data locally - UInt32 eventID = saveEvent(event); - - // add it - if (!m_buffer->addEvent(eventID)) { - // failed to send event - removeEvent(eventID); - CEvent::deleteData(event); - } + addEventToBuffer(event); + } +} + +void +CEventQueue::addEventToBuffer(const CEvent& event) +{ + CArchMutexLock lock(m_mutex); + + // store the event's data locally + UInt32 eventID = saveEvent(event); + + // add it + if (!m_buffer->addEvent(eventID)) { + // failed to send event + removeEvent(eventID); + CEvent::deleteData(event); } } @@ -526,6 +553,21 @@ CEventQueue::getSystemTarget() return &m_systemTarget; } +void +CEventQueue::waitForReady() const +{ + double timeout = ARCH->time() + 5; + m_readyCondVar->lock(); + + while (!m_readyCondVar->wait()) { + if(ARCH->time() > timeout) { + throw std::runtime_error("event queue is not ready within 5 sec"); + } + } + + m_readyCondVar->unlock(); +} + // // CEventQueue::CTimer // diff --git a/src/lib/base/EventQueue.h b/src/lib/base/EventQueue.h index 3509d9d0..4d6ef37c 100644 --- a/src/lib/base/EventQueue.h +++ b/src/lib/base/EventQueue.h @@ -18,14 +18,19 @@ #pragma once +#include "mt/CondVar.h" +#include "arch/IArchMultithread.h" #include "base/IEventQueue.h" #include "base/Event.h" #include "base/PriorityQueue.h" #include "base/Stopwatch.h" -#include "arch/IArchMultithread.h" #include "common/stdmap.h" #include "common/stdset.h" +#include + +class CMutex; + //! Event queue /*! An event queue that implements the platform independent parts and @@ -59,13 +64,15 @@ public: virtual CEvent::Type getRegisteredType(const CString& name) const; void* getSystemTarget(); + virtual void waitForReady() const; private: UInt32 saveEvent(const CEvent& event); CEvent removeEvent(UInt32 eventID); bool hasTimerExpired(CEvent& event); double getNextTimerTimeout() const; - + void addEventToBuffer(const CEvent& event); + private: class CTimer { public: @@ -170,6 +177,9 @@ private: IKeyStateEvents* m_typesForIKeyState; IPrimaryScreenEvents* m_typesForIPrimaryScreen; IScreenEvents* m_typesForIScreen; + CMutex* m_readyMutex; + CCondVar* m_readyCondVar; + std::queue m_pending; }; #define EVENT_TYPE_ACCESSOR(type_) \ diff --git a/src/lib/base/IEventQueue.h b/src/lib/base/IEventQueue.h index 41d870ff..c5f81cc8 100644 --- a/src/lib/base/IEventQueue.h +++ b/src/lib/base/IEventQueue.h @@ -176,6 +176,13 @@ public: registerTypeOnce(CEvent::Type& type, const char* name) = 0; + //! Wait for event queue to become ready + /*! + Blocks on the current thread until the event queue is ready for events to + be added. + */ + virtual void waitForReady() const = 0; + //@} //! @name accessors //@{ diff --git a/src/lib/platform/OSXDragSimulator.m b/src/lib/platform/OSXDragSimulator.m index aac66c9f..0164b7f8 100644 --- a/src/lib/platform/OSXDragSimulator.m +++ b/src/lib/platform/OSXDragSimulator.m @@ -28,9 +28,6 @@ COSXDragView* g_dragView = NULL; void runCocoaApp() { - // HACK: sleep, carbon loop should start first. - usleep(1000000); - NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; [NSApplication sharedApplication]; diff --git a/src/lib/platform/OSXScreen.cpp b/src/lib/platform/OSXScreen.cpp index 4a881b94..88832e4d 100644 --- a/src/lib/platform/OSXScreen.cpp +++ b/src/lib/platform/OSXScreen.cpp @@ -18,6 +18,7 @@ #include "platform/OSXScreen.h" +#include "base/EventQueue.h" #include "client/Client.h" #include "platform/OSXClipboard.h" #include "platform/OSXEventQueueBuffer.h" @@ -163,6 +164,10 @@ COSXScreen::COSXScreen(IEventQueue* events, bool isPrimary, bool autoShowHideCur // create thread for monitoring system power state. *m_pmThreadReady = false; +#if defined(MAC_OS_X_VERSION_10_7) + m_carbonLoopMutex = new CMutex(); + m_carbonLoopReady = new CCondVar(m_carbonLoopMutex, false); +#endif LOG((CLOG_DEBUG "starting watchSystemPowerThread")); m_pmWatchThread = new CThread(new TMethodJob (this, &COSXScreen::watchSystemPowerThread)); @@ -255,6 +260,11 @@ COSXScreen::~COSXScreen() delete m_keyState; delete m_screensaver; + +#if defined(MAC_OS_X_VERSION_10_7) + delete m_carbonLoopMutex; + delete m_carbonLoopReady; +#endif } void* @@ -1694,10 +1704,16 @@ COSXScreen::watchSystemPowerThread(void*) } LOG((CLOG_DEBUG "started watchSystemPowerThread")); + + m_events->waitForReady(); - // HACK: sleep, this seem to stop synergy from freezing. - ARCH->sleep(1); - +#if defined(MAC_OS_X_VERSION_10_7) + if (*m_carbonLoopReady == false) { + *m_carbonLoopReady = true; + m_carbonLoopReady->broadcast(); + } +#endif + // start the run loop LOG((CLOG_DEBUG "starting carbon loop")); CFRunLoopRun(); @@ -2100,6 +2116,21 @@ COSXScreen::getDraggingFilename() return m_draggingFilename; } +void +COSXScreen::waitForCarbonLoop() const +{ + double timeout = ARCH->time() + 5; + m_carbonLoopReady->lock(); + + while (!m_carbonLoopReady->wait()) { + if(ARCH->time() > timeout) { + throw std::runtime_error("carbon loop is not ready within 5 sec"); + } + } + + m_carbonLoopReady->unlock(); +} + #pragma GCC diagnostic ignored "-Wdeprecated-declarations" void diff --git a/src/lib/platform/OSXScreen.h b/src/lib/platform/OSXScreen.h index 9ac6b21a..8998c183 100644 --- a/src/lib/platform/OSXScreen.h +++ b/src/lib/platform/OSXScreen.h @@ -47,6 +47,7 @@ class CThread; class COSXKeyState; class COSXScreenSaver; class IEventQueue; +class CMutex; //! Implementation of IPlatformScreen for OS X class COSXScreen : public CPlatformScreen { @@ -98,6 +99,7 @@ public: virtual CString& getDraggingFilename(); const CString& getDropTarget() const { return m_dropTarget; } + void waitForCarbonLoop() const; protected: // IPlatformScreen overrides @@ -344,4 +346,9 @@ private: CThread* m_getDropTargetThread; CString m_dropTarget; + +#if defined(MAC_OS_X_VERSION_10_7) + CMutex* m_carbonLoopMutex; + CCondVar* m_carbonLoopReady; +#endif }; diff --git a/src/lib/synergy/ClientApp.cpp b/src/lib/synergy/ClientApp.cpp index e925fd8d..dcde580f 100644 --- a/src/lib/synergy/ClientApp.cpp +++ b/src/lib/synergy/ClientApp.cpp @@ -548,8 +548,10 @@ CClientApp::mainLoop() this, &CClientApp::runEventsLoop, NULL)); - // HACK: sleep, allow queue to start. - ARCH->sleep(1); + // wait until carbon loop is ready + COSXScreen* screen = dynamic_cast( + s_clientScreen->getPlatformScreen()); + screen->waitForCarbonLoop(); runCocoaApp(); #else diff --git a/src/lib/synergy/Screen.h b/src/lib/synergy/Screen.h index c817034c..cfb79011 100644 --- a/src/lib/synergy/Screen.h +++ b/src/lib/synergy/Screen.h @@ -296,6 +296,8 @@ public: virtual void getShape(SInt32& x, SInt32& y, SInt32& width, SInt32& height) const; virtual void getCursorPos(SInt32& x, SInt32& y) const; + + IPlatformScreen* getPlatformScreen() { return m_screen; } protected: void enablePrimary(); diff --git a/src/lib/synergy/ServerApp.cpp b/src/lib/synergy/ServerApp.cpp index 59bcb7ae..e54cd92d 100644 --- a/src/lib/synergy/ServerApp.cpp +++ b/src/lib/synergy/ServerApp.cpp @@ -798,8 +798,10 @@ CServerApp::mainLoop() this, &CServerApp::runEventsLoop, NULL)); - // HACK: sleep, allow queue to start. - ARCH->sleep(1); + // wait until carbon loop is ready + COSXScreen* screen = dynamic_cast( + s_serverScreen->getPlatformScreen()); + screen->waitForCarbonLoop(); runCocoaApp(); #else diff --git a/src/test/mock/synergy/MockEventQueue.h b/src/test/mock/synergy/MockEventQueue.h index fc0077ae..64405c7d 100644 --- a/src/test/mock/synergy/MockEventQueue.h +++ b/src/test/mock/synergy/MockEventQueue.h @@ -61,4 +61,5 @@ public: MOCK_METHOD0(forIKeyState, IKeyStateEvents&()); MOCK_METHOD0(forIPrimaryScreen, IPrimaryScreenEvents&()); MOCK_METHOD0(forIScreen, IScreenEvents&()); + MOCK_CONST_METHOD0(waitForReady, void()); };