From d60f6617896e6fccc8f0ab27fb53cb33a08db71c Mon Sep 17 00:00:00 2001 From: Julian Storer Date: Sun, 19 Dec 2010 19:33:52 +0000 Subject: [PATCH] Changed the Message class to be reference-counted, and used this to tighten up some messaging code. Minor tweaks to AudioThumbnail, ReferenceCountedArray. --- juce_amalgamated.cpp | 339 +++++++++--------- juce_amalgamated.h | 316 ++++++++-------- .../juce_AudioFormatWriter.cpp | 2 +- .../juce_AudioThumbnail.cpp | 5 +- .../audio_file_formats/juce_AudioThumbnail.h | 2 +- src/audio/dsp/juce_AudioDataConverters.h | 2 + src/containers/juce_ReferenceCountedArray.h | 47 ++- src/core/juce_StandardHeader.h | 2 +- src/events/juce_AsyncUpdater.cpp | 25 +- src/events/juce_AsyncUpdater.h | 16 +- src/events/juce_CallbackMessage.h | 15 +- src/events/juce_Message.h | 6 +- src/events/juce_MessageManager.cpp | 63 ++-- src/events/juce_MessageManager.h | 6 +- src/events/juce_Timer.cpp | 3 +- src/gui/components/juce_Component.cpp | 12 + src/native/juce_mac_NativeCode.mm | 69 ++++ src/native/linux/juce_linux_Messaging.cpp | 12 +- src/native/mac/juce_iphone_MessageManager.mm | 90 ++--- src/native/mac/juce_mac_Files.mm | 6 +- src/native/mac/juce_mac_MessageManager.mm | 45 +-- src/native/windows/juce_win32_Messaging.cpp | 9 +- 22 files changed, 569 insertions(+), 523 deletions(-) diff --git a/juce_amalgamated.cpp b/juce_amalgamated.cpp index ed525ceae7..331b3be5be 100644 --- a/juce_amalgamated.cpp +++ b/juce_amalgamated.cpp @@ -21666,7 +21666,7 @@ public: void setThumbnail (AudioThumbnail* thumb) { if (thumb != 0) - thumb->reset (buffer.getNumChannels(), writer->getSampleRate()); + thumb->reset (buffer.getNumChannels(), writer->getSampleRate(), 0); const ScopedLock sl (thumbnailLock); thumbnailToUpdate = thumb; @@ -22475,14 +22475,15 @@ void AudioThumbnail::clear() sendChangeMessage(); } -void AudioThumbnail::reset (int newNumChannels, double newSampleRate) +void AudioThumbnail::reset (int newNumChannels, double newSampleRate, int64 totalSamplesInSource) { clear(); numChannels = newNumChannels; sampleRate = newSampleRate; + totalSamples = totalSamplesInSource; - createChannels (0); + createChannels (1 + (int) (totalSamplesInSource / samplesPerThumbSample)); } void AudioThumbnail::createChannels (const int length) @@ -38416,28 +38417,34 @@ END_JUCE_NAMESPACE /*** Start of inlined file: juce_AsyncUpdater.cpp ***/ BEGIN_JUCE_NAMESPACE -class AsyncUpdater::AsyncUpdaterMessage : public CallbackMessage +class AsyncUpdaterMessage : public CallbackMessage { public: AsyncUpdaterMessage (AsyncUpdater& owner_) : owner (owner_) { - setMessageIsDeletedOnDelivery (false); } void messageCallback() { - if (owner.pendingMessage.compareAndSetBool (0, this)) + if (shouldDeliver.compareAndSetBool (0, 1)) owner.handleAsyncUpdate(); } + Atomic shouldDeliver; + private: AsyncUpdater& owner; }; AsyncUpdater::AsyncUpdater() - : message (new AsyncUpdaterMessage (*this)) { + message = new AsyncUpdaterMessage (*this); +} + +inline Atomic& AsyncUpdater::getDeliveryFlag() const throw() +{ + return static_cast (message.getObject())->shouldDeliver; } AsyncUpdater::~AsyncUpdater() @@ -38448,19 +38455,18 @@ AsyncUpdater::~AsyncUpdater() // deleting this object, or find some other way to avoid such a race condition. jassert ((! isUpdatePending()) || MessageManager::getInstance()->currentThreadHasLockedMessageManager()); - if (pendingMessage.exchange (0) != 0) - message.release()->setMessageIsDeletedOnDelivery (true); + getDeliveryFlag().set (0); } void AsyncUpdater::triggerAsyncUpdate() { - if (pendingMessage.compareAndSetBool (message, 0)) + if (getDeliveryFlag().compareAndSetBool (1, 0)) message->post(); } void AsyncUpdater::cancelPendingUpdate() throw() { - pendingMessage = 0; + getDeliveryFlag().set (0); } void AsyncUpdater::handleUpdateNowIfNeeded() @@ -38468,13 +38474,13 @@ void AsyncUpdater::handleUpdateNowIfNeeded() // This can only be called by the event thread. jassert (MessageManager::getInstance()->currentThreadHasLockedMessageManager()); - if (pendingMessage.exchange (0) != 0) + if (getDeliveryFlag().exchange (0) != 0) handleAsyncUpdate(); } bool AsyncUpdater::isUpdatePending() const throw() { - return pendingMessage.value != 0; + return getDeliveryFlag().value != 0; } END_JUCE_NAMESPACE @@ -39088,10 +39094,10 @@ MessageManager* MessageManager::getInstance() throw() void MessageManager::postMessageToQueue (Message* const message) { if (quitMessagePosted || ! juce_postMessageToSystemQueue (message)) - delete message; + Message::Ptr deleter (message); // (this will delete messages that were just created with a 0 ref count) } -CallbackMessage::CallbackMessage() throw() : deleteOnDelivery (true) {} +CallbackMessage::CallbackMessage() throw() {} CallbackMessage::~CallbackMessage() {} void CallbackMessage::post() @@ -39105,7 +39111,6 @@ void MessageManager::deliverMessage (Message* const message) { JUCE_TRY { - ScopedPointer messageDeleter (message); MessageListener* const recipient = message->messageRecipient; if (recipient == 0) @@ -39115,9 +39120,6 @@ void MessageManager::deliverMessage (Message* const message) if (callbackMessage != 0) { callbackMessage->messageCallback(); - - if (! callbackMessage->isMessageDeletedOnDelivery()) - messageDeleter.release(); } else if (message->intParameter1 == quitMessageId) { @@ -39200,9 +39202,11 @@ bool MessageManager::isThisTheMessageThread() const throw() void MessageManager::setCurrentThreadAsMessageThread() { - if (messageThreadId != Thread::getCurrentThreadId()) + const Thread::ThreadID thisThread = Thread::getCurrentThreadId(); + + if (messageThreadId != thisThread) { - messageThreadId = Thread::getCurrentThreadId(); + messageThreadId = thisThread; // This is needed on windows to make sure the message window is created by this thread doPlatformSpecificShutdown(); @@ -39225,47 +39229,31 @@ bool MessageManager::currentThreadHasLockedMessageManager() const throw() accessed from another thread inside a MM lock, you're screwed. (this is exactly what happens in Cocoa). */ -class MessageManagerLock::SharedEvents : public ReferenceCountedObject -{ -public: - SharedEvents() {} - - /* This class just holds a couple of events to communicate between the BlockingMessage - and the MessageManagerLock. Because both of these objects may be deleted at any time, - this shared data must be kept in a separate, ref-counted container. */ - WaitableEvent lockedEvent, releaseEvent; - -private: - JUCE_DECLARE_NON_COPYABLE (SharedEvents); -}; - class MessageManagerLock::BlockingMessage : public CallbackMessage { public: - BlockingMessage (MessageManagerLock::SharedEvents* const events_) : events (events_) {} + BlockingMessage() {} void messageCallback() { - events->lockedEvent.signal(); - events->releaseEvent.wait(); + lockedEvent.signal(); + releaseEvent.wait(); } -private: - ReferenceCountedObjectPtr events; + WaitableEvent lockedEvent, releaseEvent; +private: JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BlockingMessage); }; MessageManagerLock::MessageManagerLock (Thread* const threadToCheck) - : sharedEvents (0), - locked (false) + : locked (false) { init (threadToCheck, 0); } MessageManagerLock::MessageManagerLock (ThreadPoolJob* const jobToCheckForExitSignal) - : sharedEvents (0), - locked (false) + : locked (false) { init (0, jobToCheckForExitSignal); } @@ -39296,19 +39284,16 @@ void MessageManagerLock::init (Thread* const threadToCheck, ThreadPoolJob* const } } - sharedEvents = new SharedEvents(); - sharedEvents->incReferenceCount(); - - (new BlockingMessage (sharedEvents))->post(); + blockingMessage = new BlockingMessage(); + blockingMessage->post(); - while (! sharedEvents->lockedEvent.wait (50)) + while (! blockingMessage->lockedEvent.wait (20)) { if ((threadToCheck != 0 && threadToCheck->threadShouldExit()) || (job != 0 && job->shouldExit())) { - sharedEvents->releaseEvent.signal(); - sharedEvents->decReferenceCount(); - sharedEvents = 0; + blockingMessage->releaseEvent.signal(); + blockingMessage = 0; MessageManager::instance->lockingLock.exit(); return; } @@ -39324,12 +39309,12 @@ void MessageManagerLock::init (Thread* const threadToCheck, ThreadPoolJob* const MessageManagerLock::~MessageManagerLock() throw() { - if (sharedEvents != 0) + if (blockingMessage != 0) { jassert (MessageManager::instance == 0 || MessageManager::instance->currentThreadHasLockedMessageManager()); - sharedEvents->releaseEvent.signal(); - sharedEvents->decReferenceCount(); + blockingMessage->releaseEvent.signal(); + blockingMessage = 0; if (MessageManager::instance != 0) { @@ -39478,6 +39463,7 @@ public: void run() { uint32 lastTime = Time::getMillisecondCounter(); + Message::Ptr message (new Message()); while (! threadShouldExit()) { @@ -39503,7 +39489,7 @@ public: */ if (callbackNeeded.compareAndSetBool (1, 0)) { - postMessage (new Message()); + postMessage (message); /* Sometimes our message can get discarded by the OS (e.g. when running as an RTAS when the app has a modal loop), so this is how long to wait before assuming the @@ -41290,6 +41276,10 @@ void Component::exitModalState (const int returnValue) } else { + // if component methods are being called from threads other than the message + // thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe. + CHECK_MESSAGE_MANAGER_IS_LOCKED + class ExitModalStateMessage : public CallbackMessage { public: @@ -41854,6 +41844,10 @@ void Component::parentSizeChanged() void Component::addComponentListener (ComponentListener* const newListener) { + // if component methods are being called from threads other than the message + // thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe. + CHECK_MESSAGE_MANAGER_IS_LOCKED + componentListeners.add (newListener); } @@ -41895,6 +41889,10 @@ void Component::paintOverChildren (Graphics&) void Component::postCommandMessage (const int commandId) { + // if component methods are being called from threads other than the message + // thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe. + CHECK_MESSAGE_MANAGER_IS_LOCKED + class CustomCommandMessage : public CallbackMessage { public: @@ -239896,7 +239894,9 @@ static LRESULT CALLBACK juce_MessageWndProc (HWND h, // here in case there are windows modal dialog boxes doing their own // dispatch loop and not calling our version - MessageManager::getInstance()->deliverMessage ((Message*) lParam); + Message* const message = reinterpret_cast (lParam); + MessageManager::getInstance()->deliverMessage (message); + message->decReferenceCount(); return 0; } else if (message == broadcastId) @@ -239984,7 +239984,9 @@ bool juce_dispatchNextMessageOnSystemQueue (const bool returnIfNoPendingMessages { if (m.message == specialId && m.hwnd == juce_messageWindowHandle) { - MessageManager::getInstance()->deliverMessage ((Message*) (void*) m.lParam); + Message* const message = reinterpret_cast (m.lParam); + MessageManager::getInstance()->deliverMessage (message); + message->decReferenceCount(); } else if (m.message == WM_QUIT) { @@ -240014,6 +240016,7 @@ bool juce_dispatchNextMessageOnSystemQueue (const bool returnIfNoPendingMessages bool juce_postMessageToSystemQueue (Message* message) { + message->incReferenceCount(); return PostMessage (juce_messageWindowHandle, specialId, 0, (LPARAM) message) != 0; } @@ -255883,7 +255886,7 @@ public: private: CriticalSection lock; - OwnedArray queue; + ReferenceCountedArray queue; int fd[2]; int bytesInSocket; int totalEventCount; @@ -255923,15 +255926,15 @@ private: return true; } - Message* popNextMessage() + const Message::Ptr popNextMessage() { - ScopedLock sl (lock); + const ScopedLock sl (lock); if (bytesInSocket > 0) { --bytesInSocket; - ScopedUnlock ul (lock); + const ScopedUnlock ul (lock); unsigned char x; size_t numBytes = read (fd[1], &x, 1); (void) numBytes; @@ -255942,7 +255945,7 @@ private: bool dispatchNextInternalMessage() { - ScopedPointer msg (popNextMessage()); + const Message::Ptr msg (popNextMessage()); if (msg == 0) return false; @@ -255958,7 +255961,7 @@ private: else { // Handle "normal" messages - MessageManager::getInstance()->deliverMessage (msg.release()); + MessageManager::getInstance()->deliverMessage (msg); } return true; @@ -262337,6 +262340,73 @@ namespace } } +class MessageQueue +{ +public: + MessageQueue() + { + #if MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_4 && ! JUCE_IOS + runLoop = CFRunLoopGetMain(); + #else + runLoop = CFRunLoopGetCurrent(); + #endif + + CFRunLoopSourceContext sourceContext; + zerostruct (sourceContext); + sourceContext.info = this; + sourceContext.perform = runLoopSourceCallback; + runLoopSource = CFRunLoopSourceCreate (kCFAllocatorDefault, 1, &sourceContext); + CFRunLoopAddSource (runLoop, runLoopSource, kCFRunLoopCommonModes); + } + + ~MessageQueue() + { + CFRunLoopRemoveSource (runLoop, runLoopSource, kCFRunLoopCommonModes); + CFRunLoopSourceInvalidate (runLoopSource); + CFRelease (runLoopSource); + } + + void post (Message* const message) + { + messages.add (message); + CFRunLoopSourceSignal (runLoopSource); + CFRunLoopWakeUp (runLoop); + } + +private: + ReferenceCountedArray messages; + CriticalSection lock; + CFRunLoopRef runLoop; + CFRunLoopSourceRef runLoopSource; + + bool deliverNextMessage() + { + const Message::Ptr nextMessage (messages.removeAndReturn (0)); + + if (nextMessage == 0) + return false; + + const ScopedAutoReleasePool pool; + MessageManager::getInstance()->deliverMessage (nextMessage); + return true; + } + + void runLoopCallback() + { + for (int i = 4; --i >= 0;) + if (! deliverNextMessage()) + return; + + CFRunLoopSourceSignal (runLoopSource); + CFRunLoopWakeUp (runLoop); + } + + static void runLoopSourceCallback (void* info) + { + static_cast (info)->runLoopCallback(); + } +}; + #define JUCE_INCLUDED_FILE 1 // Now include the actual code files.. @@ -264281,12 +264351,12 @@ const File File::getSpecialLocation (const SpecialLocationType type) case userHomeDirectory: resultPath = nsStringToJuce (NSHomeDirectory()); break; #if JUCE_IOS - case userDocumentsDirectory: resultPath = getIOSSystemLocation (NSDocumentDirectory); break; - case userDesktopDirectory: resultPath = getIOSSystemLocation (NSDesktopDirectory); break; + case userDocumentsDirectory: resultPath = FileHelpers::getIOSSystemLocation (NSDocumentDirectory); break; + case userDesktopDirectory: resultPath = FileHelpers::getIOSSystemLocation (NSDesktopDirectory); break; case tempDirectory: { - File tmp (getIOSSystemLocation (NSCachesDirectory)); + File tmp (FileHelpers::getIOSSystemLocation (NSCachesDirectory)); tmp = tmp.getChildFile (juce_getExecutableFile().getFileNameWithoutExtension()); tmp.createDirectory(); return tmp.getFullPathName(); @@ -267433,79 +267503,41 @@ bool MessageManager::runDispatchLoopUntil (int millisecondsToRunFor) return ! quitMessagePosted; } -namespace iOSMessageLoopHelpers +struct MessageDispatchSystem { - static CFRunLoopRef runLoop = 0; - static CFRunLoopSourceRef runLoopSource = 0; - static OwnedArray * pendingMessages = 0; - static JuceCustomMessageHandler* juceCustomMessageHandler = 0; - - void runLoopSourceCallback (void*) + MessageDispatchSystem() + : juceCustomMessageHandler (0) { - if (pendingMessages != 0) - { - int numDispatched = 0; - - do - { - Message* const nextMessage = pendingMessages->removeAndReturn (0); - - if (nextMessage == 0) - return; + juceCustomMessageHandler = [[JuceCustomMessageHandler alloc] init]; + } - const ScopedAutoReleasePool pool; - MessageManager::getInstance()->deliverMessage (nextMessage); + ~MessageDispatchSystem() + { + [[NSRunLoop currentRunLoop] cancelPerformSelectorsWithTarget: juceCustomMessageHandler]; + [juceCustomMessageHandler release]; + } - } while (++numDispatched <= 4); + JuceCustomMessageHandler* juceCustomMessageHandler; + MessageQueue messageQueue; +}; - CFRunLoopSourceSignal (runLoopSource); - CFRunLoopWakeUp (runLoop); - } - } -} +static MessageDispatchSystem* dispatcher = 0; void MessageManager::doPlatformSpecificInitialisation() { - using namespace iOSMessageLoopHelpers; - pendingMessages = new OwnedArray (); - - runLoop = CFRunLoopGetCurrent(); - CFRunLoopSourceContext sourceContext; - zerostruct (sourceContext); - sourceContext.perform = runLoopSourceCallback; - runLoopSource = CFRunLoopSourceCreate (kCFAllocatorDefault, 1, &sourceContext); - CFRunLoopAddSource (runLoop, runLoopSource, kCFRunLoopCommonModes); - - if (juceCustomMessageHandler == 0) - juceCustomMessageHandler = [[JuceCustomMessageHandler alloc] init]; + if (dispatcher == 0) + dispatcher = new MessageDispatchSystem(); } void MessageManager::doPlatformSpecificShutdown() { - using namespace iOSMessageLoopHelpers; - CFRunLoopSourceInvalidate (runLoopSource); - CFRelease (runLoopSource); - runLoopSource = 0; - deleteAndZero (pendingMessages); - - if (juceCustomMessageHandler != 0) - { - [[NSRunLoop currentRunLoop] cancelPerformSelectorsWithTarget: juceCustomMessageHandler]; - [juceCustomMessageHandler release]; - juceCustomMessageHandler = 0; - } + deleteAndZero (dispatcher); } bool juce_postMessageToSystemQueue (Message* message) { - using namespace iOSMessageLoopHelpers; - - if (pendingMessages != 0) - { - pendingMessages->add (message); - CFRunLoopSourceSignal (runLoopSource); - CFRunLoopWakeUp (runLoop); - } + if (dispatcher != 0) + dispatcher->messageQueue.post (message); return true; } @@ -267516,14 +267548,14 @@ void MessageManager::broadcastMessage (const String& value) void* MessageManager::callFunctionOnMessageThread (MessageCallbackFunction* callback, void* data) { - using namespace iOSMessageLoopHelpers; - if (isThisTheMessageThread()) { return (*callback) (data); } else { + jassert (dispatcher != 0); // trying to call this when the juce system isn't initialised.. + // If a thread has a MessageManagerLock and then tries to call this method, it'll // deadlock because the message manager is blocked from running, so can never // call your function.. @@ -267537,11 +267569,11 @@ void* MessageManager::callFunctionOnMessageThread (MessageCallbackFunction* call cmp.result = 0; cmp.hasBeenExecuted = false; - [juceCustomMessageHandler performSelectorOnMainThread: @selector (performCallback:) - withObject: [NSData dataWithBytesNoCopy: &cmp - length: sizeof (cmp) - freeWhenDone: NO] - waitUntilDone: YES]; + [dispatcher->juceCustomMessageHandler performSelectorOnMainThread: @selector (performCallback:) + withObject: [NSData dataWithBytesNoCopy: &cmp + length: sizeof (cmp) + freeWhenDone: NO] + waitUntilDone: YES]; return cmp.result; } @@ -275660,24 +275692,10 @@ class AppDelegateRedirector public: AppDelegateRedirector() { -#if MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_4 - runLoop = CFRunLoopGetMain(); -#else - runLoop = CFRunLoopGetCurrent(); -#endif - CFRunLoopSourceContext sourceContext; - zerostruct (sourceContext); - sourceContext.info = this; - sourceContext.perform = runLoopSourceCallback; - runLoopSource = CFRunLoopSourceCreate (kCFAllocatorDefault, 1, &sourceContext); - CFRunLoopAddSource (runLoop, runLoopSource, kCFRunLoopCommonModes); } virtual ~AppDelegateRedirector() { - CFRunLoopRemoveSource (runLoop, runLoopSource, kCFRunLoopCommonModes); - CFRunLoopSourceInvalidate (runLoopSource); - CFRelease (runLoopSource); } virtual NSApplicationTerminateReply shouldTerminate() @@ -275753,40 +275771,13 @@ public: void postMessage (Message* const m) { - messages.add (m); - CFRunLoopSourceSignal (runLoopSource); - CFRunLoopWakeUp (runLoop); + messageQueue.post (m); } private: CFRunLoopRef runLoop; CFRunLoopSourceRef runLoopSource; - OwnedArray messages; - - void runLoopCallback() - { - int numDispatched = 0; - - do - { - Message* const nextMessage = messages.removeAndReturn (0); - - if (nextMessage == 0) - return; - - const ScopedAutoReleasePool pool; - MessageManager::getInstance()->deliverMessage (nextMessage); - - } while (++numDispatched <= 4); - - CFRunLoopSourceSignal (runLoopSource); - CFRunLoopWakeUp (runLoop); - } - - static void runLoopSourceCallback (void* info) - { - static_cast (info)->runLoopCallback(); - } + MessageQueue messageQueue; }; END_JUCE_NAMESPACE diff --git a/juce_amalgamated.h b/juce_amalgamated.h index f209aead28..a68fd567e7 100644 --- a/juce_amalgamated.h +++ b/juce_amalgamated.h @@ -64,7 +64,7 @@ */ #define JUCE_MAJOR_VERSION 1 #define JUCE_MINOR_VERSION 52 -#define JUCE_BUILDNUMBER 106 +#define JUCE_BUILDNUMBER 107 /** Current Juce version number. @@ -11025,6 +11025,7 @@ template ObjectClassPtr; /** Creates an empty array. @see ReferenceCountedObject, Array, OwnedArray @@ -11101,7 +11102,7 @@ public: @see getUnchecked */ - inline const ReferenceCountedObjectPtr operator[] (const int index) const throw() + inline const ObjectClassPtr operator[] (const int index) const throw() { const ScopedLockType lock (getLock()); return isPositiveAndBelow (index, numUsed) ? data.elements [index] @@ -11113,7 +11114,7 @@ public: This is a faster and less safe version of operator[] which doesn't check the index passed in, so it can be used when you're sure the index if always going to be legal. */ - inline const ReferenceCountedObjectPtr getUnchecked (const int index) const throw() + inline const ObjectClassPtr getUnchecked (const int index) const throw() { const ScopedLockType lock (getLock()); jassert (isPositiveAndBelow (index, numUsed)); @@ -11125,7 +11126,7 @@ public: This will return a null pointer if the array's empty. @see getLast */ - inline const ReferenceCountedObjectPtr getFirst() const throw() + inline const ObjectClassPtr getFirst() const throw() { const ScopedLockType lock (getLock()); return numUsed > 0 ? data.elements [0] @@ -11137,7 +11138,7 @@ public: This will return a null pointer if the array's empty. @see getFirst */ - inline const ReferenceCountedObjectPtr getLast() const throw() + inline const ObjectClassPtr getLast() const throw() { const ScopedLockType lock (getLock()); return numUsed > 0 ? data.elements [numUsed - 1] @@ -11409,6 +11410,43 @@ public: } } + /** Removes and returns an object from the array. + + This will remove the object at a given index and return it, moving back all + the subsequent objects to close the gap. If the index passed in is out-of-range, + nothing will happen and a null pointer will be returned. + + @param indexToRemove the index of the element to remove + @see remove, removeObject, removeRange + */ + const ObjectClassPtr removeAndReturn (const int indexToRemove) + { + ObjectClassPtr removedItem; + const ScopedLockType lock (getLock()); + + if (isPositiveAndBelow (indexToRemove, numUsed)) + { + ObjectClass** const e = data.elements + indexToRemove; + + if (*e != 0) + { + removedItem = *e; + (*e)->decReferenceCount(); + } + + --numUsed; + const int numberToShift = numUsed - indexToRemove; + + if (numberToShift > 0) + memmove (e, e + 1, numberToShift * sizeof (ObjectClass*)); + + if ((numUsed << 1) < data.numAllocated) + minimiseStorageOverheads(); + } + + return removedItem; + } + /** Removes the first occurrence of a specified object from the array. If the item isn't found, no action is taken. If it is found, it is @@ -12476,6 +12514,123 @@ private: #ifndef __JUCE_ASYNCUPDATER_JUCEHEADER__ #define __JUCE_ASYNCUPDATER_JUCEHEADER__ + +/*** Start of inlined file: juce_CallbackMessage.h ***/ +#ifndef __JUCE_CALLBACKMESSAGE_JUCEHEADER__ +#define __JUCE_CALLBACKMESSAGE_JUCEHEADER__ + + +/*** Start of inlined file: juce_Message.h ***/ +#ifndef __JUCE_MESSAGE_JUCEHEADER__ +#define __JUCE_MESSAGE_JUCEHEADER__ + +class MessageListener; +class MessageManager; + +/** The base class for objects that can be delivered to a MessageListener. + + The simplest Message object contains a few integer and pointer parameters + that the user can set, and this is enough for a lot of purposes. For passing more + complex data, subclasses of Message can also be used. + + @see MessageListener, MessageManager, ActionListener, ChangeListener +*/ +class JUCE_API Message : public ReferenceCountedObject +{ +public: + + /** Creates an uninitialised message. + + The class's variables will also be left uninitialised. + */ + Message() throw(); + + /** Creates a message object, filling in the member variables. + + The corresponding public member variables will be set from the parameters + passed in. + */ + Message (int intParameter1, + int intParameter2, + int intParameter3, + void* pointerParameter) throw(); + + /** Destructor. */ + virtual ~Message(); + + // These values can be used for carrying simple data that the application needs to + // pass around. For more complex messages, just create a subclass. + + int intParameter1; /**< user-defined integer value. */ + int intParameter2; /**< user-defined integer value. */ + int intParameter3; /**< user-defined integer value. */ + void* pointerParameter; /**< user-defined pointer value. */ + + /** A typedef for pointers to messages. */ + typedef ReferenceCountedObjectPtr Ptr; + +private: + friend class MessageListener; + friend class MessageManager; + MessageListener* messageRecipient; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Message); +}; + +#endif // __JUCE_MESSAGE_JUCEHEADER__ +/*** End of inlined file: juce_Message.h ***/ + +/** + A message that calls a custom function when it gets delivered. + + You can use this class to fire off actions that you want to be performed later + on the message thread. + + Unlike other Message objects, these don't get sent to a MessageListener, you + just call the post() method to send them, and when they arrive, your + messageCallback() method will automatically be invoked. + + Always create an instance of a CallbackMessage on the heap, as it will be + deleted automatically after the message has been delivered. + + @see MessageListener, MessageManager, ActionListener, ChangeListener +*/ +class JUCE_API CallbackMessage : public Message +{ +public: + + CallbackMessage() throw(); + + /** Destructor. */ + ~CallbackMessage(); + + /** Called when the message is delivered. + + You should implement this method and make it do whatever action you want + to perform. + + Note that like all other messages, this object will be deleted immediately + after this method has been invoked. + */ + virtual void messageCallback() = 0; + + /** Instead of sending this message to a MessageListener, just call this method + to post it to the event queue. + + After you've called this, this object will belong to the MessageManager, + which will delete it later. So make sure you don't delete the object yourself, + call post() more than once, or call post() on a stack-based obect! + */ + void post(); + +private: + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CallbackMessage); +}; + +#endif // __JUCE_CALLBACKMESSAGE_JUCEHEADER__ +/*** End of inlined file: juce_CallbackMessage.h ***/ + /** Has a callback method that is triggered asynchronously. @@ -12515,6 +12670,10 @@ public: If called after triggerAsyncUpdate() and before the handleAsyncUpdate() callback happens, this will cancel the handleAsyncUpdate() callback. + + Note that this method simply cancels the next callback - if a callback is already + in progress on a different thread, this won't block until it finishes, so there's + no guarantee that the callback isn't still running when you return from */ void cancelPendingUpdate() throw(); @@ -12542,11 +12701,10 @@ public: private: - class AsyncUpdaterMessage; - friend class AsyncUpdaterMessage; - friend class ScopedPointer; - ScopedPointer message; - Atomic pendingMessage; + ReferenceCountedObjectPtr message; + Atomic& getDeliveryFlag() const throw(); + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AsyncUpdater); }; #endif // __JUCE_ASYNCUPDATER_JUCEHEADER__ @@ -28476,64 +28634,6 @@ struct JUCE_API ApplicationCommandInfo #ifndef __JUCE_MESSAGELISTENER_JUCEHEADER__ #define __JUCE_MESSAGELISTENER_JUCEHEADER__ - -/*** Start of inlined file: juce_Message.h ***/ -#ifndef __JUCE_MESSAGE_JUCEHEADER__ -#define __JUCE_MESSAGE_JUCEHEADER__ - -class MessageListener; -class MessageManager; - -/** The base class for objects that can be delivered to a MessageListener. - - The simplest Message object contains a few integer and pointer parameters - that the user can set, and this is enough for a lot of purposes. For passing more - complex data, subclasses of Message can also be used. - - @see MessageListener, MessageManager, ActionListener, ChangeListener -*/ -class JUCE_API Message -{ -public: - - /** Creates an uninitialised message. - - The class's variables will also be left uninitialised. - */ - Message() throw(); - - /** Creates a message object, filling in the member variables. - - The corresponding public member variables will be set from the parameters - passed in. - */ - Message (int intParameter1, - int intParameter2, - int intParameter3, - void* pointerParameter) throw(); - - /** Destructor. */ - virtual ~Message(); - - // These values can be used for carrying simple data that the application needs to - // pass around. For more complex messages, just create a subclass. - - int intParameter1; /**< user-defined integer value. */ - int intParameter2; /**< user-defined integer value. */ - int intParameter3; /**< user-defined integer value. */ - void* pointerParameter; /**< user-defined pointer value. */ - -private: - friend class MessageListener; - friend class MessageManager; - MessageListener* messageRecipient; - - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Message); -}; - -#endif // __JUCE_MESSAGE_JUCEHEADER__ -/*** End of inlined file: juce_Message.h ***/ - /** MessageListener subclasses can post and receive Message objects. @@ -30889,6 +30989,8 @@ public: A set of routines to convert buffers of 32-bit floating point data to and from various integer formats. + Note that these functions are deprecated - the AudioData class provides a much more + flexible set of conversion classes now. */ class JUCE_API AudioDataConverters { @@ -32681,7 +32783,7 @@ public: If you're going to generate a thumbnail yourself, call this before using addBlock() to add the data. */ - void reset (int numChannels, double sampleRate); + void reset (int numChannels, double sampleRate, int64 totalSamplesInSource = 0); /** Adds a block of level data to the thumbnail. Call reset() before using this, to tell the thumbnail about the data format. @@ -43777,76 +43879,6 @@ private: #endif #ifndef __JUCE_CALLBACKMESSAGE_JUCEHEADER__ -/*** Start of inlined file: juce_CallbackMessage.h ***/ -#ifndef __JUCE_CALLBACKMESSAGE_JUCEHEADER__ -#define __JUCE_CALLBACKMESSAGE_JUCEHEADER__ - -/** - A message that calls a custom function when it gets delivered. - - You can use this class to fire off actions that you want to be performed later - on the message thread. - - Unlike other Message objects, these don't get sent to a MessageListener, you - just call the post() method to send them, and when they arrive, your - messageCallback() method will automatically be invoked. - - Always create an instance of a CallbackMessage on the heap, as it will be - deleted automatically after the message has been delivered. - - @see MessageListener, MessageManager, ActionListener, ChangeListener -*/ -class JUCE_API CallbackMessage : public Message -{ -public: - - CallbackMessage() throw(); - - /** Destructor. */ - ~CallbackMessage(); - - /** Called when the message is delivered. - - You should implement this method and make it do whatever action you want - to perform. - - Note that like all other messages, this object will be deleted immediately - after this method has been invoked. - */ - virtual void messageCallback() = 0; - - /** Instead of sending this message to a MessageListener, just call this method - to post it to the event queue. - - After you've called this, this object will belong to the MessageManager, - which will delete it later. So make sure you don't delete the object yourself, - call post() more than once, or call post() on a stack-based obect! - */ - void post(); - - /** This can be used to indicate whether the MessageManager should delete the - message after it has been delivered. - By default, messages will be deleted, but you might want to disable this so that you - can re-use the same message. - */ - void setMessageIsDeletedOnDelivery (bool shouldBeDeleted) throw() { deleteOnDelivery = shouldBeDeleted; } - - /** Returns true if the message should be deleted after is has been delivered. - @see setMessageIsDeletedOnDelivery - */ - bool isMessageDeletedOnDelivery() const throw() { return deleteOnDelivery; } - -private: - - bool deleteOnDelivery; - - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CallbackMessage); -}; - -#endif // __JUCE_CALLBACKMESSAGE_JUCEHEADER__ -/*** End of inlined file: juce_CallbackMessage.h ***/ - - #endif #ifndef __JUCE_CHANGEBROADCASTER_JUCEHEADER__ @@ -44374,11 +44406,9 @@ public: bool lockWasGained() const throw() { return locked; } private: - class SharedEvents; class BlockingMessage; - friend class SharedEvents; - friend class BlockingMessage; - SharedEvents* sharedEvents; + friend class ReferenceCountedObjectPtr; + ReferenceCountedObjectPtr blockingMessage; bool locked; void init (Thread* thread, ThreadPoolJob* job); diff --git a/src/audio/audio_file_formats/juce_AudioFormatWriter.cpp b/src/audio/audio_file_formats/juce_AudioFormatWriter.cpp index fee2d57216..3b6ef47a48 100644 --- a/src/audio/audio_file_formats/juce_AudioFormatWriter.cpp +++ b/src/audio/audio_file_formats/juce_AudioFormatWriter.cpp @@ -264,7 +264,7 @@ public: void setThumbnail (AudioThumbnail* thumb) { if (thumb != 0) - thumb->reset (buffer.getNumChannels(), writer->getSampleRate()); + thumb->reset (buffer.getNumChannels(), writer->getSampleRate(), 0); const ScopedLock sl (thumbnailLock); thumbnailToUpdate = thumb; diff --git a/src/audio/audio_file_formats/juce_AudioThumbnail.cpp b/src/audio/audio_file_formats/juce_AudioThumbnail.cpp index 6176ba72d8..c4d9c606c3 100644 --- a/src/audio/audio_file_formats/juce_AudioThumbnail.cpp +++ b/src/audio/audio_file_formats/juce_AudioThumbnail.cpp @@ -553,14 +553,15 @@ void AudioThumbnail::clear() sendChangeMessage(); } -void AudioThumbnail::reset (int newNumChannels, double newSampleRate) +void AudioThumbnail::reset (int newNumChannels, double newSampleRate, int64 totalSamplesInSource) { clear(); numChannels = newNumChannels; sampleRate = newSampleRate; + totalSamples = totalSamplesInSource; - createChannels (0); + createChannels (1 + (int) (totalSamplesInSource / samplesPerThumbSample)); } void AudioThumbnail::createChannels (const int length) diff --git a/src/audio/audio_file_formats/juce_AudioThumbnail.h b/src/audio/audio_file_formats/juce_AudioThumbnail.h index 95fe52c203..a3b22fc4bf 100644 --- a/src/audio/audio_file_formats/juce_AudioThumbnail.h +++ b/src/audio/audio_file_formats/juce_AudioThumbnail.h @@ -113,7 +113,7 @@ public: If you're going to generate a thumbnail yourself, call this before using addBlock() to add the data. */ - void reset (int numChannels, double sampleRate); + void reset (int numChannels, double sampleRate, int64 totalSamplesInSource = 0); /** Adds a block of level data to the thumbnail. Call reset() before using this, to tell the thumbnail about the data format. diff --git a/src/audio/dsp/juce_AudioDataConverters.h b/src/audio/dsp/juce_AudioDataConverters.h index 641652d80e..dd70179000 100644 --- a/src/audio/dsp/juce_AudioDataConverters.h +++ b/src/audio/dsp/juce_AudioDataConverters.h @@ -581,6 +581,8 @@ public: A set of routines to convert buffers of 32-bit floating point data to and from various integer formats. + Note that these functions are deprecated - the AudioData class provides a much more + flexible set of conversion classes now. */ class JUCE_API AudioDataConverters { diff --git a/src/containers/juce_ReferenceCountedArray.h b/src/containers/juce_ReferenceCountedArray.h index bbf1df12b6..51a1c91bc3 100644 --- a/src/containers/juce_ReferenceCountedArray.h +++ b/src/containers/juce_ReferenceCountedArray.h @@ -49,6 +49,8 @@ template ObjectClassPtr; + //============================================================================== /** Creates an empty array. @see ReferenceCountedObject, Array, OwnedArray @@ -126,7 +128,7 @@ public: @see getUnchecked */ - inline const ReferenceCountedObjectPtr operator[] (const int index) const throw() + inline const ObjectClassPtr operator[] (const int index) const throw() { const ScopedLockType lock (getLock()); return isPositiveAndBelow (index, numUsed) ? data.elements [index] @@ -138,7 +140,7 @@ public: This is a faster and less safe version of operator[] which doesn't check the index passed in, so it can be used when you're sure the index if always going to be legal. */ - inline const ReferenceCountedObjectPtr getUnchecked (const int index) const throw() + inline const ObjectClassPtr getUnchecked (const int index) const throw() { const ScopedLockType lock (getLock()); jassert (isPositiveAndBelow (index, numUsed)); @@ -150,7 +152,7 @@ public: This will return a null pointer if the array's empty. @see getLast */ - inline const ReferenceCountedObjectPtr getFirst() const throw() + inline const ObjectClassPtr getFirst() const throw() { const ScopedLockType lock (getLock()); return numUsed > 0 ? data.elements [0] @@ -162,7 +164,7 @@ public: This will return a null pointer if the array's empty. @see getFirst */ - inline const ReferenceCountedObjectPtr getLast() const throw() + inline const ObjectClassPtr getLast() const throw() { const ScopedLockType lock (getLock()); return numUsed > 0 ? data.elements [numUsed - 1] @@ -436,6 +438,43 @@ public: } } + /** Removes and returns an object from the array. + + This will remove the object at a given index and return it, moving back all + the subsequent objects to close the gap. If the index passed in is out-of-range, + nothing will happen and a null pointer will be returned. + + @param indexToRemove the index of the element to remove + @see remove, removeObject, removeRange + */ + const ObjectClassPtr removeAndReturn (const int indexToRemove) + { + ObjectClassPtr removedItem; + const ScopedLockType lock (getLock()); + + if (isPositiveAndBelow (indexToRemove, numUsed)) + { + ObjectClass** const e = data.elements + indexToRemove; + + if (*e != 0) + { + removedItem = *e; + (*e)->decReferenceCount(); + } + + --numUsed; + const int numberToShift = numUsed - indexToRemove; + + if (numberToShift > 0) + memmove (e, e + 1, numberToShift * sizeof (ObjectClass*)); + + if ((numUsed << 1) < data.numAllocated) + minimiseStorageOverheads(); + } + + return removedItem; + } + /** Removes the first occurrence of a specified object from the array. If the item isn't found, no action is taken. If it is found, it is diff --git a/src/core/juce_StandardHeader.h b/src/core/juce_StandardHeader.h index 4ab825c2cc..7ef24f3518 100644 --- a/src/core/juce_StandardHeader.h +++ b/src/core/juce_StandardHeader.h @@ -33,7 +33,7 @@ */ #define JUCE_MAJOR_VERSION 1 #define JUCE_MINOR_VERSION 52 -#define JUCE_BUILDNUMBER 106 +#define JUCE_BUILDNUMBER 107 /** Current Juce version number. diff --git a/src/events/juce_AsyncUpdater.cpp b/src/events/juce_AsyncUpdater.cpp index 0176b7a96a..9d695075b4 100644 --- a/src/events/juce_AsyncUpdater.cpp +++ b/src/events/juce_AsyncUpdater.cpp @@ -33,29 +33,35 @@ BEGIN_JUCE_NAMESPACE //============================================================================== -class AsyncUpdater::AsyncUpdaterMessage : public CallbackMessage +class AsyncUpdaterMessage : public CallbackMessage { public: AsyncUpdaterMessage (AsyncUpdater& owner_) : owner (owner_) { - setMessageIsDeletedOnDelivery (false); } void messageCallback() { - if (owner.pendingMessage.compareAndSetBool (0, this)) + if (shouldDeliver.compareAndSetBool (0, 1)) owner.handleAsyncUpdate(); } + Atomic shouldDeliver; + private: AsyncUpdater& owner; }; //============================================================================== AsyncUpdater::AsyncUpdater() - : message (new AsyncUpdaterMessage (*this)) { + message = new AsyncUpdaterMessage (*this); +} + +inline Atomic& AsyncUpdater::getDeliveryFlag() const throw() +{ + return static_cast (message.getObject())->shouldDeliver; } AsyncUpdater::~AsyncUpdater() @@ -66,19 +72,18 @@ AsyncUpdater::~AsyncUpdater() // deleting this object, or find some other way to avoid such a race condition. jassert ((! isUpdatePending()) || MessageManager::getInstance()->currentThreadHasLockedMessageManager()); - if (pendingMessage.exchange (0) != 0) - message.release()->setMessageIsDeletedOnDelivery (true); + getDeliveryFlag().set (0); } void AsyncUpdater::triggerAsyncUpdate() { - if (pendingMessage.compareAndSetBool (message, 0)) + if (getDeliveryFlag().compareAndSetBool (1, 0)) message->post(); } void AsyncUpdater::cancelPendingUpdate() throw() { - pendingMessage = 0; + getDeliveryFlag().set (0); } void AsyncUpdater::handleUpdateNowIfNeeded() @@ -86,13 +91,13 @@ void AsyncUpdater::handleUpdateNowIfNeeded() // This can only be called by the event thread. jassert (MessageManager::getInstance()->currentThreadHasLockedMessageManager()); - if (pendingMessage.exchange (0) != 0) + if (getDeliveryFlag().exchange (0) != 0) handleAsyncUpdate(); } bool AsyncUpdater::isUpdatePending() const throw() { - return pendingMessage.value != 0; + return getDeliveryFlag().value != 0; } diff --git a/src/events/juce_AsyncUpdater.h b/src/events/juce_AsyncUpdater.h index 54af442083..3fe84b77bf 100644 --- a/src/events/juce_AsyncUpdater.h +++ b/src/events/juce_AsyncUpdater.h @@ -27,7 +27,7 @@ #define __JUCE_ASYNCUPDATER_JUCEHEADER__ #include "../core/juce_Atomic.h" -#include "../containers/juce_ScopedPointer.h" +#include "../events/juce_CallbackMessage.h" //============================================================================== @@ -71,6 +71,10 @@ public: If called after triggerAsyncUpdate() and before the handleAsyncUpdate() callback happens, this will cancel the handleAsyncUpdate() callback. + + Note that this method simply cancels the next callback - if a callback is already + in progress on a different thread, this won't block until it finishes, so there's + no guarantee that the callback isn't still running when you return from */ void cancelPendingUpdate() throw(); @@ -97,14 +101,12 @@ public: */ virtual void handleAsyncUpdate() = 0; - private: //============================================================================== - class AsyncUpdaterMessage; - friend class AsyncUpdaterMessage; - friend class ScopedPointer; - ScopedPointer message; - Atomic pendingMessage; + ReferenceCountedObjectPtr message; + Atomic& getDeliveryFlag() const throw(); + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AsyncUpdater); }; diff --git a/src/events/juce_CallbackMessage.h b/src/events/juce_CallbackMessage.h index fb0d4bc422..2b6be7da44 100644 --- a/src/events/juce_CallbackMessage.h +++ b/src/events/juce_CallbackMessage.h @@ -28,6 +28,7 @@ #include "juce_Message.h" + //============================================================================== /** A message that calls a custom function when it gets delivered. @@ -73,22 +74,8 @@ public: */ void post(); - /** This can be used to indicate whether the MessageManager should delete the - message after it has been delivered. - By default, messages will be deleted, but you might want to disable this so that you - can re-use the same message. - */ - void setMessageIsDeletedOnDelivery (bool shouldBeDeleted) throw() { deleteOnDelivery = shouldBeDeleted; } - - /** Returns true if the message should be deleted after is has been delivered. - @see setMessageIsDeletedOnDelivery - */ - bool isMessageDeletedOnDelivery() const throw() { return deleteOnDelivery; } - private: //============================================================================== - bool deleteOnDelivery; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CallbackMessage); }; diff --git a/src/events/juce_Message.h b/src/events/juce_Message.h index 0228133bca..ef869a26c0 100644 --- a/src/events/juce_Message.h +++ b/src/events/juce_Message.h @@ -26,9 +26,11 @@ #ifndef __JUCE_MESSAGE_JUCEHEADER__ #define __JUCE_MESSAGE_JUCEHEADER__ +#include "../containers/juce_ReferenceCountedObject.h" class MessageListener; class MessageManager; + //============================================================================== /** The base class for objects that can be delivered to a MessageListener. @@ -38,7 +40,7 @@ class MessageManager; @see MessageListener, MessageManager, ActionListener, ChangeListener */ -class JUCE_API Message +class JUCE_API Message : public ReferenceCountedObject { public: //============================================================================== @@ -70,6 +72,8 @@ public: int intParameter3; /**< user-defined integer value. */ void* pointerParameter; /**< user-defined pointer value. */ + /** A typedef for pointers to messages. */ + typedef ReferenceCountedObjectPtr Ptr; //============================================================================== private: diff --git a/src/events/juce_MessageManager.cpp b/src/events/juce_MessageManager.cpp index 1614cd9206..9f2798806f 100644 --- a/src/events/juce_MessageManager.cpp +++ b/src/events/juce_MessageManager.cpp @@ -84,11 +84,11 @@ MessageManager* MessageManager::getInstance() throw() void MessageManager::postMessageToQueue (Message* const message) { if (quitMessagePosted || ! juce_postMessageToSystemQueue (message)) - delete message; + Message::Ptr deleter (message); // (this will delete messages that were just created with a 0 ref count) } //============================================================================== -CallbackMessage::CallbackMessage() throw() : deleteOnDelivery (true) {} +CallbackMessage::CallbackMessage() throw() {} CallbackMessage::~CallbackMessage() {} void CallbackMessage::post() @@ -103,7 +103,6 @@ void MessageManager::deliverMessage (Message* const message) { JUCE_TRY { - ScopedPointer messageDeleter (message); MessageListener* const recipient = message->messageRecipient; if (recipient == 0) @@ -113,9 +112,6 @@ void MessageManager::deliverMessage (Message* const message) if (callbackMessage != 0) { callbackMessage->messageCallback(); - - if (! callbackMessage->isMessageDeletedOnDelivery()) - messageDeleter.release(); } else if (message->intParameter1 == quitMessageId) { @@ -201,9 +197,11 @@ bool MessageManager::isThisTheMessageThread() const throw() void MessageManager::setCurrentThreadAsMessageThread() { - if (messageThreadId != Thread::getCurrentThreadId()) + const Thread::ThreadID thisThread = Thread::getCurrentThreadId(); + + if (messageThreadId != thisThread) { - messageThreadId = Thread::getCurrentThreadId(); + messageThreadId = thisThread; // This is needed on windows to make sure the message window is created by this thread doPlatformSpecificShutdown(); @@ -228,48 +226,32 @@ bool MessageManager::currentThreadHasLockedMessageManager() const throw() accessed from another thread inside a MM lock, you're screwed. (this is exactly what happens in Cocoa). */ -class MessageManagerLock::SharedEvents : public ReferenceCountedObject -{ -public: - SharedEvents() {} - - /* This class just holds a couple of events to communicate between the BlockingMessage - and the MessageManagerLock. Because both of these objects may be deleted at any time, - this shared data must be kept in a separate, ref-counted container. */ - WaitableEvent lockedEvent, releaseEvent; - -private: - JUCE_DECLARE_NON_COPYABLE (SharedEvents); -}; - class MessageManagerLock::BlockingMessage : public CallbackMessage { public: - BlockingMessage (MessageManagerLock::SharedEvents* const events_) : events (events_) {} + BlockingMessage() {} void messageCallback() { - events->lockedEvent.signal(); - events->releaseEvent.wait(); + lockedEvent.signal(); + releaseEvent.wait(); } -private: - ReferenceCountedObjectPtr events; + WaitableEvent lockedEvent, releaseEvent; +private: JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BlockingMessage); }; //============================================================================== MessageManagerLock::MessageManagerLock (Thread* const threadToCheck) - : sharedEvents (0), - locked (false) + : locked (false) { init (threadToCheck, 0); } MessageManagerLock::MessageManagerLock (ThreadPoolJob* const jobToCheckForExitSignal) - : sharedEvents (0), - locked (false) + : locked (false) { init (0, jobToCheckForExitSignal); } @@ -300,19 +282,16 @@ void MessageManagerLock::init (Thread* const threadToCheck, ThreadPoolJob* const } } - sharedEvents = new SharedEvents(); - sharedEvents->incReferenceCount(); - - (new BlockingMessage (sharedEvents))->post(); + blockingMessage = new BlockingMessage(); + blockingMessage->post(); - while (! sharedEvents->lockedEvent.wait (50)) + while (! blockingMessage->lockedEvent.wait (20)) { if ((threadToCheck != 0 && threadToCheck->threadShouldExit()) || (job != 0 && job->shouldExit())) { - sharedEvents->releaseEvent.signal(); - sharedEvents->decReferenceCount(); - sharedEvents = 0; + blockingMessage->releaseEvent.signal(); + blockingMessage = 0; MessageManager::instance->lockingLock.exit(); return; } @@ -328,12 +307,12 @@ void MessageManagerLock::init (Thread* const threadToCheck, ThreadPoolJob* const MessageManagerLock::~MessageManagerLock() throw() { - if (sharedEvents != 0) + if (blockingMessage != 0) { jassert (MessageManager::instance == 0 || MessageManager::instance->currentThreadHasLockedMessageManager()); - sharedEvents->releaseEvent.signal(); - sharedEvents->decReferenceCount(); + blockingMessage->releaseEvent.signal(); + blockingMessage = 0; if (MessageManager::instance != 0) { diff --git a/src/events/juce_MessageManager.h b/src/events/juce_MessageManager.h index 5bd48498a7..20d10ff582 100644 --- a/src/events/juce_MessageManager.h +++ b/src/events/juce_MessageManager.h @@ -299,11 +299,9 @@ public: private: - class SharedEvents; class BlockingMessage; - friend class SharedEvents; - friend class BlockingMessage; - SharedEvents* sharedEvents; + friend class ReferenceCountedObjectPtr; + ReferenceCountedObjectPtr blockingMessage; bool locked; void init (Thread* thread, ThreadPoolJob* job); diff --git a/src/events/juce_Timer.cpp b/src/events/juce_Timer.cpp index 72fec44435..1de0458fa8 100644 --- a/src/events/juce_Timer.cpp +++ b/src/events/juce_Timer.cpp @@ -64,6 +64,7 @@ public: void run() { uint32 lastTime = Time::getMillisecondCounter(); + Message::Ptr message (new Message()); while (! threadShouldExit()) { @@ -89,7 +90,7 @@ public: */ if (callbackNeeded.compareAndSetBool (1, 0)) { - postMessage (new Message()); + postMessage (message); /* Sometimes our message can get discarded by the OS (e.g. when running as an RTAS when the app has a modal loop), so this is how long to wait before assuming the diff --git a/src/gui/components/juce_Component.cpp b/src/gui/components/juce_Component.cpp index 7dbf09d0e3..389d2a7e18 100644 --- a/src/gui/components/juce_Component.cpp +++ b/src/gui/components/juce_Component.cpp @@ -1536,6 +1536,10 @@ void Component::exitModalState (const int returnValue) } else { + // if component methods are being called from threads other than the message + // thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe. + CHECK_MESSAGE_MANAGER_IS_LOCKED + class ExitModalStateMessage : public CallbackMessage { public: @@ -2111,6 +2115,10 @@ void Component::parentSizeChanged() void Component::addComponentListener (ComponentListener* const newListener) { + // if component methods are being called from threads other than the message + // thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe. + CHECK_MESSAGE_MANAGER_IS_LOCKED + componentListeners.add (newListener); } @@ -2156,6 +2164,10 @@ void Component::paintOverChildren (Graphics&) //============================================================================== void Component::postCommandMessage (const int commandId) { + // if component methods are being called from threads other than the message + // thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe. + CHECK_MESSAGE_MANAGER_IS_LOCKED + class CustomCommandMessage : public CallbackMessage { public: diff --git a/src/native/juce_mac_NativeCode.mm b/src/native/juce_mac_NativeCode.mm index 6271331df6..ba30921b7e 100644 --- a/src/native/juce_mac_NativeCode.mm +++ b/src/native/juce_mac_NativeCode.mm @@ -63,6 +63,7 @@ BEGIN_JUCE_NAMESPACE #include "../application/juce_Application.h" #include "../utilities/juce_SystemClipboard.h" #include "../events/juce_MessageManager.h" +#include "../containers/juce_ReferenceCountedArray.h" #include "../gui/graphics/contexts/juce_LowLevelGraphicsSoftwareRenderer.h" #include "../gui/graphics/imaging/juce_ImageFileFormat.h" #include "../gui/graphics/imaging/juce_CameraDevice.h" @@ -112,6 +113,74 @@ namespace } } +//============================================================================== +class MessageQueue +{ +public: + MessageQueue() + { + #if MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_4 && ! JUCE_IOS + runLoop = CFRunLoopGetMain(); + #else + runLoop = CFRunLoopGetCurrent(); + #endif + + CFRunLoopSourceContext sourceContext; + zerostruct (sourceContext); + sourceContext.info = this; + sourceContext.perform = runLoopSourceCallback; + runLoopSource = CFRunLoopSourceCreate (kCFAllocatorDefault, 1, &sourceContext); + CFRunLoopAddSource (runLoop, runLoopSource, kCFRunLoopCommonModes); + } + + ~MessageQueue() + { + CFRunLoopRemoveSource (runLoop, runLoopSource, kCFRunLoopCommonModes); + CFRunLoopSourceInvalidate (runLoopSource); + CFRelease (runLoopSource); + } + + void post (Message* const message) + { + messages.add (message); + CFRunLoopSourceSignal (runLoopSource); + CFRunLoopWakeUp (runLoop); + } + +private: + ReferenceCountedArray messages; + CriticalSection lock; + CFRunLoopRef runLoop; + CFRunLoopSourceRef runLoopSource; + + bool deliverNextMessage() + { + const Message::Ptr nextMessage (messages.removeAndReturn (0)); + + if (nextMessage == 0) + return false; + + const ScopedAutoReleasePool pool; + MessageManager::getInstance()->deliverMessage (nextMessage); + return true; + } + + void runLoopCallback() + { + for (int i = 4; --i >= 0;) + if (! deliverNextMessage()) + return; + + CFRunLoopSourceSignal (runLoopSource); + CFRunLoopWakeUp (runLoop); + } + + static void runLoopSourceCallback (void* info) + { + static_cast (info)->runLoopCallback(); + } +}; + //============================================================================== #define JUCE_INCLUDED_FILE 1 diff --git a/src/native/linux/juce_linux_Messaging.cpp b/src/native/linux/juce_linux_Messaging.cpp index 34c85f40be..92cc584935 100644 --- a/src/native/linux/juce_linux_Messaging.cpp +++ b/src/native/linux/juce_linux_Messaging.cpp @@ -153,7 +153,7 @@ public: private: CriticalSection lock; - OwnedArray queue; + ReferenceCountedArray queue; int fd[2]; int bytesInSocket; int totalEventCount; @@ -193,15 +193,15 @@ private: return true; } - Message* popNextMessage() + const Message::Ptr popNextMessage() { - ScopedLock sl (lock); + const ScopedLock sl (lock); if (bytesInSocket > 0) { --bytesInSocket; - ScopedUnlock ul (lock); + const ScopedUnlock ul (lock); unsigned char x; size_t numBytes = read (fd[1], &x, 1); (void) numBytes; @@ -212,7 +212,7 @@ private: bool dispatchNextInternalMessage() { - ScopedPointer msg (popNextMessage()); + const Message::Ptr msg (popNextMessage()); if (msg == 0) return false; @@ -228,7 +228,7 @@ private: else { // Handle "normal" messages - MessageManager::getInstance()->deliverMessage (msg.release()); + MessageManager::getInstance()->deliverMessage (msg); } return true; diff --git a/src/native/mac/juce_iphone_MessageManager.mm b/src/native/mac/juce_iphone_MessageManager.mm index 9424d99894..6bfbb9484f 100644 --- a/src/native/mac/juce_iphone_MessageManager.mm +++ b/src/native/mac/juce_iphone_MessageManager.mm @@ -107,79 +107,41 @@ bool MessageManager::runDispatchLoopUntil (int millisecondsToRunFor) } //============================================================================== -namespace iOSMessageLoopHelpers +struct MessageDispatchSystem { - static CFRunLoopRef runLoop = 0; - static CFRunLoopSourceRef runLoopSource = 0; - static OwnedArray * pendingMessages = 0; - static JuceCustomMessageHandler* juceCustomMessageHandler = 0; - - void runLoopSourceCallback (void*) + MessageDispatchSystem() + : juceCustomMessageHandler (0) { - if (pendingMessages != 0) - { - int numDispatched = 0; - - do - { - Message* const nextMessage = pendingMessages->removeAndReturn (0); - - if (nextMessage == 0) - return; + juceCustomMessageHandler = [[JuceCustomMessageHandler alloc] init]; + } - const ScopedAutoReleasePool pool; - MessageManager::getInstance()->deliverMessage (nextMessage); + ~MessageDispatchSystem() + { + [[NSRunLoop currentRunLoop] cancelPerformSelectorsWithTarget: juceCustomMessageHandler]; + [juceCustomMessageHandler release]; + } - } while (++numDispatched <= 4); + JuceCustomMessageHandler* juceCustomMessageHandler; + MessageQueue messageQueue; +}; - CFRunLoopSourceSignal (runLoopSource); - CFRunLoopWakeUp (runLoop); - } - } -} +static MessageDispatchSystem* dispatcher = 0; void MessageManager::doPlatformSpecificInitialisation() { - using namespace iOSMessageLoopHelpers; - pendingMessages = new OwnedArray (); - - runLoop = CFRunLoopGetCurrent(); - CFRunLoopSourceContext sourceContext; - zerostruct (sourceContext); - sourceContext.perform = runLoopSourceCallback; - runLoopSource = CFRunLoopSourceCreate (kCFAllocatorDefault, 1, &sourceContext); - CFRunLoopAddSource (runLoop, runLoopSource, kCFRunLoopCommonModes); - - if (juceCustomMessageHandler == 0) - juceCustomMessageHandler = [[JuceCustomMessageHandler alloc] init]; + if (dispatcher == 0) + dispatcher = new MessageDispatchSystem(); } void MessageManager::doPlatformSpecificShutdown() { - using namespace iOSMessageLoopHelpers; - CFRunLoopSourceInvalidate (runLoopSource); - CFRelease (runLoopSource); - runLoopSource = 0; - deleteAndZero (pendingMessages); - - if (juceCustomMessageHandler != 0) - { - [[NSRunLoop currentRunLoop] cancelPerformSelectorsWithTarget: juceCustomMessageHandler]; - [juceCustomMessageHandler release]; - juceCustomMessageHandler = 0; - } + deleteAndZero (dispatcher); } bool juce_postMessageToSystemQueue (Message* message) { - using namespace iOSMessageLoopHelpers; - - if (pendingMessages != 0) - { - pendingMessages->add (message); - CFRunLoopSourceSignal (runLoopSource); - CFRunLoopWakeUp (runLoop); - } + if (dispatcher != 0) + dispatcher->messageQueue.post (message); return true; } @@ -190,14 +152,14 @@ void MessageManager::broadcastMessage (const String& value) void* MessageManager::callFunctionOnMessageThread (MessageCallbackFunction* callback, void* data) { - using namespace iOSMessageLoopHelpers; - if (isThisTheMessageThread()) { return (*callback) (data); } else { + jassert (dispatcher != 0); // trying to call this when the juce system isn't initialised.. + // If a thread has a MessageManagerLock and then tries to call this method, it'll // deadlock because the message manager is blocked from running, so can never // call your function.. @@ -211,11 +173,11 @@ void* MessageManager::callFunctionOnMessageThread (MessageCallbackFunction* call cmp.result = 0; cmp.hasBeenExecuted = false; - [juceCustomMessageHandler performSelectorOnMainThread: @selector (performCallback:) - withObject: [NSData dataWithBytesNoCopy: &cmp - length: sizeof (cmp) - freeWhenDone: NO] - waitUntilDone: YES]; + [dispatcher->juceCustomMessageHandler performSelectorOnMainThread: @selector (performCallback:) + withObject: [NSData dataWithBytesNoCopy: &cmp + length: sizeof (cmp) + freeWhenDone: NO] + waitUntilDone: YES]; return cmp.result; } diff --git a/src/native/mac/juce_mac_Files.mm b/src/native/mac/juce_mac_Files.mm index 2ad3682ea3..7976d0d1a9 100644 --- a/src/native/mac/juce_mac_Files.mm +++ b/src/native/mac/juce_mac_Files.mm @@ -182,12 +182,12 @@ const File File::getSpecialLocation (const SpecialLocationType type) case userHomeDirectory: resultPath = nsStringToJuce (NSHomeDirectory()); break; #if JUCE_IOS - case userDocumentsDirectory: resultPath = getIOSSystemLocation (NSDocumentDirectory); break; - case userDesktopDirectory: resultPath = getIOSSystemLocation (NSDesktopDirectory); break; + case userDocumentsDirectory: resultPath = FileHelpers::getIOSSystemLocation (NSDocumentDirectory); break; + case userDesktopDirectory: resultPath = FileHelpers::getIOSSystemLocation (NSDesktopDirectory); break; case tempDirectory: { - File tmp (getIOSSystemLocation (NSCachesDirectory)); + File tmp (FileHelpers::getIOSSystemLocation (NSCachesDirectory)); tmp = tmp.getChildFile (juce_getExecutableFile().getFileNameWithoutExtension()); tmp.createDirectory(); return tmp.getFullPathName(); diff --git a/src/native/mac/juce_mac_MessageManager.mm b/src/native/mac/juce_mac_MessageManager.mm index a3654e9b89..3ca36b8165 100644 --- a/src/native/mac/juce_mac_MessageManager.mm +++ b/src/native/mac/juce_mac_MessageManager.mm @@ -44,24 +44,10 @@ class AppDelegateRedirector public: AppDelegateRedirector() { -#if MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_4 - runLoop = CFRunLoopGetMain(); -#else - runLoop = CFRunLoopGetCurrent(); -#endif - CFRunLoopSourceContext sourceContext; - zerostruct (sourceContext); - sourceContext.info = this; - sourceContext.perform = runLoopSourceCallback; - runLoopSource = CFRunLoopSourceCreate (kCFAllocatorDefault, 1, &sourceContext); - CFRunLoopAddSource (runLoop, runLoopSource, kCFRunLoopCommonModes); } virtual ~AppDelegateRedirector() { - CFRunLoopRemoveSource (runLoop, runLoopSource, kCFRunLoopCommonModes); - CFRunLoopSourceInvalidate (runLoopSource); - CFRelease (runLoopSource); } virtual NSApplicationTerminateReply shouldTerminate() @@ -137,40 +123,13 @@ public: void postMessage (Message* const m) { - messages.add (m); - CFRunLoopSourceSignal (runLoopSource); - CFRunLoopWakeUp (runLoop); + messageQueue.post (m); } private: CFRunLoopRef runLoop; CFRunLoopSourceRef runLoopSource; - OwnedArray messages; - - void runLoopCallback() - { - int numDispatched = 0; - - do - { - Message* const nextMessage = messages.removeAndReturn (0); - - if (nextMessage == 0) - return; - - const ScopedAutoReleasePool pool; - MessageManager::getInstance()->deliverMessage (nextMessage); - - } while (++numDispatched <= 4); - - CFRunLoopSourceSignal (runLoopSource); - CFRunLoopWakeUp (runLoop); - } - - static void runLoopSourceCallback (void* info) - { - static_cast (info)->runLoopCallback(); - } + MessageQueue messageQueue; }; diff --git a/src/native/windows/juce_win32_Messaging.cpp b/src/native/windows/juce_win32_Messaging.cpp index e70594a269..964b8c7a69 100644 --- a/src/native/windows/juce_win32_Messaging.cpp +++ b/src/native/windows/juce_win32_Messaging.cpp @@ -65,7 +65,9 @@ static LRESULT CALLBACK juce_MessageWndProc (HWND h, // here in case there are windows modal dialog boxes doing their own // dispatch loop and not calling our version - MessageManager::getInstance()->deliverMessage ((Message*) lParam); + Message* const message = reinterpret_cast (lParam); + MessageManager::getInstance()->deliverMessage (message); + message->decReferenceCount(); return 0; } else if (message == broadcastId) @@ -153,7 +155,9 @@ bool juce_dispatchNextMessageOnSystemQueue (const bool returnIfNoPendingMessages { if (m.message == specialId && m.hwnd == juce_messageWindowHandle) { - MessageManager::getInstance()->deliverMessage ((Message*) (void*) m.lParam); + Message* const message = reinterpret_cast (m.lParam); + MessageManager::getInstance()->deliverMessage (message); + message->decReferenceCount(); } else if (m.message == WM_QUIT) { @@ -184,6 +188,7 @@ bool juce_dispatchNextMessageOnSystemQueue (const bool returnIfNoPendingMessages //============================================================================== bool juce_postMessageToSystemQueue (Message* message) { + message->incReferenceCount(); return PostMessage (juce_messageWindowHandle, specialId, 0, (LPARAM) message) != 0; }