2 ==============================================================================
4 This file is part of the JUCE library.
5 Copyright (c) 2022 - Raw Material Software Limited
7 JUCE is an open source library subject to commercial or open-source
10 The code included in this file is provided under the terms of the ISC license
11 http://www.isc.org/downloads/software-support-policy/isc-license. Permission
12 To use, copy, modify, and/or distribute this software for any purpose with or
13 without fee is hereby granted provided that the above copyright notice and
14 this permission notice appear in all copies.
16 JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
17 EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
20 ==============================================================================
26 //==============================================================================
27 class InternalMessageQueue
30 InternalMessageQueue()
32 auto err
= ::socketpair (AF_LOCAL
, SOCK_STREAM
, 0, msgpipe
);
33 jassertquiet (err
== 0);
35 LinuxEventLoop::registerFdCallback (getReadHandle(),
38 while (auto msg
= popNextMessage (fd
))
42 msg
->messageCallback();
49 ~InternalMessageQueue()
51 LinuxEventLoop::unregisterFdCallback (getReadHandle());
53 close (getReadHandle());
54 close (getWriteHandle());
56 clearSingletonInstance();
59 //==============================================================================
60 void postMessage (MessageManager::MessageBase
* const msg
) noexcept
65 if (bytesInSocket
< maxBytesInSocketQueue
)
69 ScopedUnlock
ul (lock
);
70 unsigned char x
= 0xff;
71 auto numBytes
= write (getWriteHandle(), &x
, 1);
72 ignoreUnused (numBytes
);
76 //==============================================================================
77 JUCE_DECLARE_SINGLETON (InternalMessageQueue
, false)
81 ReferenceCountedArray
<MessageManager::MessageBase
> queue
;
84 int bytesInSocket
= 0;
85 static constexpr int maxBytesInSocketQueue
= 128;
87 int getWriteHandle() const noexcept
{ return msgpipe
[0]; }
88 int getReadHandle() const noexcept
{ return msgpipe
[1]; }
90 MessageManager::MessageBase::Ptr
popNextMessage (int fd
) noexcept
92 const ScopedLock
sl (lock
);
94 if (bytesInSocket
> 0)
98 ScopedUnlock
ul (lock
);
100 auto numBytes
= read (fd
, &x
, 1);
101 ignoreUnused (numBytes
);
104 return queue
.removeAndReturn (0);
108 JUCE_IMPLEMENT_SINGLETON (InternalMessageQueue
)
110 //==============================================================================
112 Stores callbacks associated with file descriptors (FD).
114 The callback for a particular FD should be called whenever that file has data to read.
116 For standalone apps, the main thread will call poll to wait for new data on any FD, and then
117 call the associated callbacks for any FDs that changed.
119 For plugins, the host (generally) provides some kind of run loop mechanism instead.
120 - In VST2 plugins, the host should call effEditIdle at regular intervals, and plugins can
121 dispatch all pending events inside this callback. The host doesn't know about any of the
122 plugin's FDs, so it's possible there will be a bit of latency between an FD becoming ready,
123 and its associated callback being called.
124 - In VST3 plugins, it's possible to register each FD individually with the host. In this case,
125 the facilities in LinuxEventLoopInternal can be used to observe added/removed FD callbacks,
126 and the host can be notified whenever the set of FDs changes. The host will call onFDIsSet
127 whenever a particular FD has data ready. This call should be forwarded through to
128 InternalRunLoop::dispatchEvent.
130 struct InternalRunLoop
133 InternalRunLoop() = default;
135 void registerFdCallback (int fd
, std::function
<void()>&& cb
, short eventMask
)
138 const ScopedLock
sl (lock
);
140 callbacks
.emplace (fd
, std::make_shared
<std::function
<void()>> (std::move (cb
)));
142 const auto iter
= getPollfd (fd
);
144 if (iter
== pfds
.end() || iter
->fd
!= fd
)
145 pfds
.insert (iter
, { fd
, eventMask
, 0 });
149 jassert (pfdsAreSorted());
152 listeners
.call ([] (auto& l
) { l
.fdCallbacksChanged(); });
155 void unregisterFdCallback (int fd
)
158 const ScopedLock
sl (lock
);
160 callbacks
.erase (fd
);
162 const auto iter
= getPollfd (fd
);
164 if (iter
!= pfds
.end() && iter
->fd
== fd
)
169 jassert (pfdsAreSorted());
172 listeners
.call ([] (auto& l
) { l
.fdCallbacksChanged(); });
175 bool dispatchPendingEvents()
177 callbackStorage
.clear();
178 getFunctionsToCallThisTime (callbackStorage
);
180 // CriticalSection should be available during the callback
181 for (auto& fn
: callbackStorage
)
184 return ! callbackStorage
.empty();
187 void dispatchEvent (int fd
) const
191 const ScopedLock
sl (lock
);
192 const auto iter
= callbacks
.find (fd
);
193 return iter
!= callbacks
.end() ? iter
->second
: nullptr;
196 // CriticalSection should be available during the callback
197 if (auto* callback
= fn
.get())
201 bool sleepUntilNextEvent (int timeoutMs
)
203 const ScopedLock
sl (lock
);
204 return poll (pfds
.data(), static_cast<nfds_t
> (pfds
.size()), timeoutMs
) != 0;
207 std::vector
<int> getRegisteredFds()
209 const ScopedLock
sl (lock
);
210 std::vector
<int> result
;
211 result
.reserve (callbacks
.size());
212 std::transform (callbacks
.begin(),
214 std::back_inserter (result
),
215 [] (const auto& pair
) { return pair
.first
; });
219 void addListener (LinuxEventLoopInternal::Listener
& listener
) { listeners
.add (&listener
); }
220 void removeListener (LinuxEventLoopInternal::Listener
& listener
) { listeners
.remove (&listener
); }
222 //==============================================================================
223 JUCE_DECLARE_SINGLETON (InternalRunLoop
, false)
226 using SharedCallback
= std::shared_ptr
<std::function
<void()>>;
228 /* Appends any functions that need to be called to the passed-in vector.
230 We take a copy of each shared function so that the functions can be called without
231 locking or racing in the event that the function attempts to register/deregister a
234 void getFunctionsToCallThisTime (std::vector
<SharedCallback
>& functions
)
236 const ScopedLock
sl (lock
);
238 if (! sleepUntilNextEvent (0))
241 for (auto& pfd
: pfds
)
243 if (std::exchange (pfd
.revents
, 0) != 0)
245 const auto iter
= callbacks
.find (pfd
.fd
);
247 if (iter
!= callbacks
.end())
248 functions
.emplace_back (iter
->second
);
253 std::vector
<pollfd
>::iterator
getPollfd (int fd
)
255 return std::lower_bound (pfds
.begin(), pfds
.end(), fd
, [] (auto descriptor
, auto toFind
)
257 return descriptor
.fd
< toFind
;
261 bool pfdsAreSorted() const
263 return std::is_sorted (pfds
.begin(), pfds
.end(), [] (auto a
, auto b
) { return a
.fd
< b
.fd
; });
266 CriticalSection lock
;
268 std::map
<int, SharedCallback
> callbacks
;
269 std::vector
<SharedCallback
> callbackStorage
;
270 std::vector
<pollfd
> pfds
;
272 ListenerList
<LinuxEventLoopInternal::Listener
> listeners
;
275 JUCE_IMPLEMENT_SINGLETON (InternalRunLoop
)
277 //==============================================================================
278 namespace LinuxErrorHandling
280 static bool keyboardBreakOccurred
= false;
282 static void keyboardBreakSignalHandler (int sig
)
285 keyboardBreakOccurred
= true;
288 static void installKeyboardBreakHandler()
290 struct sigaction saction
;
292 sigemptyset (&maskSet
);
293 saction
.sa_handler
= keyboardBreakSignalHandler
;
294 saction
.sa_mask
= maskSet
;
295 saction
.sa_flags
= 0;
296 sigaction (SIGINT
, &saction
, nullptr);
300 //==============================================================================
301 void MessageManager::doPlatformSpecificInitialisation()
303 if (JUCEApplicationBase::isStandaloneApp())
304 LinuxErrorHandling::installKeyboardBreakHandler();
306 InternalRunLoop::getInstance();
307 InternalMessageQueue::getInstance();
310 void MessageManager::doPlatformSpecificShutdown()
312 InternalMessageQueue::deleteInstance();
313 InternalRunLoop::deleteInstance();
316 bool MessageManager::postMessageToSystemQueue (MessageManager::MessageBase
* const message
)
318 if (auto* queue
= InternalMessageQueue::getInstanceWithoutCreating())
320 queue
->postMessage (message
);
327 void MessageManager::broadcastMessage (const String
&)
332 // this function expects that it will NEVER be called simultaneously for two concurrent threads
333 bool dispatchNextMessageOnSystemQueue (bool returnIfNoPendingMessages
)
337 if (LinuxErrorHandling::keyboardBreakOccurred
)
338 JUCEApplicationBase::quit();
340 if (auto* runLoop
= InternalRunLoop::getInstanceWithoutCreating())
342 if (runLoop
->dispatchPendingEvents())
345 if (returnIfNoPendingMessages
)
348 runLoop
->sleepUntilNextEvent (2000);
355 //==============================================================================
356 void LinuxEventLoop::registerFdCallback (int fd
, std::function
<void (int)> readCallback
, short eventMask
)
358 if (auto* runLoop
= InternalRunLoop::getInstanceWithoutCreating())
359 runLoop
->registerFdCallback (fd
, [cb
= std::move (readCallback
), fd
] { cb (fd
); }, eventMask
);
362 void LinuxEventLoop::unregisterFdCallback (int fd
)
364 if (auto* runLoop
= InternalRunLoop::getInstanceWithoutCreating())
365 runLoop
->unregisterFdCallback (fd
);
368 //==============================================================================
369 void LinuxEventLoopInternal::registerLinuxEventLoopListener (LinuxEventLoopInternal::Listener
& listener
)
371 if (auto* runLoop
= InternalRunLoop::getInstanceWithoutCreating())
372 runLoop
->addListener (listener
);
375 void LinuxEventLoopInternal::deregisterLinuxEventLoopListener (LinuxEventLoopInternal::Listener
& listener
)
377 if (auto* runLoop
= InternalRunLoop::getInstanceWithoutCreating())
378 runLoop
->removeListener (listener
);
381 void LinuxEventLoopInternal::invokeEventLoopCallbackForFd (int fd
)
383 if (auto* runLoop
= InternalRunLoop::getInstanceWithoutCreating())
384 runLoop
->dispatchEvent (fd
);
387 std::vector
<int> LinuxEventLoopInternal::getRegisteredFds()
389 if (auto* runLoop
= InternalRunLoop::getInstanceWithoutCreating())
390 return runLoop
->getRegisteredFds();