JUCE-7.0.12-0-g4f43011b96 JUCE-7.0.12-0-g4f43011b96
JUCE — C++ application framework with suport for VST, VST3, LV2 audio plug-ins

« « « Anklang Documentation
Loading...
Searching...
No Matches
juce_Messaging_linux.cpp
Go to the documentation of this file.
1 /*
2 ==============================================================================
3
4 This file is part of the JUCE library.
5 Copyright (c) 2022 - Raw Material Software Limited
6
7 JUCE is an open source library subject to commercial or open-source
8 licensing.
9
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.
15
16 JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
17 EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
18 DISCLAIMED.
19
20 ==============================================================================
21*/
22
23namespace juce
24{
25
26//==============================================================================
28{
29public:
31 {
32 [[maybe_unused]] auto err = ::socketpair (AF_LOCAL, SOCK_STREAM, 0, msgpipe);
33 jassert (err == 0);
34
35 LinuxEventLoop::registerFdCallback (getReadHandle(),
36 [this] (int fd)
37 {
38 while (auto msg = popNextMessage (fd))
39 {
41 {
42 msg->messageCallback();
43 }
45 }
46 });
47 }
48
50 {
51 LinuxEventLoop::unregisterFdCallback (getReadHandle());
52
53 close (getReadHandle());
54 close (getWriteHandle());
55
57 }
58
59 //==============================================================================
60 void postMessage (MessageManager::MessageBase* const msg) noexcept
61 {
62 ScopedLock sl (lock);
63 queue.add (msg);
64
65 if (bytesInSocket < maxBytesInSocketQueue)
66 {
67 bytesInSocket++;
68
69 ScopedUnlock ul (lock);
70 unsigned char x = 0xff;
71 [[maybe_unused]] auto numBytes = write (getWriteHandle(), &x, 1);
72 }
73 }
74
75 //==============================================================================
77
78private:
79 CriticalSection lock;
81
82 int msgpipe[2];
83 int bytesInSocket = 0;
84 static constexpr int maxBytesInSocketQueue = 128;
85
86 int getWriteHandle() const noexcept { return msgpipe[0]; }
87 int getReadHandle() const noexcept { return msgpipe[1]; }
88
89 MessageManager::MessageBase::Ptr popNextMessage (int fd) noexcept
90 {
91 const ScopedLock sl (lock);
92
93 if (bytesInSocket > 0)
94 {
95 --bytesInSocket;
96
97 ScopedUnlock ul (lock);
98 unsigned char x;
99 [[maybe_unused]] auto numBytes = read (fd, &x, 1);
100 }
101
102 return queue.removeAndReturn (0);
103 }
104};
105
107
108//==============================================================================
109/*
110 Stores callbacks associated with file descriptors (FD).
111
112 The callback for a particular FD should be called whenever that file has data to read.
113
114 For standalone apps, the main thread will call poll to wait for new data on any FD, and then
115 call the associated callbacks for any FDs that changed.
116
117 For plugins, the host (generally) provides some kind of run loop mechanism instead.
118 - In VST2 plugins, the host should call effEditIdle at regular intervals, and plugins can
119 dispatch all pending events inside this callback. The host doesn't know about any of the
120 plugin's FDs, so it's possible there will be a bit of latency between an FD becoming ready,
121 and its associated callback being called.
122 - In VST3 plugins, it's possible to register each FD individually with the host. In this case,
123 the facilities in LinuxEventLoopInternal can be used to observe added/removed FD callbacks,
124 and the host can be notified whenever the set of FDs changes. The host will call onFDIsSet
125 whenever a particular FD has data ready. This call should be forwarded through to
126 InternalRunLoop::dispatchEvent.
127*/
129{
130public:
131 InternalRunLoop() = default;
132
133 void registerFdCallback (int fd, std::function<void()>&& cb, short eventMask)
134 {
135 {
136 const ScopedLock sl (lock);
137
138 callbacks.emplace (fd, std::make_shared<std::function<void()>> (std::move (cb)));
139
140 const auto iter = getPollfd (fd);
141
142 if (iter == pfds.end() || iter->fd != fd)
143 pfds.insert (iter, { fd, eventMask, 0 });
144 else
146
147 jassert (pfdsAreSorted());
148 }
149
150 listeners.call ([] (auto& l) { l.fdCallbacksChanged(); });
151 }
152
153 void unregisterFdCallback (int fd)
154 {
155 {
156 const ScopedLock sl (lock);
157
158 callbacks.erase (fd);
159
160 const auto iter = getPollfd (fd);
161
162 if (iter != pfds.end() && iter->fd == fd)
163 pfds.erase (iter);
164 else
166
167 jassert (pfdsAreSorted());
168 }
169
170 listeners.call ([] (auto& l) { l.fdCallbacksChanged(); });
171 }
172
173 bool dispatchPendingEvents()
174 {
175 callbackStorage.clear();
176 getFunctionsToCallThisTime (callbackStorage);
177
178 // CriticalSection should be available during the callback
179 for (auto& fn : callbackStorage)
180 (*fn)();
181
182 return ! callbackStorage.empty();
183 }
184
185 void dispatchEvent (int fd) const
186 {
187 const auto fn = [&]
188 {
189 const ScopedLock sl (lock);
190 const auto iter = callbacks.find (fd);
191 return iter != callbacks.end() ? iter->second : nullptr;
192 }();
193
194 // CriticalSection should be available during the callback
195 if (auto* callback = fn.get())
196 (*callback)();
197 }
198
199 bool sleepUntilNextEvent (int timeoutMs)
200 {
201 const ScopedLock sl (lock);
202 return poll (pfds.data(), static_cast<nfds_t> (pfds.size()), timeoutMs) != 0;
203 }
204
205 std::vector<int> getRegisteredFds()
206 {
207 const ScopedLock sl (lock);
208 std::vector<int> result;
209 result.reserve (callbacks.size());
210 std::transform (callbacks.begin(),
211 callbacks.end(),
212 std::back_inserter (result),
213 [] (const auto& pair) { return pair.first; });
214 return result;
215 }
216
217 void addListener (LinuxEventLoopInternal::Listener& listener) { listeners.add (&listener); }
218 void removeListener (LinuxEventLoopInternal::Listener& listener) { listeners.remove (&listener); }
219
220 //==============================================================================
222
223private:
225
226 /* Appends any functions that need to be called to the passed-in vector.
227
228 We take a copy of each shared function so that the functions can be called without
229 locking or racing in the event that the function attempts to register/deregister a
230 new FD callback.
231 */
232 void getFunctionsToCallThisTime (std::vector<SharedCallback>& functions)
233 {
234 const ScopedLock sl (lock);
235
236 if (! sleepUntilNextEvent (0))
237 return;
238
239 for (auto& pfd : pfds)
240 {
241 if (std::exchange (pfd.revents, 0) != 0)
242 {
243 const auto iter = callbacks.find (pfd.fd);
244
245 if (iter != callbacks.end())
246 functions.emplace_back (iter->second);
247 }
248 }
249 }
250
251 std::vector<pollfd>::iterator getPollfd (int fd)
252 {
253 return std::lower_bound (pfds.begin(), pfds.end(), fd, [] (auto descriptor, auto toFind)
254 {
255 return descriptor.fd < toFind;
256 });
257 }
258
259 bool pfdsAreSorted() const
260 {
261 return std::is_sorted (pfds.begin(), pfds.end(), [] (auto a, auto b) { return a.fd < b.fd; });
262 }
263
264 CriticalSection lock;
265
267 std::vector<SharedCallback> callbackStorage;
269
271};
272
274
275//==============================================================================
276namespace LinuxErrorHandling
277{
278 static bool keyboardBreakOccurred = false;
279
280 static void keyboardBreakSignalHandler (int sig)
281 {
282 if (sig == SIGINT)
283 keyboardBreakOccurred = true;
284 }
285
286 static void installKeyboardBreakHandler()
287 {
288 struct sigaction saction;
291 saction.sa_handler = keyboardBreakSignalHandler;
292 saction.sa_mask = maskSet;
293 saction.sa_flags = 0;
294 sigaction (SIGINT, &saction, nullptr);
295 }
296}
297
298//==============================================================================
299void MessageManager::doPlatformSpecificInitialisation()
300{
302 LinuxErrorHandling::installKeyboardBreakHandler();
303
304 InternalRunLoop::getInstance();
305 InternalMessageQueue::getInstance();
306}
307
308void MessageManager::doPlatformSpecificShutdown()
309{
310 InternalMessageQueue::deleteInstance();
311 InternalRunLoop::deleteInstance();
312}
313
314bool MessageManager::postMessageToSystemQueue (MessageManager::MessageBase* const message)
315{
316 if (auto* queue = InternalMessageQueue::getInstanceWithoutCreating())
317 {
318 queue->postMessage (message);
319 return true;
320 }
321
322 return false;
323}
324
326{
327 // TODO
328}
329
330namespace detail
331{
332// this function expects that it will NEVER be called simultaneously for two concurrent threads
333bool dispatchNextMessageOnSystemQueue (bool returnIfNoPendingMessages)
334{
335 for (;;)
336 {
337 if (LinuxErrorHandling::keyboardBreakOccurred)
339
340 if (auto* runLoop = InternalRunLoop::getInstanceWithoutCreating())
341 {
342 if (runLoop->dispatchPendingEvents())
343 break;
344
346 return false;
347
348 runLoop->sleepUntilNextEvent (2000);
349 }
350 }
351
352 return true;
353}
354} // namespace detail
355
356//==============================================================================
357void LinuxEventLoop::registerFdCallback (int fd, std::function<void (int)> readCallback, short eventMask)
358{
359 if (auto* runLoop = InternalRunLoop::getInstanceWithoutCreating())
360 runLoop->registerFdCallback (fd, [cb = std::move (readCallback), fd] { cb (fd); }, eventMask);
361}
362
363void LinuxEventLoop::unregisterFdCallback (int fd)
364{
365 if (auto* runLoop = InternalRunLoop::getInstanceWithoutCreating())
366 runLoop->unregisterFdCallback (fd);
367}
368
369//==============================================================================
370void LinuxEventLoopInternal::registerLinuxEventLoopListener (LinuxEventLoopInternal::Listener& listener)
371{
372 if (auto* runLoop = InternalRunLoop::getInstanceWithoutCreating())
373 runLoop->addListener (listener);
374}
375
376void LinuxEventLoopInternal::deregisterLinuxEventLoopListener (LinuxEventLoopInternal::Listener& listener)
377{
378 if (auto* runLoop = InternalRunLoop::getInstanceWithoutCreating())
379 runLoop->removeListener (listener);
380}
381
382void LinuxEventLoopInternal::invokeEventLoopCallbackForFd (int fd)
383{
384 if (auto* runLoop = InternalRunLoop::getInstanceWithoutCreating())
385 runLoop->dispatchEvent (fd);
386}
387
388std::vector<int> LinuxEventLoopInternal::getRegisteredFds()
389{
390 if (auto* runLoop = InternalRunLoop::getInstanceWithoutCreating())
391 return runLoop->getRegisteredFds();
392
393 return {};
394}
395
396} // namespace juce
T back_inserter(T... args)
Automatically locks and unlocks a mutex object.
Automatically unlocks and re-locks a mutex object.
static void quit()
Signals that the main message loop should stop and the application should terminate.
static bool isStandaloneApp() noexcept
Returns true if this executable is running as an app (as opposed to being a plugin or other kind of s...
Holds a set of objects and can invoke a member function callback on each object in the set with a sin...
Internal class used as the base class for all message objects.
static void broadcastMessage(const String &messageText)
Sends a message to all other JUCE applications that are running.
ObjectClassPtr removeAndReturn(int indexToRemove)
Removes and returns an object from the array.
A smart-pointer class which points to a reference-counted object.
The JUCE String class!
Definition juce_String.h:53
close
T emplace_back(T... args)
T exchange(T... args)
T is_sorted(T... args)
#define JUCE_TRY
The JUCE_TRY/JUCE_CATCH_EXCEPTION wrappers can be used to pass any uncaught exceptions to the JUCEApp...
#define JUCE_CATCH_EXCEPTION
The JUCE_TRY/JUCE_CATCH_EXCEPTION wrappers can be used to pass any uncaught exceptions to the JUCEApp...
#define jassert(expression)
Platform-independent assertion macro.
#define jassertfalse
This will always cause an assertion failure.
#define JUCE_IMPLEMENT_SINGLETON(Classname)
This is a counterpart to the JUCE_DECLARE_SINGLETON macros.
#define JUCE_DECLARE_SINGLETON(Classname, doNotRecreateAfterDeletion)
Macro to generate the appropriate methods and boilerplate for a singleton class.
T lower_bound(T... args)
T make_shared(T... args)
JUCE Namespace.
Type unalignedPointerCast(void *ptr) noexcept
Casts a pointer to another type via void*, which suppresses the cast-align warning which sometimes ar...
Definition juce_Memory.h:88
poll
read
write
T reserve(T... args)
sigaction
sigemptyset
T transform(T... args)