OpenShot Library | libopenshot-audio 0.2.0
juce_MessageManager.cpp
1/*
2 ==============================================================================
3
4 This file is part of the JUCE library.
5 Copyright (c) 2017 - ROLI Ltd.
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 Thread::setCurrentThreadName ("JUCE Message Thread");
31}
32
33MessageManager::~MessageManager() noexcept
34{
35 broadcaster.reset();
36
37 doPlatformSpecificShutdown();
38
39 jassert (instance == this);
40 instance = nullptr; // do this last in case this instance is still needed by doPlatformSpecificShutdown()
41}
42
43MessageManager* MessageManager::instance = nullptr;
44
46{
47 if (instance == nullptr)
48 {
49 instance = new MessageManager();
50 doPlatformSpecificInitialisation();
51 }
52
53 return instance;
54}
55
57{
58 return instance;
59}
60
62{
63 deleteAndZero (instance);
64}
65
66//==============================================================================
67bool MessageManager::MessageBase::post()
68{
69 auto* mm = MessageManager::instance;
70
71 if (mm == nullptr || mm->quitMessagePosted.get() != 0 || ! postMessageToSystemQueue (this))
72 {
73 Ptr deleter (this); // (this will delete messages that were just created with a 0 ref count)
74 return false;
75 }
76
77 return true;
78}
79
80//==============================================================================
81#if JUCE_MODAL_LOOPS_PERMITTED && ! (JUCE_MAC || JUCE_IOS)
82bool MessageManager::runDispatchLoopUntil (int millisecondsToRunFor)
83{
84 jassert (isThisTheMessageThread()); // must only be called by the message thread
85
86 auto endTime = Time::currentTimeMillis() + millisecondsToRunFor;
87
88 while (quitMessageReceived.get() == 0)
89 {
90 JUCE_TRY
91 {
92 if (! dispatchNextMessageOnSystemQueue (millisecondsToRunFor >= 0))
93 Thread::sleep (1);
94 }
95 JUCE_CATCH_EXCEPTION
96
97 if (millisecondsToRunFor >= 0 && Time::currentTimeMillis() >= endTime)
98 break;
99 }
100
101 return quitMessageReceived.get() == 0;
102}
103#endif
104
105#if ! (JUCE_MAC || JUCE_IOS || JUCE_ANDROID)
106class MessageManager::QuitMessage : public MessageManager::MessageBase
107{
108public:
109 QuitMessage() {}
110
111 void messageCallback() override
112 {
113 if (auto* mm = MessageManager::instance)
114 mm->quitMessageReceived = true;
115 }
116
117 JUCE_DECLARE_NON_COPYABLE (QuitMessage)
118};
119
121{
122 jassert (isThisTheMessageThread()); // must only be called by the message thread
123
124 while (quitMessageReceived.get() == 0)
125 {
126 JUCE_TRY
127 {
128 if (! dispatchNextMessageOnSystemQueue (false))
129 Thread::sleep (1);
130 }
131 JUCE_CATCH_EXCEPTION
132 }
133}
134
136{
137 (new QuitMessage())->post();
138 quitMessagePosted = true;
139}
140
141#endif
142
143//==============================================================================
145{
146public:
147 AsyncFunctionCallback (MessageCallbackFunction* const f, void* const param)
148 : func (f), parameter (param)
149 {}
150
151 void messageCallback() override
152 {
153 result = (*func) (parameter);
154 finished.signal();
155 }
156
157 WaitableEvent finished;
158 std::atomic<void*> result { nullptr };
159
160private:
161 MessageCallbackFunction* const func;
162 void* const parameter;
163
164 JUCE_DECLARE_NON_COPYABLE (AsyncFunctionCallback)
165};
166
167void* MessageManager::callFunctionOnMessageThread (MessageCallbackFunction* const func, void* const parameter)
168{
170 return func (parameter);
171
172 // If this thread has the message manager locked, then this will deadlock!
174
176
177 if (message->post())
178 {
179 message->finished.wait();
180 return message->result.load();
181 }
182
183 jassertfalse; // the OS message queue failed to send the message!
184 return nullptr;
185}
186
187//==============================================================================
188void MessageManager::deliverBroadcastMessage (const String& value)
189{
190 if (broadcaster != nullptr)
191 broadcaster->sendActionMessage (value);
192}
193
195{
196 if (broadcaster == nullptr)
197 broadcaster.reset (new ActionBroadcaster());
198
199 broadcaster->addActionListener (listener);
200}
201
203{
204 if (broadcaster != nullptr)
205 broadcaster->removeActionListener (listener);
206}
207
208//==============================================================================
210{
211 return Thread::getCurrentThreadId() == messageThreadId;
212}
213
215{
216 auto thisThread = Thread::getCurrentThreadId();
217
218 if (messageThreadId != thisThread)
219 {
220 messageThreadId = thisThread;
221
222 // This is needed on windows to make sure the message window is created by this thread
223 doPlatformSpecificShutdown();
224 doPlatformSpecificInitialisation();
225 }
226}
227
229{
230 auto thisThread = Thread::getCurrentThreadId();
231 return thisThread == messageThreadId || thisThread == threadWithLock.get();
232}
233
235{
236 if (auto i = getInstanceWithoutCreating())
237 return i->currentThreadHasLockedMessageManager();
238
239 return false;
240}
241
243{
244 if (auto i = getInstanceWithoutCreating())
245 return i->isThisTheMessageThread();
246
247 return false;
248}
249
250//==============================================================================
251//==============================================================================
252/* The only safe way to lock the message thread while another thread does
253 some work is by posting a special message, whose purpose is to tie up the event
254 loop until the other thread has finished its business.
255
256 Any other approach can get horribly deadlocked if the OS uses its own hidden locks which
257 get locked before making an event callback, because if the same OS lock gets indirectly
258 accessed from another thread inside a MM lock, you're screwed. (this is exactly what happens
259 in Cocoa).
260*/
262{
263 BlockingMessage (const MessageManager::Lock* parent) noexcept
264 // need a const_cast here as VS2013 doesn't like a const pointer to be in an atomic
265 : owner (const_cast<MessageManager::Lock*> (parent)) {}
266
267 void messageCallback() override
268 {
269 {
270 ScopedLock lock (ownerCriticalSection);
271
272 if (auto* o = owner.get())
273 o->messageCallback();
274 }
275
276 releaseEvent.wait();
277 }
278
279 CriticalSection ownerCriticalSection;
281 WaitableEvent releaseEvent;
282
283 JUCE_DECLARE_NON_COPYABLE (BlockingMessage)
284};
285
286//==============================================================================
289void MessageManager::Lock::enter() const noexcept { tryAcquire (true); }
290bool MessageManager::Lock::tryEnter() const noexcept { return tryAcquire (false); }
291
292bool MessageManager::Lock::tryAcquire (bool lockIsMandatory) const noexcept
293{
294 auto* mm = MessageManager::instance;
295
296 if (mm == nullptr)
297 {
298 jassertfalse;
299 return false;
300 }
301
302 if (! lockIsMandatory && (abortWait.get() != 0))
303 {
304 abortWait.set (0);
305 return false;
306 }
307
308 if (mm->currentThreadHasLockedMessageManager())
309 return true;
310
311 try
312 {
313 blockingMessage = *new BlockingMessage (this);
314 }
315 catch (...)
316 {
317 jassert (! lockIsMandatory);
318 return false;
319 }
320
321 if (! blockingMessage->post())
322 {
323 // post of message failed while trying to get the lock
324 jassert (! lockIsMandatory);
325 blockingMessage = nullptr;
326 return false;
327 }
328
329 do
330 {
331 while (abortWait.get() == 0)
332 lockedEvent.wait (-1);
333
334 abortWait.set (0);
335
336 if (lockGained.get() != 0)
337 {
338 mm->threadWithLock = Thread::getCurrentThreadId();
339 return true;
340 }
341
342 } while (lockIsMandatory);
343
344 // we didn't get the lock
345 blockingMessage->releaseEvent.signal();
346
347 {
348 ScopedLock lock (blockingMessage->ownerCriticalSection);
349
350 lockGained.set (0);
351 blockingMessage->owner.set (nullptr);
352 }
353
354 blockingMessage = nullptr;
355 return false;
356}
357
358void MessageManager::Lock::exit() const noexcept
359{
360 if (lockGained.compareAndSetBool (false, true))
361 {
362 auto* mm = MessageManager::instance;
363
364 jassert (mm == nullptr || mm->currentThreadHasLockedMessageManager());
365 lockGained.set (0);
366
367 if (mm != nullptr)
368 mm->threadWithLock = {};
369
370 if (blockingMessage != nullptr)
371 {
372 blockingMessage->releaseEvent.signal();
373 blockingMessage = nullptr;
374 }
375 }
376}
377
378void MessageManager::Lock::messageCallback() const
379{
380 lockGained.set (1);
381 abort();
382}
383
384void MessageManager::Lock::abort() const noexcept
385{
386 abortWait.set (1);
387 lockedEvent.signal();
388}
389
390//==============================================================================
392 : locked (attemptLock (threadToCheck, nullptr))
393{}
394
396 : locked (attemptLock (nullptr, jobToCheck))
397{}
398
399bool MessageManagerLock::attemptLock (Thread* threadToCheck, ThreadPoolJob* jobToCheck)
400{
401 jassert (threadToCheck == nullptr || jobToCheck == nullptr);
402
403 if (threadToCheck != nullptr)
404 threadToCheck->addListener (this);
405
406 if (jobToCheck != nullptr)
407 jobToCheck->addListener (this);
408
409 // tryEnter may have a spurious abort (return false) so keep checking the condition
410 while ((threadToCheck == nullptr || ! threadToCheck->threadShouldExit())
411 && (jobToCheck == nullptr || ! jobToCheck->shouldExit()))
412 {
413 if (mmLock.tryEnter())
414 break;
415 }
416
417 if (threadToCheck != nullptr)
418 {
419 threadToCheck->removeListener (this);
420
421 if (threadToCheck->threadShouldExit())
422 return false;
423 }
424
425 if (jobToCheck != nullptr)
426 {
427 jobToCheck->removeListener (this);
428
429 if (jobToCheck->shouldExit())
430 return false;
431 }
432
433 return true;
434}
435
437
438void MessageManagerLock::exitSignalSent()
439{
440 mmLock.abort();
441}
442
443//==============================================================================
444JUCE_API void JUCE_CALLTYPE initialiseJuce_GUI();
445JUCE_API void JUCE_CALLTYPE initialiseJuce_GUI()
446{
447 JUCE_AUTORELEASEPOOL
448 {
450 }
451}
452
453JUCE_API void JUCE_CALLTYPE shutdownJuce_GUI();
454JUCE_API void JUCE_CALLTYPE shutdownJuce_GUI()
455{
456 JUCE_AUTORELEASEPOOL
457 {
460 }
461}
462
463static int numScopedInitInstances = 0;
464
465ScopedJuceInitialiser_GUI::ScopedJuceInitialiser_GUI() { if (numScopedInitInstances++ == 0) initialiseJuce_GUI(); }
466ScopedJuceInitialiser_GUI::~ScopedJuceInitialiser_GUI() { if (--numScopedInitInstances == 0) shutdownJuce_GUI(); }
467
468} // 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.
Automatically locks and unlocks a mutex object.
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 meesage 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.
bool runDispatchLoopUntil(int millisecondsToRunFor)
Synchronously dispatches messages until a given time has elapsed.
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:43
A task that is executed by a ThreadPool object.
void addListener(Thread::Listener *)
Add a listener to this thread job which will receive a callback when signalJobShouldExit was called o...
bool shouldExit() const noexcept
Returns true if something is trying to interrupt this job and make it stop.
void removeListener(Thread::Listener *)
Removes a listener added with addListener.
Encapsulates a thread.
Definition juce_Thread.h:47
static void JUCE_CALLTYPE sleep(int milliseconds)
Suspends the execution of the current thread until the specified timeout period has elapsed (note tha...
static void JUCE_CALLTYPE setCurrentThreadName(const String &newThreadName)
Changes the name of the caller thread.
bool threadShouldExit() const
Checks whether the thread has been told to stop running.
static ThreadID JUCE_CALLTYPE getCurrentThreadId()
Returns an id that identifies the caller thread.
void addListener(Listener *)
Add a listener to this thread which will receive a callback when signalThreadShouldExit was called on...
void removeListener(Listener *)
Removes a listener added with addListener.
static int64 currentTimeMillis() noexcept
Returns the current system time.
Allows threads to wait for events triggered by other threads.
#define JUCE_API
This macro is added to all JUCE public class declarations.
A simple wrapper around std::atomic.
Definition juce_Atomic.h:46
Type get() const noexcept
Atomically reads and returns the current value.
Definition juce_Atomic.h:68