diff --git a/modules/juce_core/containers/juce_ListenerList.h b/modules/juce_core/containers/juce_ListenerList.h index 3dfc2c00dc..4401f2af4f 100644 --- a/modules/juce_core/containers/juce_ListenerList.h +++ b/modules/juce_core/containers/juce_ListenerList.h @@ -111,6 +111,8 @@ public: */ void add (ListenerClass* listenerToAdd) { + initialiseIfNeeded(); + if (listenerToAdd != nullptr) listeners->addIfNotAlreadyThere (listenerToAdd); else @@ -127,6 +129,9 @@ public: { jassert (listenerToRemove != nullptr); // Listeners can't be null pointers! + if (! initialised()) + return; + const ScopedLockType lock (listeners->getLock()); if (const auto index = listeners->removeFirstMatchingValue (listenerToRemove); index >= 0) @@ -154,10 +159,10 @@ public: } /** Returns the number of registered listeners. */ - int size() const noexcept { return listeners->size(); } + int size() const noexcept { return ! initialised() ? 0 : listeners->size(); } /** Returns true if no listeners are registered, false otherwise. */ - bool isEmpty() const noexcept { return listeners->isEmpty(); } + bool isEmpty() const noexcept { return ! initialised() || listeners->isEmpty(); } /** Clears the list. @@ -166,7 +171,8 @@ public: */ void clear() { - const ScopedLockType lock (listeners->getLock()); + if (! initialised()) + return; listeners->clear(); @@ -175,7 +181,11 @@ public: } /** Returns true if the specified listener has been added to the list. */ - bool contains (ListenerClass* listener) const noexcept { return listeners->contains (listener); } + bool contains (ListenerClass* listener) const noexcept + { + return initialised() + && listeners->contains (listener); + } /** Returns the raw array of listeners. @@ -186,7 +196,11 @@ public: @see add, remove, clear, contains */ - const ArrayType& getListeners() const noexcept { return *listeners; } + const ArrayType& getListeners() const noexcept + { + const_cast (this)->initialiseIfNeeded(); + return *listeners; + } //============================================================================== /** Calls an invokable object for each listener in the list. */ @@ -234,6 +248,9 @@ public: const BailOutCheckerType& bailOutChecker, Callback&& callback) { + if (! initialised()) + return; + const auto localListeners = listeners; const ScopedLockType lock { localListeners->getLock() }; @@ -339,7 +356,7 @@ private: //============================================================================== using SharedListeners = std::shared_ptr; - const SharedListeners listeners = std::make_shared(); + SharedListeners listeners; struct Iterator { @@ -349,7 +366,37 @@ private: using SafeIterators = std::vector; using SharedIterators = std::shared_ptr; - const SharedIterators iterators = std::make_shared(); + SharedIterators iterators; + + enum class State + { + uninitialised, + initialising, + initialised + }; + + std::atomic state { State::uninitialised }; + + inline bool initialised() const noexcept { return state == State::initialised; } + + inline void initialiseIfNeeded() noexcept + { + if (initialised()) + return; + + auto expected = State::uninitialised; + + if (state.compare_exchange_strong (expected, State::initialising)) + { + listeners = std::make_shared(); + iterators = std::make_shared(); + state = State::initialised; + return; + } + + while (! initialised()) + std::this_thread::yield(); + } //============================================================================== JUCE_DECLARE_NON_COPYABLE (ListenerList)