1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-02-05 03:50:07 +00:00

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.
This commit is contained in:
ed 2019-05-30 15:16:19 +01:00
parent d8e07dca91
commit fd76cbc70d
4 changed files with 153 additions and 143 deletions

View file

@ -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 <typename FdCallbackFunction>
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<void(int)> readCallback);
bool operator() (int fd) override { return callback (fd); }
};
/** Unregisters a previously registered file descriptor.
template <typename FdCallbackFunction>
void setWindowSystemFd (int fd, FdCallbackFunction readCallback)
{
setWindowSystemFdInternal (fd, new CallbackFunction<FdCallbackFunction> (readCallback));
}
void removeWindowSystemFd() noexcept;
void setWindowSystemFdInternal (int fd, CallbackFunctionBase* readCallback) noexcept;
@see registerFdCallback
*/
void unregisterFdCallback (int fd);
}
} // namespace juce

View file

@ -22,13 +22,6 @@
#include <poll.h>
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<decltype(internalQueueCb)> (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<nfds_t> (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 <MessageManager::MessageBase> queue;
int fd[2];
pollfd pfds[FD_COUNT];
std::unique_ptr<LinuxEventLoop::CallbackFunctionBase> 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<void(int)>&& 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<int, std::function<void(int)>>& 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<nfds_t> (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<nfds_t> (pfds.size()), timeoutMs);
}
//==============================================================================
JUCE_DECLARE_SINGLETON_SINGLETHREADED_MINIMAL (InternalRunLoop)
private:
CriticalSection lock;
std::vector<std::pair<int, std::function<void(int)>>> fdReadCallbacks;
std::vector<pollfd> 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<void(int)> 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

View file

@ -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)

View file

@ -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