Skip to content

Commit

Permalink
ListenerList: Optimise empty listener list
Browse files Browse the repository at this point in the history
  • Loading branch information
Anthony-Nicholls committed Jun 7, 2024
1 parent 315167a commit 0dfff14
Showing 1 changed file with 54 additions and 7 deletions.
61 changes: 54 additions & 7 deletions modules/juce_core/containers/juce_ListenerList.h
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,8 @@ class ListenerList
*/
void add (ListenerClass* listenerToAdd)
{
initialiseIfNeeded();

if (listenerToAdd != nullptr)
listeners->addIfNotAlreadyThere (listenerToAdd);
else
Expand All @@ -127,6 +129,9 @@ class ListenerList
{
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)
Expand Down Expand Up @@ -154,10 +159,10 @@ class ListenerList
}

/** 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.
Expand All @@ -166,7 +171,8 @@ class ListenerList
*/
void clear()
{
const ScopedLockType lock (listeners->getLock());
if (! initialised())
return;

listeners->clear();

Expand All @@ -175,7 +181,11 @@ class ListenerList
}

/** 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.
Expand All @@ -186,7 +196,11 @@ class ListenerList
@see add, remove, clear, contains
*/
const ArrayType& getListeners() const noexcept { return *listeners; }
const ArrayType& getListeners() const noexcept
{
const_cast<ListenerList*> (this)->initialiseIfNeeded();
return *listeners;
}

//==============================================================================
/** Calls an invokable object for each listener in the list. */
Expand Down Expand Up @@ -234,6 +248,9 @@ class ListenerList
const BailOutCheckerType& bailOutChecker,
Callback&& callback)
{
if (! initialised())
return;

const auto localListeners = listeners;
const ScopedLockType lock { localListeners->getLock() };

Expand Down Expand Up @@ -339,7 +356,7 @@ class ListenerList

//==============================================================================
using SharedListeners = std::shared_ptr<ArrayType>;
const SharedListeners listeners = std::make_shared<ArrayType>();
SharedListeners listeners;

struct Iterator
{
Expand All @@ -349,7 +366,37 @@ class ListenerList

using SafeIterators = std::vector<Iterator*>;
using SharedIterators = std::shared_ptr<SafeIterators>;
const SharedIterators iterators = std::make_shared<SafeIterators>();
SharedIterators iterators;

enum class State
{
uninitialised,
initialising,
initialised
};

std::atomic<State> 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<ArrayType>();
iterators = std::make_shared<SafeIterators>();
state = State::initialised;
return;
}

while (! initialised())
std::this_thread::yield();
}

//==============================================================================
JUCE_DECLARE_NON_COPYABLE (ListenerList)
Expand Down

0 comments on commit 0dfff14

Please sign in to comment.