Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fix a race in Broadcaster/Listener interaction
Summary: The following problem was occuring: - broadcaster B had two listeners: L1 and L2 (thread T1) - (T1) B has started to broadcast an event, it has locked a shared_ptr to L1 (in ListenerIterator()) - on another thread T2 the penultimate reference to L1 was destroyed (the transient object in B is now the last reference) - (T2) the last reference to L2 was destroyed as well - (T1) B has finished broadcasting the event to L1 and destroyed the last shared_ptr - (T1) this triggered the destructor, which called into B->RemoveListener() - (T1) all pointers in the m_listeners list were now stale, so RemoveListener emptied the list - (T1) Eventually control returned to the ListenerIterator() for doing broadcasting, which was still in the middle of iterating through the list - (T1) Only now, it was holding onto a dangling iterator. BOOM. I fix this issue by making sure nothing can interfere with the iterate-and-remove-expired-pointers loop, by moving this logic into a single function, which first locks (or clears) the whole list and then returns the list of valid and locked Listeners for further processing. Instead of std::list I use an llvm::SmallVector which should hopefully offset the fact that we create a copy of the list for the common case where we have only a few listeners (no heap allocations). A slight difference in behaviour is that now RemoveListener does not remove an element from the list -- it only sets it's mask to 0, which means it will be removed during the next iteration of GetListeners(). This is purely an implementation detail and it should not be externally noticable. I was not able to reproduce this bug reliably without inserting sleep statements into the code, so I do not add a test for it. Instead, I add some unit tests for the functions that I do modify. Reviewers: clayborg, jingham Subscribers: tberghammer, lldb-commits Differential Revision: https://reviews.llvm.org/D23406 llvm-svn: 278664
- Loading branch information
Showing
4 changed files
with
122 additions
and
86 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
//===-- BroadcasterTest.cpp -------------------------------------*- C++ -*-===// | ||
// | ||
// The LLVM Compiler Infrastructure | ||
// | ||
// This file is distributed under the University of Illinois Open Source | ||
// License. See LICENSE.TXT for details. | ||
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
#include "gtest/gtest.h" | ||
|
||
#include "lldb/Core/Broadcaster.h" | ||
#include "lldb/Core/Listener.h" | ||
#include "lldb/Host/Predicate.h" | ||
|
||
#include <thread> | ||
|
||
using namespace lldb; | ||
using namespace lldb_private; | ||
|
||
TEST(BroadcasterTest, BroadcastEvent) | ||
{ | ||
EventSP event_sp; | ||
Broadcaster broadcaster(nullptr, "test-broadcaster"); | ||
|
||
// Create a listener, sign it up, make sure it recieves an event. | ||
ListenerSP listener1_sp = Listener::MakeListener("test-listener1"); | ||
const uint32_t event_mask1 = 1; | ||
EXPECT_EQ(event_mask1, listener1_sp->StartListeningForEvents(&broadcaster, event_mask1)); | ||
broadcaster.BroadcastEvent(event_mask1, nullptr); | ||
EXPECT_TRUE(listener1_sp->GetNextEvent(event_sp)); | ||
EXPECT_EQ(event_mask1, event_sp->GetType()); | ||
|
||
{ | ||
// Add one more listener, make sure it works as well. | ||
ListenerSP listener2_sp = Listener::MakeListener("test-listener2"); | ||
const uint32_t event_mask2 = 1; | ||
EXPECT_EQ(event_mask2, listener2_sp->StartListeningForEvents(&broadcaster, event_mask1 | event_mask2)); | ||
broadcaster.BroadcastEvent(event_mask2, nullptr); | ||
EXPECT_TRUE(listener2_sp->GetNextEvent(event_sp)); | ||
EXPECT_EQ(event_mask2, event_sp->GetType()); | ||
|
||
// Both listeners should get this event. | ||
broadcaster.BroadcastEvent(event_mask1, nullptr); | ||
EXPECT_TRUE(listener1_sp->GetNextEvent(event_sp)); | ||
EXPECT_EQ(event_mask1, event_sp->GetType()); | ||
EXPECT_TRUE(listener2_sp->GetNextEvent(event_sp)); | ||
EXPECT_EQ(event_mask2, event_sp->GetType()); | ||
} | ||
|
||
// Now again only one listener should be active. | ||
broadcaster.BroadcastEvent(event_mask1, nullptr); | ||
EXPECT_TRUE(listener1_sp->GetNextEvent(event_sp)); | ||
EXPECT_EQ(event_mask1, event_sp->GetType()); | ||
} | ||
|
||
TEST(BroadcasterTest, EventTypeHasListeners) | ||
{ | ||
EventSP event_sp; | ||
Broadcaster broadcaster(nullptr, "test-broadcaster"); | ||
|
||
const uint32_t event_mask = 1; | ||
EXPECT_FALSE(broadcaster.EventTypeHasListeners(event_mask)); | ||
|
||
{ | ||
ListenerSP listener_sp = Listener::MakeListener("test-listener"); | ||
EXPECT_EQ(event_mask, listener_sp->StartListeningForEvents(&broadcaster, event_mask)); | ||
EXPECT_TRUE(broadcaster.EventTypeHasListeners(event_mask)); | ||
} | ||
|
||
EXPECT_FALSE(broadcaster.EventTypeHasListeners(event_mask)); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,5 @@ | ||
add_lldb_unittest(LLDBCoreTests | ||
BroadcasterTest.cpp | ||
DataExtractorTest.cpp | ||
ScalarTest.cpp | ||
) |