Browse Source

Changed the Message class to be reference-counted, and used this to tighten up some messaging code. Minor tweaks to AudioThumbnail, ReferenceCountedArray.

tags/2021-05-28
Julian Storer 15 years ago
parent
commit
d60f661789
22 changed files with 569 additions and 523 deletions
  1. +165
    -174
      juce_amalgamated.cpp
  2. +173
    -143
      juce_amalgamated.h
  3. +1
    -1
      src/audio/audio_file_formats/juce_AudioFormatWriter.cpp
  4. +3
    -2
      src/audio/audio_file_formats/juce_AudioThumbnail.cpp
  5. +1
    -1
      src/audio/audio_file_formats/juce_AudioThumbnail.h
  6. +2
    -0
      src/audio/dsp/juce_AudioDataConverters.h
  7. +43
    -4
      src/containers/juce_ReferenceCountedArray.h
  8. +1
    -1
      src/core/juce_StandardHeader.h
  9. +15
    -10
      src/events/juce_AsyncUpdater.cpp
  10. +9
    -7
      src/events/juce_AsyncUpdater.h
  11. +1
    -14
      src/events/juce_CallbackMessage.h
  12. +5
    -1
      src/events/juce_Message.h
  13. +21
    -42
      src/events/juce_MessageManager.cpp
  14. +2
    -4
      src/events/juce_MessageManager.h
  15. +2
    -1
      src/events/juce_Timer.cpp
  16. +12
    -0
      src/gui/components/juce_Component.cpp
  17. +69
    -0
      src/native/juce_mac_NativeCode.mm
  18. +6
    -6
      src/native/linux/juce_linux_Messaging.cpp
  19. +26
    -64
      src/native/mac/juce_iphone_MessageManager.mm
  20. +3
    -3
      src/native/mac/juce_mac_Files.mm
  21. +2
    -43
      src/native/mac/juce_mac_MessageManager.mm
  22. +7
    -2
      src/native/windows/juce_win32_Messaging.cpp

+ 165
- 174
juce_amalgamated.cpp View File

@@ -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<int> shouldDeliver;

private:
AsyncUpdater& owner;
};

AsyncUpdater::AsyncUpdater()
: message (new AsyncUpdaterMessage (*this))
{
message = new AsyncUpdaterMessage (*this);
}

inline Atomic<int>& AsyncUpdater::getDeliveryFlag() const throw()
{
return static_cast <AsyncUpdaterMessage*> (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 <Message> 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 <MessageManagerLock::SharedEvents> 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 <Message*> (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 <Message*> (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 <Message> queue;
ReferenceCountedArray <Message> 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 <Message> 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 <Message, CriticalSection> 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 <MessageQueue*> (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 <Message, CriticalSection>* 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 <Message, CriticalSection>();

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 <Message, CriticalSection> 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 <AppDelegateRedirector*> (info)->runLoopCallback();
}
MessageQueue messageQueue;
};

END_JUCE_NAMESPACE


+ 173
- 143
juce_amalgamated.h View File

@@ -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 <class ObjectClass, class TypeOfCriticalSectionToUse = DummyCriticalSec
class ReferenceCountedArray
{
public:
typedef ReferenceCountedObjectPtr<ObjectClass> ObjectClassPtr;

/** Creates an empty array.
@see ReferenceCountedObject, Array, OwnedArray
@@ -11101,7 +11102,7 @@ public:

@see getUnchecked
*/
inline const ReferenceCountedObjectPtr<ObjectClass> 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<ObjectClass> 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<ObjectClass> 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<ObjectClass> 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 <Message> 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<AsyncUpdaterMessage>;
ScopedPointer<AsyncUpdaterMessage> message;
Atomic<AsyncUpdaterMessage*> pendingMessage;
ReferenceCountedObjectPtr<CallbackMessage> message;
Atomic<int>& 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<BlockingMessage>;
ReferenceCountedObjectPtr<BlockingMessage> blockingMessage;
bool locked;

void init (Thread* thread, ThreadPoolJob* job);


+ 1
- 1
src/audio/audio_file_formats/juce_AudioFormatWriter.cpp View File

@@ -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;


+ 3
- 2
src/audio/audio_file_formats/juce_AudioThumbnail.cpp View File

@@ -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)


+ 1
- 1
src/audio/audio_file_formats/juce_AudioThumbnail.h View File

@@ -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.


+ 2
- 0
src/audio/dsp/juce_AudioDataConverters.h View File

@@ -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
{


+ 43
- 4
src/containers/juce_ReferenceCountedArray.h View File

@@ -49,6 +49,8 @@ template <class ObjectClass, class TypeOfCriticalSectionToUse = DummyCriticalSec
class ReferenceCountedArray
{
public:
typedef ReferenceCountedObjectPtr<ObjectClass> ObjectClassPtr;
//==============================================================================
/** Creates an empty array.
@see ReferenceCountedObject, Array, OwnedArray
@@ -126,7 +128,7 @@ public:
@see getUnchecked
*/
inline const ReferenceCountedObjectPtr<ObjectClass> 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<ObjectClass> 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<ObjectClass> 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<ObjectClass> 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


+ 1
- 1
src/core/juce_StandardHeader.h View File

@@ -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.


+ 15
- 10
src/events/juce_AsyncUpdater.cpp View File

@@ -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<int> shouldDeliver;
private:
AsyncUpdater& owner;
};
//==============================================================================
AsyncUpdater::AsyncUpdater()
: message (new AsyncUpdaterMessage (*this))
{
message = new AsyncUpdaterMessage (*this);
}
inline Atomic<int>& AsyncUpdater::getDeliveryFlag() const throw()
{
return static_cast <AsyncUpdaterMessage*> (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;
}


+ 9
- 7
src/events/juce_AsyncUpdater.h View File

@@ -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<AsyncUpdaterMessage>;
ScopedPointer<AsyncUpdaterMessage> message;
Atomic<AsyncUpdaterMessage*> pendingMessage;
ReferenceCountedObjectPtr<CallbackMessage> message;
Atomic<int>& getDeliveryFlag() const throw();
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AsyncUpdater);
};


+ 1
- 14
src/events/juce_CallbackMessage.h View File

@@ -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);
};


+ 5
- 1
src/events/juce_Message.h View File

@@ -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 <Message> Ptr;
//==============================================================================
private:


+ 21
- 42
src/events/juce_MessageManager.cpp View File

@@ -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 <Message> 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 <MessageManagerLock::SharedEvents> 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)
{


+ 2
- 4
src/events/juce_MessageManager.h View File

@@ -299,11 +299,9 @@ public:
private:
class SharedEvents;
class BlockingMessage;
friend class SharedEvents;
friend class BlockingMessage;
SharedEvents* sharedEvents;
friend class ReferenceCountedObjectPtr<BlockingMessage>;
ReferenceCountedObjectPtr<BlockingMessage> blockingMessage;
bool locked;
void init (Thread* thread, ThreadPoolJob* job);


+ 2
- 1
src/events/juce_Timer.cpp View File

@@ -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


+ 12
- 0
src/gui/components/juce_Component.cpp View File

@@ -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:


+ 69
- 0
src/native/juce_mac_NativeCode.mm View File

@@ -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 <Message, CriticalSection> 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 <MessageQueue*> (info)->runLoopCallback();
}
};
//==============================================================================
#define JUCE_INCLUDED_FILE 1


+ 6
- 6
src/native/linux/juce_linux_Messaging.cpp View File

@@ -153,7 +153,7 @@ public:
private:
CriticalSection lock;
OwnedArray <Message> queue;
ReferenceCountedArray <Message> 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 <Message> 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;


+ 26
- 64
src/native/mac/juce_iphone_MessageManager.mm View File

@@ -107,79 +107,41 @@ bool MessageManager::runDispatchLoopUntil (int millisecondsToRunFor)
}
//==============================================================================
namespace iOSMessageLoopHelpers
struct MessageDispatchSystem
{
static CFRunLoopRef runLoop = 0;
static CFRunLoopSourceRef runLoopSource = 0;
static OwnedArray <Message, CriticalSection>* 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 <Message, CriticalSection>();
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;
}


+ 3
- 3
src/native/mac/juce_mac_Files.mm View File

@@ -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();


+ 2
- 43
src/native/mac/juce_mac_MessageManager.mm View File

@@ -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 <Message, CriticalSection> 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 <AppDelegateRedirector*> (info)->runLoopCallback();
}
MessageQueue messageQueue;
};


+ 7
- 2
src/native/windows/juce_win32_Messaging.cpp View File

@@ -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 <Message*> (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 <Message*> (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;
}


Loading…
Cancel
Save