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 
23 namespace juce
24 {
25 
26 MessageManager::MessageManager() noexcept
27  : messageThreadId (Thread::getCurrentThreadId())
28 {
30  Thread::setCurrentThreadName ("JUCE Message Thread");
31 }
32 
33 MessageManager::~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 
43 MessageManager* 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 //==============================================================================
67 bool 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)
82 bool 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)
106 class MessageManager::QuitMessage : public MessageManager::MessageBase
107 {
108 public:
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 {
146 public:
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 
160 private:
161  MessageCallbackFunction* const func;
162  void* const parameter;
163 
164  JUCE_DECLARE_NON_COPYABLE (AsyncFunctionCallback)
165 };
166 
167 void* 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 
175  const ReferenceCountedObjectPtr<AsyncFunctionCallback> message (new AsyncFunctionCallback (func, parameter));
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 //==============================================================================
188 void 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 //==============================================================================
289 void MessageManager::Lock::enter() const noexcept { tryAcquire (true); }
290 bool MessageManager::Lock::tryEnter() const noexcept { return tryAcquire (false); }
291 
292 bool 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 
358 void 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 
378 void MessageManager::Lock::messageCallback() const
379 {
380  lockGained.set (1);
381  abort();
382 }
383 
384 void 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 
399 bool 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 
438 void MessageManagerLock::exitSignalSent()
439 {
440  mmLock.abort();
441 }
442 
443 //==============================================================================
444 JUCE_API void JUCE_CALLTYPE initialiseJuce_GUI();
445 JUCE_API void JUCE_CALLTYPE initialiseJuce_GUI()
446 {
447  JUCE_AUTORELEASEPOOL
448  {
450  }
451 }
452 
453 JUCE_API void JUCE_CALLTYPE shutdownJuce_GUI();
454 JUCE_API void JUCE_CALLTYPE shutdownJuce_GUI()
455 {
456  JUCE_AUTORELEASEPOOL
457  {
460  }
461 }
462 
463 static int numScopedInitInstances = 0;
464 
465 ScopedJuceInitialiser_GUI::ScopedJuceInitialiser_GUI() { if (numScopedInitInstances++ == 0) initialiseJuce_GUI(); }
466 ScopedJuceInitialiser_GUI::~ScopedJuceInitialiser_GUI() { if (--numScopedInitInstances == 0) shutdownJuce_GUI(); }
467 
468 } // namespace juce
void * callFunctionOnMessageThread(MessageCallbackFunction *callback, void *userData)
Calls a function using the message-thread.
A simple wrapper around std::atomic.
Definition: juce_Atomic.h:45
#define JUCE_API
This macro is added to all JUCE public class declarations.
Interface class for delivery of events that are sent by an ActionBroadcaster.
bool currentThreadHasLockedMessageManager() const noexcept
Returns true if the caller thread has currently got the message manager locked.
static bool existsAndIsCurrentThread() noexcept
Returns true if there&#39;s an instance of the MessageManager, and if the current thread is running it...
void setCurrentThreadAsMessageThread()
Called to tell the manager that the current thread is the one that&#39;s running the dispatch loop...
void exit() const noexcept
Releases the message manager lock.
void addListener(Listener *)
Add a listener to this thread which will receive a callback when signalThreadShouldExit was called on...
void removeListener(Thread::Listener *)
Removes a listener added with addListener.
static void deleteAll()
Deletes all extant objects.
ScopedJuceInitialiser_GUI()
The constructor simply calls initialiseJuce_GUI().
Allows threads to wait for events triggered by other threads.
static MessageManager * getInstance()
Returns the global instance of the MessageManager.
This class is in charge of the application&#39;s event-dispatch loop.
Lock()
Creates a new critical section to exclusively access methods which can only be called when the messag...
The JUCE String class!
Definition: juce_String.h:42
bool runDispatchLoopUntil(int millisecondsToRunFor)
Synchronously dispatches messages until a given time has elapsed.
void stopDispatchLoop()
Sends a signal that the dispatch loop should terminate.
static int64 currentTimeMillis() noexcept
Returns the current system time.
Definition: juce_Time.cpp:205
static ThreadID JUCE_CALLTYPE getCurrentThreadId()
Returns an id that identifies the caller thread.
void runDispatchLoop()
Runs the event dispatch loop until a stop message is posted.
bool isThisTheMessageThread() const noexcept
Returns true if the caller-thread is the message thread.
~MessageManagerLock() override
Releases the current thread&#39;s lock on the message manager.
bool threadShouldExit() const
Checks whether the thread has been told to stop running.
Encapsulates a thread.
Definition: juce_Thread.h:46
MessageManagerLock(Thread *threadToCheckForExitSignal=nullptr)
Tries to acquire a lock on the message manager.
void enter() const noexcept
Acquires the message manager lock.
void addListener(Thread::Listener *)
Add a listener to this thread job which will receive a callback when signalJobShouldExit was called o...
void registerBroadcastListener(ActionListener *listener)
Registers a listener to get told about broadcast messages.
static void deleteInstance()
Deletes the global MessageManager instance.
~ScopedJuceInitialiser_GUI()
The destructor simply calls shutdownJuce_GUI().
Type get() const noexcept
Atomically reads and returns the current value.
Definition: juce_Atomic.h:68
A re-entrant mutex.
static bool existsAndIsLockedByCurrentThread() noexcept
Returns true if there&#39;s an instance of the MessageManager, and if the current thread has the lock on ...
bool tryEnter() const noexcept
Attempts to lock the meesage manager and exits if abort is called.
A smart-pointer class which points to a reference-counted object.
static void JUCE_CALLTYPE sleep(int milliseconds)
Suspends the execution of the current thread until the specified timeout period has elapsed (note tha...
A task that is executed by a ThreadPool object.
static MessageManager * getInstanceWithoutCreating() noexcept
Returns the global instance of the MessageManager, or nullptr if it doesn&#39;t exist.
Automatically locks and unlocks a mutex object.
void deregisterBroadcastListener(ActionListener *listener)
Deregisters a broadcast listener.
void abort() const noexcept
Unblocks a thread which is waiting in tryEnter Call this method if you want to unblock a thread which...
void removeListener(Listener *)
Removes a listener added with addListener.
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...
Manages a list of ActionListeners, and can send them messages.
Internal class used as the base class for all message objects.
static void JUCE_CALLTYPE setCurrentThreadName(const String &newThreadName)
Changes the name of the caller thread.
bool shouldExit() const noexcept
Returns true if something is trying to interrupt this job and make it stop.
A lock you can use to lock the message manager.