- /*
- ==============================================================================
-
- This file is part of the JUCE library.
- Copyright (c) 2022 - Raw Material Software Limited
-
- JUCE is an open source library subject to commercial or open-source
- licensing.
-
- The code included in this file is provided under the terms of the ISC license
- http://www.isc.org/downloads/software-support-policy/isc-license. Permission
- To use, copy, modify, and/or distribute this software for any purpose with or
- without fee is hereby granted provided that the above copyright notice and
- this permission notice appear in all copies.
-
- JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
- EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
- DISCLAIMED.
-
- ==============================================================================
- */
-
- namespace juce
- {
-
- //==============================================================================
- class InternalMessageQueue
- {
- public:
- InternalMessageQueue()
- {
- auto err = ::socketpair (AF_LOCAL, SOCK_STREAM, 0, msgpipe);
- jassertquiet (err == 0);
-
- LinuxEventLoop::registerFdCallback (getReadHandle(),
- [this] (int fd)
- {
- while (auto msg = popNextMessage (fd))
- {
- JUCE_TRY
- {
- msg->messageCallback();
- }
- JUCE_CATCH_EXCEPTION
- }
- });
- }
-
- ~InternalMessageQueue()
- {
- LinuxEventLoop::unregisterFdCallback (getReadHandle());
-
- close (getReadHandle());
- close (getWriteHandle());
-
- clearSingletonInstance();
- }
-
- //==============================================================================
- void postMessage (MessageManager::MessageBase* const msg) noexcept
- {
- ScopedLock sl (lock);
- queue.add (msg);
-
- if (bytesInSocket < maxBytesInSocketQueue)
- {
- bytesInSocket++;
-
- ScopedUnlock ul (lock);
- unsigned char x = 0xff;
- auto numBytes = write (getWriteHandle(), &x, 1);
- ignoreUnused (numBytes);
- }
- }
-
- //==============================================================================
- JUCE_DECLARE_SINGLETON (InternalMessageQueue, false)
-
- private:
- CriticalSection lock;
- ReferenceCountedArray <MessageManager::MessageBase> queue;
-
- int msgpipe[2];
- int bytesInSocket = 0;
- static constexpr int maxBytesInSocketQueue = 128;
-
- int getWriteHandle() const noexcept { return msgpipe[0]; }
- int getReadHandle() const noexcept { return msgpipe[1]; }
-
- MessageManager::MessageBase::Ptr popNextMessage (int fd) noexcept
- {
- const ScopedLock sl (lock);
-
- if (bytesInSocket > 0)
- {
- --bytesInSocket;
-
- ScopedUnlock ul (lock);
- unsigned char x;
- auto numBytes = read (fd, &x, 1);
- ignoreUnused (numBytes);
- }
-
- return queue.removeAndReturn (0);
- }
- };
-
- JUCE_IMPLEMENT_SINGLETON (InternalMessageQueue)
-
- //==============================================================================
- /*
- Stores callbacks associated with file descriptors (FD).
-
- The callback for a particular FD should be called whenever that file has data to read.
-
- For standalone apps, the main thread will call poll to wait for new data on any FD, and then
- call the associated callbacks for any FDs that changed.
-
- For plugins, the host (generally) provides some kind of run loop mechanism instead.
- - In VST2 plugins, the host should call effEditIdle at regular intervals, and plugins can
- dispatch all pending events inside this callback. The host doesn't know about any of the
- plugin's FDs, so it's possible there will be a bit of latency between an FD becoming ready,
- and its associated callback being called.
- - In VST3 plugins, it's possible to register each FD individually with the host. In this case,
- the facilities in LinuxEventLoopInternal can be used to observe added/removed FD callbacks,
- and the host can be notified whenever the set of FDs changes. The host will call onFDIsSet
- whenever a particular FD has data ready. This call should be forwarded through to
- InternalRunLoop::dispatchEvent.
- */
- struct InternalRunLoop
- {
- public:
- InternalRunLoop() = default;
-
- void registerFdCallback (int fd, std::function<void()>&& cb, short eventMask)
- {
- {
- const ScopedLock sl (lock);
-
- callbacks.emplace (fd, std::make_shared<std::function<void()>> (std::move (cb)));
-
- const auto iter = getPollfd (fd);
-
- if (iter == pfds.end() || iter->fd != fd)
- pfds.insert (iter, { fd, eventMask, 0 });
- else
- jassertfalse;
-
- jassert (pfdsAreSorted());
- }
-
- listeners.call ([] (auto& l) { l.fdCallbacksChanged(); });
- }
-
- void unregisterFdCallback (int fd)
- {
- {
- const ScopedLock sl (lock);
-
- callbacks.erase (fd);
-
- const auto iter = getPollfd (fd);
-
- if (iter != pfds.end() && iter->fd == fd)
- pfds.erase (iter);
- else
- jassertfalse;
-
- jassert (pfdsAreSorted());
- }
-
- listeners.call ([] (auto& l) { l.fdCallbacksChanged(); });
- }
-
- bool dispatchPendingEvents()
- {
- callbackStorage.clear();
- getFunctionsToCallThisTime (callbackStorage);
-
- // CriticalSection should be available during the callback
- for (auto& fn : callbackStorage)
- (*fn)();
-
- return ! callbackStorage.empty();
- }
-
- void dispatchEvent (int fd) const
- {
- const auto fn = [&]
- {
- const ScopedLock sl (lock);
- const auto iter = callbacks.find (fd);
- return iter != callbacks.end() ? iter->second : nullptr;
- }();
-
- // CriticalSection should be available during the callback
- if (auto* callback = fn.get())
- (*callback)();
- }
-
- bool sleepUntilNextEvent (int timeoutMs)
- {
- const ScopedLock sl (lock);
- return poll (pfds.data(), static_cast<nfds_t> (pfds.size()), timeoutMs) != 0;
- }
-
- std::vector<int> getRegisteredFds()
- {
- const ScopedLock sl (lock);
- std::vector<int> result;
- result.reserve (callbacks.size());
- std::transform (callbacks.begin(),
- callbacks.end(),
- std::back_inserter (result),
- [] (const auto& pair) { return pair.first; });
- return result;
- }
-
- void addListener (LinuxEventLoopInternal::Listener& listener) { listeners.add (&listener); }
- void removeListener (LinuxEventLoopInternal::Listener& listener) { listeners.remove (&listener); }
-
- //==============================================================================
- JUCE_DECLARE_SINGLETON (InternalRunLoop, false)
-
- private:
- using SharedCallback = std::shared_ptr<std::function<void()>>;
-
- /* Appends any functions that need to be called to the passed-in vector.
-
- We take a copy of each shared function so that the functions can be called without
- locking or racing in the event that the function attempts to register/deregister a
- new FD callback.
- */
- void getFunctionsToCallThisTime (std::vector<SharedCallback>& functions)
- {
- const ScopedLock sl (lock);
-
- if (! sleepUntilNextEvent (0))
- return;
-
- for (auto& pfd : pfds)
- {
- if (std::exchange (pfd.revents, 0) != 0)
- {
- const auto iter = callbacks.find (pfd.fd);
-
- if (iter != callbacks.end())
- functions.emplace_back (iter->second);
- }
- }
- }
-
- std::vector<pollfd>::iterator getPollfd (int fd)
- {
- return std::lower_bound (pfds.begin(), pfds.end(), fd, [] (auto descriptor, auto toFind)
- {
- return descriptor.fd < toFind;
- });
- }
-
- bool pfdsAreSorted() const
- {
- return std::is_sorted (pfds.begin(), pfds.end(), [] (auto a, auto b) { return a.fd < b.fd; });
- }
-
- CriticalSection lock;
-
- std::map<int, SharedCallback> callbacks;
- std::vector<SharedCallback> callbackStorage;
- std::vector<pollfd> pfds;
-
- ListenerList<LinuxEventLoopInternal::Listener> listeners;
- };
-
- JUCE_IMPLEMENT_SINGLETON (InternalRunLoop)
-
- //==============================================================================
- namespace LinuxErrorHandling
- {
- static bool keyboardBreakOccurred = false;
-
- static void keyboardBreakSignalHandler (int sig)
- {
- if (sig == SIGINT)
- keyboardBreakOccurred = true;
- }
-
- static void installKeyboardBreakHandler()
- {
- struct sigaction saction;
- sigset_t maskSet;
- sigemptyset (&maskSet);
- saction.sa_handler = keyboardBreakSignalHandler;
- saction.sa_mask = maskSet;
- saction.sa_flags = 0;
- sigaction (SIGINT, &saction, nullptr);
- }
- }
-
- //==============================================================================
- void MessageManager::doPlatformSpecificInitialisation()
- {
- if (JUCEApplicationBase::isStandaloneApp())
- LinuxErrorHandling::installKeyboardBreakHandler();
-
- InternalRunLoop::getInstance();
- InternalMessageQueue::getInstance();
- }
-
- void MessageManager::doPlatformSpecificShutdown()
- {
- InternalMessageQueue::deleteInstance();
- InternalRunLoop::deleteInstance();
- }
-
- bool MessageManager::postMessageToSystemQueue (MessageManager::MessageBase* const message)
- {
- if (auto* queue = InternalMessageQueue::getInstanceWithoutCreating())
- {
- queue->postMessage (message);
- return true;
- }
-
- return false;
- }
-
- void MessageManager::broadcastMessage (const String&)
- {
- // TODO
- }
-
- // this function expects that it will NEVER be called simultaneously for two concurrent threads
- bool dispatchNextMessageOnSystemQueue (bool returnIfNoPendingMessages)
- {
- for (;;)
- {
- if (LinuxErrorHandling::keyboardBreakOccurred)
- JUCEApplicationBase::quit();
-
- if (auto* runLoop = InternalRunLoop::getInstanceWithoutCreating())
- {
- if (runLoop->dispatchPendingEvents())
- break;
-
- if (returnIfNoPendingMessages)
- return false;
-
- runLoop->sleepUntilNextEvent (2000);
- }
- }
-
- return true;
- }
-
- //==============================================================================
- void LinuxEventLoop::registerFdCallback (int fd, std::function<void (int)> readCallback, short eventMask)
- {
- if (auto* runLoop = InternalRunLoop::getInstanceWithoutCreating())
- runLoop->registerFdCallback (fd, [cb = std::move (readCallback), fd] { cb (fd); }, eventMask);
- }
-
- void LinuxEventLoop::unregisterFdCallback (int fd)
- {
- if (auto* runLoop = InternalRunLoop::getInstanceWithoutCreating())
- runLoop->unregisterFdCallback (fd);
- }
-
- //==============================================================================
- void LinuxEventLoopInternal::registerLinuxEventLoopListener (LinuxEventLoopInternal::Listener& listener)
- {
- if (auto* runLoop = InternalRunLoop::getInstanceWithoutCreating())
- runLoop->addListener (listener);
- }
-
- void LinuxEventLoopInternal::deregisterLinuxEventLoopListener (LinuxEventLoopInternal::Listener& listener)
- {
- if (auto* runLoop = InternalRunLoop::getInstanceWithoutCreating())
- runLoop->removeListener (listener);
- }
-
- void LinuxEventLoopInternal::invokeEventLoopCallbackForFd (int fd)
- {
- if (auto* runLoop = InternalRunLoop::getInstanceWithoutCreating())
- runLoop->dispatchEvent (fd);
- }
-
- std::vector<int> LinuxEventLoopInternal::getRegisteredFds()
- {
- if (auto* runLoop = InternalRunLoop::getInstanceWithoutCreating())
- return runLoop->getRegisteredFds();
-
- return {};
- }
-
- } // namespace juce
|