OpenShot Library | OpenShotAudio  0.2.2
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* func, void* 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 bool MessageManager::callAsync (std::function<void()> fn)
188 {
189  struct AsyncCallInvoker : public MessageBase
190  {
191  AsyncCallInvoker (std::function<void()> f) : callback (std::move (f)) {}
192  void messageCallback() override { callback(); }
193  std::function<void()> callback;
194  };
195 
196  return (new AsyncCallInvoker (std::move (fn)))->post();
197 }
198 
199 //==============================================================================
200 void MessageManager::deliverBroadcastMessage (const String& value)
201 {
202  if (broadcaster != nullptr)
203  broadcaster->sendActionMessage (value);
204 }
205 
207 {
208  if (broadcaster == nullptr)
209  broadcaster.reset (new ActionBroadcaster());
210 
211  broadcaster->addActionListener (listener);
212 }
213 
215 {
216  if (broadcaster != nullptr)
217  broadcaster->removeActionListener (listener);
218 }
219 
220 //==============================================================================
222 {
223  return Thread::getCurrentThreadId() == messageThreadId;
224 }
225 
227 {
228  auto thisThread = Thread::getCurrentThreadId();
229 
230  if (messageThreadId != thisThread)
231  {
232  messageThreadId = thisThread;
233 
234  // This is needed on windows to make sure the message window is created by this thread
235  doPlatformSpecificShutdown();
236  doPlatformSpecificInitialisation();
237  }
238 }
239 
241 {
242  auto thisThread = Thread::getCurrentThreadId();
243  return thisThread == messageThreadId || thisThread == threadWithLock.get();
244 }
245 
247 {
248  if (auto i = getInstanceWithoutCreating())
249  return i->currentThreadHasLockedMessageManager();
250 
251  return false;
252 }
253 
255 {
256  if (auto i = getInstanceWithoutCreating())
257  return i->isThisTheMessageThread();
258 
259  return false;
260 }
261 
262 //==============================================================================
263 //==============================================================================
264 /* The only safe way to lock the message thread while another thread does
265  some work is by posting a special message, whose purpose is to tie up the event
266  loop until the other thread has finished its business.
267 
268  Any other approach can get horribly deadlocked if the OS uses its own hidden locks which
269  get locked before making an event callback, because if the same OS lock gets indirectly
270  accessed from another thread inside a MM lock, you're screwed. (this is exactly what happens
271  in Cocoa).
272 */
274 {
275  BlockingMessage (const MessageManager::Lock* parent) noexcept
276  : owner (parent)
277  {}
278 
279  void messageCallback() override
280  {
281  {
282  ScopedLock lock (ownerCriticalSection);
283 
284  if (auto* o = owner.get())
285  o->messageCallback();
286  }
287 
288  releaseEvent.wait();
289  }
290 
291  CriticalSection ownerCriticalSection;
293  WaitableEvent releaseEvent;
294 
295  JUCE_DECLARE_NON_COPYABLE (BlockingMessage)
296 };
297 
298 //==============================================================================
301 void MessageManager::Lock::enter() const noexcept { tryAcquire (true); }
302 bool MessageManager::Lock::tryEnter() const noexcept { return tryAcquire (false); }
303 
304 bool MessageManager::Lock::tryAcquire (bool lockIsMandatory) const noexcept
305 {
306  auto* mm = MessageManager::instance;
307 
308  if (mm == nullptr)
309  {
310  jassertfalse;
311  return false;
312  }
313 
314  if (! lockIsMandatory && (abortWait.get() != 0))
315  {
316  abortWait.set (0);
317  return false;
318  }
319 
320  if (mm->currentThreadHasLockedMessageManager())
321  return true;
322 
323  try
324  {
325  blockingMessage = *new BlockingMessage (this);
326  }
327  catch (...)
328  {
329  jassert (! lockIsMandatory);
330  return false;
331  }
332 
333  if (! blockingMessage->post())
334  {
335  // post of message failed while trying to get the lock
336  jassert (! lockIsMandatory);
337  blockingMessage = nullptr;
338  return false;
339  }
340 
341  do
342  {
343  while (abortWait.get() == 0)
344  lockedEvent.wait (-1);
345 
346  abortWait.set (0);
347 
348  if (lockGained.get() != 0)
349  {
350  mm->threadWithLock = Thread::getCurrentThreadId();
351  return true;
352  }
353 
354  } while (lockIsMandatory);
355 
356  // we didn't get the lock
357  blockingMessage->releaseEvent.signal();
358 
359  {
360  ScopedLock lock (blockingMessage->ownerCriticalSection);
361 
362  lockGained.set (0);
363  blockingMessage->owner.set (nullptr);
364  }
365 
366  blockingMessage = nullptr;
367  return false;
368 }
369 
370 void MessageManager::Lock::exit() const noexcept
371 {
372  if (lockGained.compareAndSetBool (false, true))
373  {
374  auto* mm = MessageManager::instance;
375 
376  jassert (mm == nullptr || mm->currentThreadHasLockedMessageManager());
377  lockGained.set (0);
378 
379  if (mm != nullptr)
380  mm->threadWithLock = {};
381 
382  if (blockingMessage != nullptr)
383  {
384  blockingMessage->releaseEvent.signal();
385  blockingMessage = nullptr;
386  }
387  }
388 }
389 
390 void MessageManager::Lock::messageCallback() const
391 {
392  lockGained.set (1);
393  abort();
394 }
395 
396 void MessageManager::Lock::abort() const noexcept
397 {
398  abortWait.set (1);
399  lockedEvent.signal();
400 }
401 
402 //==============================================================================
404  : locked (attemptLock (threadToCheck, nullptr))
405 {}
406 
408  : locked (attemptLock (nullptr, jobToCheck))
409 {}
410 
411 bool MessageManagerLock::attemptLock (Thread* threadToCheck, ThreadPoolJob* jobToCheck)
412 {
413  jassert (threadToCheck == nullptr || jobToCheck == nullptr);
414 
415  if (threadToCheck != nullptr)
416  threadToCheck->addListener (this);
417 
418  if (jobToCheck != nullptr)
419  jobToCheck->addListener (this);
420 
421  // tryEnter may have a spurious abort (return false) so keep checking the condition
422  while ((threadToCheck == nullptr || ! threadToCheck->threadShouldExit())
423  && (jobToCheck == nullptr || ! jobToCheck->shouldExit()))
424  {
425  if (mmLock.tryEnter())
426  break;
427  }
428 
429  if (threadToCheck != nullptr)
430  {
431  threadToCheck->removeListener (this);
432 
433  if (threadToCheck->threadShouldExit())
434  return false;
435  }
436 
437  if (jobToCheck != nullptr)
438  {
439  jobToCheck->removeListener (this);
440 
441  if (jobToCheck->shouldExit())
442  return false;
443  }
444 
445  return true;
446 }
447 
449 
450 void MessageManagerLock::exitSignalSent()
451 {
452  mmLock.abort();
453 }
454 
455 //==============================================================================
456 JUCE_API void JUCE_CALLTYPE initialiseJuce_GUI();
457 JUCE_API void JUCE_CALLTYPE initialiseJuce_GUI()
458 {
459  JUCE_AUTORELEASEPOOL
460  {
462  }
463 }
464 
465 JUCE_API void JUCE_CALLTYPE shutdownJuce_GUI();
466 JUCE_API void JUCE_CALLTYPE shutdownJuce_GUI()
467 {
468  JUCE_AUTORELEASEPOOL
469  {
472  }
473 }
474 
475 static int numScopedInitInstances = 0;
476 
477 ScopedJuceInitialiser_GUI::ScopedJuceInitialiser_GUI() { if (numScopedInitInstances++ == 0) initialiseJuce_GUI(); }
478 ScopedJuceInitialiser_GUI::~ScopedJuceInitialiser_GUI() { if (--numScopedInitInstances == 0) shutdownJuce_GUI(); }
479 
480 } // 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 message 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 bool callAsync(std::function< void()> functionToCall)
Asynchronously invokes a function or C++11 lambda on the message thread.
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.