diff --git a/modules/juce_core/containers/juce_ListenerList.h b/modules/juce_core/containers/juce_ListenerList.h index 3b4b24956c..baa2da7ff4 100644 --- a/modules/juce_core/containers/juce_ListenerList.h +++ b/modules/juce_core/containers/juce_ListenerList.h @@ -229,25 +229,27 @@ public: const BailOutCheckerType& bailOutChecker, Callback&& callback) { - #if JUCE_ASSERTIONS_ENABLED_OR_LOGGED - // Keep a reference to the mutex to protect against the case where this list gets deleted - // during a callback. - auto localMutexPtr = callCheckedExcludingMutex; - const ScopedTryLock callCheckedExcludingLock (*localMutexPtr); - - // If you hit this assertion it means you're trying to call the listeners from multiple - // threads concurrently. If you need to do this either use a LightweightListenerList, for a - // lock free option, or a ThreadSafeListenerList if you also need the extra guarantees - // provided by ListenerList. See the class descriptions for more details. - jassert (callCheckedExcludingLock.isLocked()); - #endif - if (! initialised()) return; const auto localListeners = listeners; const ScopedLockType lock { localListeners->getLock() }; + #if JUCE_ASSERTIONS_ENABLED_OR_LOGGED + { + // Keep a reference to the mutex to protect against the case where this list gets deleted + // during a callback. + auto localMutexPtr = callCheckedExcludingMutex; + const ScopedTryLock callCheckedExcludingLock (*localMutexPtr); + + // If you hit this assertion it means you're trying to call the listeners from multiple + // threads concurrently. If you need to do this either use a LightweightListenerList, for a + // lock free option, or a ThreadSafeListenerList if you also need the extra guarantees + // provided by ListenerList. See the class descriptions for more details. + jassert (callCheckedExcludingLock.isLocked()); + } + #endif + Iterator it{}; it.end = localListeners->size(); diff --git a/modules/juce_core/containers/juce_ListenerList_test.cpp b/modules/juce_core/containers/juce_ListenerList_test.cpp index e7f8bda2d9..30665cf8e5 100644 --- a/modules/juce_core/containers/juce_ListenerList_test.cpp +++ b/modules/juce_core/containers/juce_ListenerList_test.cpp @@ -485,6 +485,38 @@ public: expect (numberOfCallbacks == 2); expect (listeners.size() == 2); } + + beginTest ("ThreadSafeListenerList stress test"); + { + struct Listener { void callback(){} }; + + ThreadPool threadPool { 10 }; + ThreadSafeListenerList listeners; + + for (int i = 0; i < 1'000; ++i) + { + threadPool.addJob ([&] + { + std::vector> listenersToAdd; + + for (int j = 0; j < 1'000; ++j) + { + listenersToAdd.push_back (std::make_unique()); + listeners.add (listenersToAdd.back().get()); + } + + for (auto& listener : listenersToAdd) + listeners.remove (listener.get()); + }); + + threadPool.addJob ([&] + { + listeners.call (&Listener::callback); + }); + } + + expect (threadPool.removeAllJobs (false, 30'000)); + } } private: