From fd76cbc70d68b4ce21451fe560243ace745953cf Mon Sep 17 00:00:00 2001 From: ed Date: Thu, 30 May 2019 15:16:19 +0100 Subject: [PATCH] Linux: Refactored InternalMessageQueue This commit simplifies the Linux event loop code and adds methods to allow arbitrary file descriptors to registered. Also adds a method to register external event loops to be used instead of JUCE's internal event loop. --- .../juce_events/native/juce_linux_EventLoop.h | 34 +-- .../native/juce_linux_Messaging.cpp | 242 +++++++++--------- .../juce_gui_basics/native/juce_linux_X11.cpp | 10 +- .../native/juce_linux_X11_Windowing.cpp | 10 + 4 files changed, 153 insertions(+), 143 deletions(-) diff --git a/modules/juce_events/native/juce_linux_EventLoop.h b/modules/juce_events/native/juce_linux_EventLoop.h index ce99bd7ff0..cb7aded887 100644 --- a/modules/juce_events/native/juce_linux_EventLoop.h +++ b/modules/juce_events/native/juce_linux_EventLoop.h @@ -25,31 +25,23 @@ namespace juce namespace LinuxEventLoop { - struct CallbackFunctionBase - { - virtual ~CallbackFunctionBase() {} - virtual bool operator()(int fd) = 0; - bool active = true; - }; + /** Registers a callback that will be called when a file descriptor is ready for I/O. - template - struct CallbackFunction : public CallbackFunctionBase - { - FdCallbackFunction callback; + This will add the given file descriptor to the internal set of file descriptors + that will be passed to the poll() call. When this file descriptor has data to read + the readCallback will be called. - CallbackFunction (FdCallbackFunction c) : callback (c) {} + @param fd the file descriptor to be monitored + @param readCallback a callback that will be called when the file descriptor has + data to read. The file descriptor will be passed as an argument + */ + void registerFdCallback (int fd, std::function readCallback); - bool operator() (int fd) override { return callback (fd); } - }; + /** Unregisters a previously registered file descriptor. - template - void setWindowSystemFd (int fd, FdCallbackFunction readCallback) - { - setWindowSystemFdInternal (fd, new CallbackFunction (readCallback)); - } - void removeWindowSystemFd() noexcept; - - void setWindowSystemFdInternal (int fd, CallbackFunctionBase* readCallback) noexcept; + @see registerFdCallback + */ + void unregisterFdCallback (int fd); } } // namespace juce diff --git a/modules/juce_events/native/juce_linux_Messaging.cpp b/modules/juce_events/native/juce_linux_Messaging.cpp index 16ebc3f4a0..c9207fb64d 100644 --- a/modules/juce_events/native/juce_linux_Messaging.cpp +++ b/modules/juce_events/native/juce_linux_Messaging.cpp @@ -22,13 +22,6 @@ #include -enum FdType -{ - INTERNAL_QUEUE_FD, - WINDOW_SYSTEM_FD, - FD_COUNT, -}; - namespace juce { @@ -38,30 +31,28 @@ class InternalMessageQueue public: InternalMessageQueue() { - auto ret = ::socketpair (AF_LOCAL, SOCK_STREAM, 0, fd); - ignoreUnused (ret); jassert (ret == 0); + auto err = ::socketpair (AF_LOCAL, SOCK_STREAM, 0, msgpipe); + jassert (err == 0); + ignoreUnused (err); - auto internalQueueCb = [this] (int _fd) - { - if (const MessageManager::MessageBase::Ptr msg = this->popNextMessage (_fd)) - { - JUCE_TRY - { - msg->messageCallback(); - return true; - } - JUCE_CATCH_EXCEPTION - } - return false; - }; - - pfds[INTERNAL_QUEUE_FD].fd = getReadHandle(); - pfds[INTERNAL_QUEUE_FD].events = POLLIN; - readCallback[INTERNAL_QUEUE_FD].reset (new LinuxEventLoop::CallbackFunction (internalQueueCb)); + LinuxEventLoop::registerFdCallback (getReadHandle(), + [this] (int fd) + { + if (auto msg = popNextMessage (fd)) + { + JUCE_TRY + { + msg->messageCallback(); + } + JUCE_CATCH_EXCEPTION + } + }); } ~InternalMessageQueue() { + LinuxEventLoop::unregisterFdCallback (getReadHandle()); + close (getReadHandle()); close (getWriteHandle()); @@ -71,64 +62,14 @@ public: //============================================================================== void postMessage (MessageManager::MessageBase* const msg) noexcept { - ScopedLock sl (lock); - queue.add (msg); - - const int maxBytesInSocketQueue = 128; - - if (bytesInSocket < maxBytesInSocketQueue) { - bytesInSocket++; - - ScopedUnlock ul (lock); - const unsigned char x = 0xff; - ssize_t bytesWritten = write (getWriteHandle(), &x, 1); - ignoreUnused (bytesWritten); - } - } - - void setWindowSystemFd (int _fd, LinuxEventLoop::CallbackFunctionBase* _readCallback) - { - jassert (fdCount == 1); - - ScopedLock sl (lock); - - fdCount = 2; - pfds[WINDOW_SYSTEM_FD].fd = _fd; - pfds[WINDOW_SYSTEM_FD].events = POLLIN; - readCallback[WINDOW_SYSTEM_FD].reset (_readCallback); - readCallback[WINDOW_SYSTEM_FD]->active = true; - } - - void removeWindowSystemFd() - { - jassert (fdCount == FD_COUNT); - - ScopedLock sl (lock); - - fdCount = 1; - readCallback[WINDOW_SYSTEM_FD]->active = false; - } - - bool dispatchNextEvent() noexcept - { - for (int counter = 0; counter < fdCount; counter++) - { - const int i = loopCount++; - loopCount %= fdCount; - - if (readCallback[i] != nullptr && readCallback[i]->active) - if ((*readCallback[i]) (pfds[i].fd)) - return true; + ScopedLock sl (lock); + queue.add (msg); } - return false; - } - - bool sleepUntilEvent (const int timeoutMs) - { - const int pnum = poll (pfds, static_cast (fdCount), timeoutMs); - return (pnum > 0); + unsigned char x = 0xff; + auto numBytes = write (getWriteHandle(), &x, 1); + ignoreUnused (numBytes); } //============================================================================== @@ -137,43 +78,114 @@ public: private: CriticalSection lock; ReferenceCountedArray queue; - int fd[2]; - pollfd pfds[FD_COUNT]; - std::unique_ptr readCallback[FD_COUNT]; - int fdCount = 1; - int loopCount = 0; - int bytesInSocket = 0; - int getWriteHandle() const noexcept { return fd[0]; } - int getReadHandle() const noexcept { return fd[1]; } + int msgpipe[2]; - MessageManager::MessageBase::Ptr popNextMessage (int _fd) noexcept + int getWriteHandle() const noexcept { return msgpipe[0]; } + int getReadHandle() const noexcept { return msgpipe[1]; } + + MessageManager::MessageBase::Ptr popNextMessage (int fd) noexcept { + unsigned char x; + auto numBytes = read (fd, &x, 1); + ignoreUnused (numBytes); + const ScopedLock sl (lock); - - if (bytesInSocket > 0) - { - --bytesInSocket; - - const ScopedUnlock ul (lock); - unsigned char x; - ssize_t numBytes = read (_fd, &x, 1); - ignoreUnused (numBytes); - } - return queue.removeAndReturn (0); } }; JUCE_IMPLEMENT_SINGLETON (InternalMessageQueue) +//============================================================================== +struct InternalRunLoop +{ +public: + InternalRunLoop() + { + fdReadCallbacks.reserve (8); + } + + void registerFdCallback (int fd, std::function&& cb) + { + const ScopedLock sl (lock); + + fdReadCallbacks.push_back ({ fd, std::move (cb) }); + pfds.push_back ({ fd, POLLIN, 0 }); + } + + void unregisterFdCallback (int fd) + { + const ScopedLock sl (lock); + + { + auto removePredicate = [=] (const std::pair>& cb) { return cb.first == fd; }; + + fdReadCallbacks.erase (std::remove_if (std::begin (fdReadCallbacks), std::end (fdReadCallbacks), removePredicate), + std::end (fdReadCallbacks)); + } + + { + auto removePredicate = [=] (const pollfd& pfd) { return pfd.fd == fd; }; + + pfds.erase (std::remove_if (std::begin (pfds), std::end (pfds), removePredicate), + std::end (pfds)); + } + } + + bool dispatchPendingEvents() + { + const ScopedLock sl (lock); + + if (poll (&pfds.front(), static_cast (pfds.size()), 0) == 0) + return false; + + bool eventWasSent = false; + + for (auto& pfd : pfds) + { + if (pfd.revents == 0) + continue; + + pfd.revents = 0; + + auto fd = pfd.fd; + + for (auto& fdAndCallback : fdReadCallbacks) + { + if (fdAndCallback.first == fd) + { + fdAndCallback.second (fd); + eventWasSent = true; + } + } + } + + return eventWasSent; + } + + void sleepUntilNextEvent (int timeoutMs) + { + poll (&pfds.front(), static_cast (pfds.size()), timeoutMs); + } + + //============================================================================== + JUCE_DECLARE_SINGLETON_SINGLETHREADED_MINIMAL (InternalRunLoop) + +private: + CriticalSection lock; + + std::vector>> fdReadCallbacks; + std::vector pfds; +}; + +JUCE_IMPLEMENT_SINGLETON (InternalRunLoop) //============================================================================== namespace LinuxErrorHandling { static bool keyboardBreakOccurred = false; - //============================================================================== void keyboardBreakSignalHandler (int sig) { if (sig == SIGINT) @@ -198,14 +210,14 @@ void MessageManager::doPlatformSpecificInitialisation() if (JUCEApplicationBase::isStandaloneApp()) LinuxErrorHandling::installKeyboardBreakHandler(); - // Create the internal message queue - auto* queue = InternalMessageQueue::getInstance(); - ignoreUnused (queue); + InternalRunLoop::getInstance(); + InternalMessageQueue::getInstance(); } void MessageManager::doPlatformSpecificShutdown() { InternalMessageQueue::deleteInstance(); + InternalRunLoop::deleteInstance(); } bool MessageManager::postMessageToSystemQueue (MessageManager::MessageBase* const message) @@ -232,16 +244,15 @@ bool MessageManager::dispatchNextMessageOnSystemQueue (bool returnIfNoPendingMes if (LinuxErrorHandling::keyboardBreakOccurred) JUCEApplicationBase::getInstance()->quit(); - if (auto* queue = InternalMessageQueue::getInstanceWithoutCreating()) + if (auto* runLoop = InternalRunLoop::getInstanceWithoutCreating()) { - if (queue->dispatchNextEvent()) + if (runLoop->dispatchPendingEvents()) break; if (returnIfNoPendingMessages) return false; - // wait for 2000ms for next events if necessary - queue->sleepUntilEvent (2000); + runLoop->sleepUntilNextEvent (2000); } } @@ -249,17 +260,16 @@ bool MessageManager::dispatchNextMessageOnSystemQueue (bool returnIfNoPendingMes } //============================================================================== -void LinuxEventLoop::setWindowSystemFdInternal (int fd, LinuxEventLoop::CallbackFunctionBase* readCallback) noexcept +void LinuxEventLoop::registerFdCallback (int fd, std::function readCallback) { - if (auto* queue = InternalMessageQueue::getInstanceWithoutCreating()) - queue->setWindowSystemFd (fd, readCallback); + if (auto* runLoop = InternalRunLoop::getInstanceWithoutCreating()) + runLoop->registerFdCallback (fd, std::move (readCallback)); } -void LinuxEventLoop::removeWindowSystemFd() noexcept +void LinuxEventLoop::unregisterFdCallback (int fd) { - if (auto* queue = InternalMessageQueue::getInstanceWithoutCreating()) - queue->removeWindowSystemFd(); + if (auto* runLoop = InternalRunLoop::getInstanceWithoutCreating()) + runLoop->unregisterFdCallback (fd); } - } // namespace juce diff --git a/modules/juce_gui_basics/native/juce_linux_X11.cpp b/modules/juce_gui_basics/native/juce_linux_X11.cpp index a54ebede7c..a2ceb67074 100644 --- a/modules/juce_gui_basics/native/juce_linux_X11.cpp +++ b/modules/juce_gui_basics/native/juce_linux_X11.cpp @@ -190,8 +190,8 @@ void XWindowSystem::initialiseXDisplay() noexcept // Setup input event handler int fd = XConnectionNumber (display); - LinuxEventLoop::setWindowSystemFd (fd, - [this](int /*fd*/) + LinuxEventLoop::registerFdCallback (fd, + [this](int) { do { @@ -201,7 +201,7 @@ void XWindowSystem::initialiseXDisplay() noexcept ScopedXLock xlock (display); if (! XPending (display)) - return false; + return; XNextEvent (display, &evt); } @@ -218,8 +218,6 @@ void XWindowSystem::initialiseXDisplay() noexcept } } while (display != nullptr); - - return false; }); } @@ -229,7 +227,7 @@ void XWindowSystem::destroyXDisplay() noexcept XDestroyWindow (display, juce_messageWindowHandle); juce_messageWindowHandle = 0; XSync (display, True); - LinuxEventLoop::removeWindowSystemFd(); + LinuxEventLoop::unregisterFdCallback (XConnectionNumber (display)); } JUCE_IMPLEMENT_SINGLETON (XWindowSystem) diff --git a/modules/juce_gui_basics/native/juce_linux_X11_Windowing.cpp b/modules/juce_gui_basics/native/juce_linux_X11_Windowing.cpp index ceb49e1893..500d5d6442 100644 --- a/modules/juce_gui_basics/native/juce_linux_X11_Windowing.cpp +++ b/modules/juce_gui_basics/native/juce_linux_X11_Windowing.cpp @@ -2250,6 +2250,16 @@ private: void timerCallback() override { #if JUCE_USE_XSHM + if (XPending (display)) + { + XEvent e; + + ScopedXLock l (display); + + while (XCheckTypedWindowEvent (display, peer.windowH, XShmGetEventBase (display), &e)) + --shmPaintsPending; + } + if (shmPaintsPending != 0) return; #endif