1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-10 23:44:24 +00:00

ListenerList: Prevent false positive assertions in callCheckedExcluding

This commit is contained in:
Anthony Nicholls 2025-05-01 10:10:08 +01:00 committed by Anthony Nicholls
parent 6972c4f0e3
commit 76215d2dd0
2 changed files with 47 additions and 13 deletions

View file

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

View file

@ -485,6 +485,38 @@ public:
expect (numberOfCallbacks == 2);
expect (listeners.size() == 2);
}
beginTest ("ThreadSafeListenerList stress test");
{
struct Listener { void callback(){} };
ThreadPool threadPool { 10 };
ThreadSafeListenerList<Listener> listeners;
for (int i = 0; i < 1'000; ++i)
{
threadPool.addJob ([&]
{
std::vector<std::unique_ptr<Listener>> listenersToAdd;
for (int j = 0; j < 1'000; ++j)
{
listenersToAdd.push_back (std::make_unique<Listener>());
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: