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_MessageManager.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
26MessageManager::MessageManager() noexcept
27 : messageThreadId (Thread::getCurrentThreadId())
28{
30
32 Thread::setCurrentThreadName ("JUCE Message Thread");
33}
34
35MessageManager::~MessageManager() noexcept
36{
37 broadcaster.reset();
38
39 doPlatformSpecificShutdown();
40
41 jassert (instance == this);
42 instance = nullptr; // do this last in case this instance is still needed by doPlatformSpecificShutdown()
43}
44
45MessageManager* MessageManager::instance = nullptr;
46
48{
49 if (instance == nullptr)
50 {
51 instance = new MessageManager();
52 doPlatformSpecificInitialisation();
53 }
54
55 return instance;
56}
57
59{
60 return instance;
61}
62
64{
65 deleteAndZero (instance);
66}
67
68//==============================================================================
69bool MessageManager::MessageBase::post()
70{
71 auto* mm = MessageManager::instance;
72
73 if (mm == nullptr || mm->quitMessagePosted.get() != 0 || ! postMessageToSystemQueue (this))
74 {
75 Ptr deleter (this); // (this will delete messages that were just created with a 0 ref count)
76 return false;
77 }
78
79 return true;
80}
81
82//==============================================================================
83#if ! (JUCE_MAC || JUCE_IOS || JUCE_ANDROID)
84// implemented in platform-specific code (juce_Messaging_linux.cpp and juce_Messaging_windows.cpp)
85namespace detail
86{
87bool dispatchNextMessageOnSystemQueue (bool returnIfNoPendingMessages);
88} // namespace detail
89
91{
92public:
93 QuitMessage() {}
94
95 void messageCallback() override
96 {
97 if (auto* mm = MessageManager::instance)
98 mm->quitMessageReceived = true;
99 }
100
102};
103
105{
106 jassert (isThisTheMessageThread()); // must only be called by the message thread
107
108 while (quitMessageReceived.get() == 0)
109 {
111 {
112 if (! detail::dispatchNextMessageOnSystemQueue (false))
113 Thread::sleep (1);
114 }
116 }
117}
118
120{
121 (new QuitMessage())->post();
122 quitMessagePosted = true;
123}
124
125#if JUCE_MODAL_LOOPS_PERMITTED
126bool MessageManager::runDispatchLoopUntil (int millisecondsToRunFor)
127{
128 jassert (isThisTheMessageThread()); // must only be called by the message thread
129
131
132 while (quitMessageReceived.get() == 0)
133 {
135 {
136 if (! detail::dispatchNextMessageOnSystemQueue (millisecondsToRunFor >= 0))
137 Thread::sleep (1);
138 }
140
141 if (millisecondsToRunFor >= 0 && Time::currentTimeMillis() >= endTime)
142 break;
143 }
144
145 return quitMessageReceived.get() == 0;
146}
147#endif
148
149#endif
150
151//==============================================================================
153{
154public:
155 AsyncFunctionCallback (MessageCallbackFunction* const f, void* const param)
156 : func (f), parameter (param)
157 {}
158
159 void messageCallback() override
160 {
161 result = (*func) (parameter);
162 finished.signal();
163 }
164
165 WaitableEvent finished;
166 std::atomic<void*> result { nullptr };
167
168private:
169 MessageCallbackFunction* const func;
170 void* const parameter;
171
173};
174
176{
178 return func (parameter);
179
180 // If this thread has the message manager locked, then this will deadlock!
182
184
185 if (message->post())
186 {
187 message->finished.wait();
188 return message->result.load();
189 }
190
191 jassertfalse; // the OS message queue failed to send the message!
192 return nullptr;
193}
194
196{
197 struct AsyncCallInvoker final : public MessageBase
198 {
199 AsyncCallInvoker (std::function<void()> f) : callback (std::move (f)) {}
200 void messageCallback() override { callback(); }
201 std::function<void()> callback;
202 };
203
204 return (new AsyncCallInvoker (std::move (fn)))->post();
205}
206
207//==============================================================================
208void MessageManager::deliverBroadcastMessage (const String& value)
209{
210 if (broadcaster != nullptr)
211 broadcaster->sendActionMessage (value);
212}
213
215{
216 if (broadcaster == nullptr)
217 broadcaster.reset (new ActionBroadcaster());
218
219 broadcaster->addActionListener (listener);
220}
221
223{
224 if (broadcaster != nullptr)
225 broadcaster->removeActionListener (listener);
226}
227
228//==============================================================================
230{
231 const std::lock_guard<std::mutex> lock { messageThreadIdMutex };
232
233 return Thread::getCurrentThreadId() == messageThreadId;
234}
235
237{
239
240 const std::lock_guard<std::mutex> lock { messageThreadIdMutex };
241
242 if (std::exchange (messageThreadId, thisThread) != thisThread)
243 {
244 #if JUCE_WINDOWS
245 // This is needed on windows to make sure the message window is created by this thread
246 doPlatformSpecificShutdown();
247 doPlatformSpecificInitialisation();
248 #endif
249 }
250}
251
253{
255 return thisThread == messageThreadId || thisThread == threadWithLock.get();
256}
257
259{
260 if (auto i = getInstanceWithoutCreating())
261 return i->currentThreadHasLockedMessageManager();
262
263 return false;
264}
265
267{
268 if (auto i = getInstanceWithoutCreating())
269 return i->isThisTheMessageThread();
270
271 return false;
272}
273
274//==============================================================================
275//==============================================================================
276/* The only safe way to lock the message thread while another thread does
277 some work is by posting a special message, whose purpose is to tie up the event
278 loop until the other thread has finished its business.
279
280 Any other approach can get horribly deadlocked if the OS uses its own hidden locks which
281 get locked before making an event callback, because if the same OS lock gets indirectly
282 accessed from another thread inside a MM lock, you're screwed. (this is exactly what happens
283 in Cocoa).
284*/
286{
287 explicit BlockingMessage (const MessageManager::Lock* parent) noexcept
288 : owner (parent) {}
289
290 void messageCallback() override
291 {
292 std::unique_lock lock { mutex };
293
294 if (owner != nullptr)
295 owner->setAcquired (true);
296
297 condvar.wait (lock, [&] { return owner == nullptr; });
298 }
299
300 void stopWaiting()
301 {
302 const ScopeGuard scope { [&] { condvar.notify_one(); } };
303 const std::scoped_lock lock { mutex };
304 owner = nullptr;
305 }
306
307private:
308 std::mutex mutex;
310
311 const MessageManager::Lock* owner = nullptr;
312
314};
315
316//==============================================================================
319void MessageManager::Lock::enter() const noexcept { exclusiveTryAcquire (true); }
320bool MessageManager::Lock::tryEnter() const noexcept { return exclusiveTryAcquire (false); }
321
322bool MessageManager::Lock::exclusiveTryAcquire (bool lockIsMandatory) const noexcept
323{
324 if (lockIsMandatory)
325 entryMutex.enter();
326 else if (! entryMutex.tryEnter())
327 return false;
328
329 const auto result = tryAcquire (lockIsMandatory);
330
331 if (! result)
332 entryMutex.exit();
333
334 return result;
335}
336
337bool MessageManager::Lock::tryAcquire (bool lockIsMandatory) const noexcept
338{
339 auto* mm = MessageManager::instance;
340
341 if (mm == nullptr)
342 {
344 return false;
345 }
346
347 if (! lockIsMandatory && [&]
348 {
349 const std::scoped_lock lock { mutex };
350 return std::exchange (abortWait, false);
351 }())
352 {
353 return false;
354 }
355
356 if (mm->currentThreadHasLockedMessageManager())
357 return true;
358
359 try
360 {
361 blockingMessage = *new BlockingMessage (this);
362 }
363 catch (...)
364 {
366 return false;
367 }
368
369 if (! blockingMessage->post())
370 {
371 // post of message failed while trying to get the lock
373 blockingMessage = nullptr;
374 return false;
375 }
376
377 for (;;)
378 {
379 {
380 std::unique_lock lock { mutex };
381 condvar.wait (lock, [&] { return std::exchange (abortWait, false); });
382 }
383
384 if (acquired)
385 {
386 mm->threadWithLock = Thread::getCurrentThreadId();
387 return true;
388 }
389
390 if (! lockIsMandatory)
391 break;
392 }
393
394 // we didn't get the lock
395
396 blockingMessage->stopWaiting();
397 blockingMessage = nullptr;
398 return false;
399}
400
402{
403 const auto wasAcquired = [&]
404 {
405 const std::scoped_lock lock { mutex };
406 return acquired;
407 }();
408
409 if (! wasAcquired)
410 return;
411
412 const ScopeGuard unlocker { [&] { entryMutex.exit(); } };
413
414 if (blockingMessage == nullptr)
415 return;
416
417 if (auto* mm = MessageManager::instance)
418 {
419 jassert (mm->currentThreadHasLockedMessageManager());
420 mm->threadWithLock = {};
421 }
422
423 blockingMessage->stopWaiting();
424 blockingMessage = nullptr;
425 acquired = false;
426}
427
429{
430 setAcquired (false);
431}
432
433void MessageManager::Lock::setAcquired (bool x) const noexcept
434{
435 const ScopeGuard scope { [&] { condvar.notify_one(); } };
436 const std::scoped_lock lock { mutex };
437 abortWait = true;
438 acquired = x;
439}
440
441//==============================================================================
445
449
450bool MessageManagerLock::attemptLock (Thread* threadToCheck, ThreadPoolJob* jobToCheck)
451{
452 jassert (threadToCheck == nullptr || jobToCheck == nullptr);
453
454 if (threadToCheck != nullptr)
455 threadToCheck->addListener (this);
456
457 if (jobToCheck != nullptr)
458 jobToCheck->addListener (this);
459
460 // tryEnter may have a spurious abort (return false) so keep checking the condition
461 while ((threadToCheck == nullptr || ! threadToCheck->threadShouldExit())
462 && (jobToCheck == nullptr || ! jobToCheck->shouldExit()))
463 {
464 if (mmLock.tryEnter())
465 break;
466 }
467
468 if (threadToCheck != nullptr)
469 {
470 threadToCheck->removeListener (this);
471
472 if (threadToCheck->threadShouldExit())
473 return false;
474 }
475
476 if (jobToCheck != nullptr)
477 {
478 jobToCheck->removeListener (this);
479
480 if (jobToCheck->shouldExit())
481 return false;
482 }
483
484 return true;
485}
486
488
489void MessageManagerLock::exitSignalSent()
490{
491 mmLock.abort();
492}
493
494//==============================================================================
502
511
512static int numScopedInitInstances = 0;
513
516
517} // namespace juce
Manages a list of ActionListeners, and can send them messages.
Interface class for delivery of events that are sent by an ActionBroadcaster.
static void deleteAll()
Deletes all extant objects.
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...
~MessageManagerLock() override
Releases the current thread's lock on the message manager.
MessageManagerLock(Thread *threadToCheckForExitSignal=nullptr)
Tries to acquire a lock on the message manager.
A lock you can use to lock the message manager.
Lock()
Creates a new critical section to exclusively access methods which can only be called when the messag...
void enter() const noexcept
Acquires the message manager lock.
void abort() const noexcept
Unblocks a thread which is waiting in tryEnter Call this method if you want to unblock a thread which...
bool tryEnter() const noexcept
Attempts to lock the message manager and exits if abort is called.
void exit() const noexcept
Releases the message manager lock.
Internal class used as the base class for all message objects.
This class is in charge of the application's event-dispatch loop.
static bool callAsync(std::function< void()> functionToCall)
Asynchronously invokes a function or C++11 lambda on the message thread.
bool isThisTheMessageThread() const noexcept
Returns true if the caller-thread is the message thread.
static bool existsAndIsCurrentThread() noexcept
Returns true if there's an instance of the MessageManager, and if the current thread is running it.
void runDispatchLoop()
Runs the event dispatch loop until a stop message is posted.
void stopDispatchLoop()
Sends a signal that the dispatch loop should terminate.
bool currentThreadHasLockedMessageManager() const noexcept
Returns true if the caller thread has currently got the message manager locked.
void * callFunctionOnMessageThread(MessageCallbackFunction *callback, void *userData)
Calls a function using the message-thread.
void deregisterBroadcastListener(ActionListener *listener)
Deregisters a broadcast listener.
void setCurrentThreadAsMessageThread()
Called to tell the manager that the current thread is the one that's running the dispatch loop.
static bool existsAndIsLockedByCurrentThread() noexcept
Returns true if there's an instance of the MessageManager, and if the current thread has the lock on ...
void registerBroadcastListener(ActionListener *listener)
Registers a listener to get told about broadcast messages.
static void deleteInstance()
Deletes the global MessageManager instance.
static MessageManager * getInstanceWithoutCreating() noexcept
Returns the global instance of the MessageManager, or nullptr if it doesn't exist.
static MessageManager * getInstance()
Returns the global instance of the MessageManager.
A smart-pointer class which points to a reference-counted object.
~ScopedJuceInitialiser_GUI()
The destructor simply calls shutdownJuce_GUI().
ScopedJuceInitialiser_GUI()
The constructor simply calls initialiseJuce_GUI().
The JUCE String class!
Definition juce_String.h:53
A task that is executed by a ThreadPool object.
Encapsulates a thread.
Definition juce_Thread.h:43
static ThreadID JUCE_CALLTYPE getCurrentThreadId()
Returns an id that identifies the caller thread.
static void JUCE_CALLTYPE setCurrentThreadName(const String &newThreadName)
Changes the name of the caller thread.
static void JUCE_CALLTYPE sleep(int milliseconds)
Suspends the execution of the current thread until the specified timeout period has elapsed (note tha...
static int64 currentTimeMillis() noexcept
Returns the current system time.
Allows threads to wait for events triggered by other threads.
T exchange(T... args)
exit
#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 JUCE_AUTORELEASEPOOL
A macro that can be used to easily declare a local ScopedAutoReleasePool object for RAII-based obj-C ...
#define jassert(expression)
Platform-independent assertion macro.
#define JUCE_DECLARE_NON_COPYABLE(className)
This is a shorthand macro for deleting a class's copy constructor and copy assignment operator.
#define jassertfalse
This will always cause an assertion failure.
#define JUCE_CALLTYPE
This macro defines the C calling convention used as the standard for JUCE calls.
T lock(T... args)
JUCE Namespace.
void *(void *userData) MessageCallbackFunction
See MessageManager::callFunctionOnMessageThread() for use of this function type.
void deleteAndZero(Type &pointer)
Delete an object pointer, and sets the pointer to null.
Definition juce_Memory.h:40
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
JUCE_API void JUCE_CALLTYPE shutdownJuce_GUI()
Clears up any static data being used by JUCE's GUI classes.
JUCE_API void JUCE_CALLTYPE initialiseJuce_GUI()
Initialises JUCE's GUI classes.
Type get() const noexcept
Atomically reads and returns the current value.
Definition juce_Atomic.h:64
An easy way to ensure that a function is called at the end of the current scope.