Commit d9a07cca authored by yhirano's avatar yhirano Committed by Commit bot

Remove lock from SharedMemoryDataConsumerHandle::Context destructor

SharedMemoryDataConsumerHandle::Context posts tasks while its lock_
locked. That means a lock held by the TaskRunner will be locked
after |lock_| is locked. On the other hand, the Context may be
destructed in the middle of TaskRunner destruction, which means we
cannot lock |lock_| in the Context destruction. Otherwise, a
lock-order-inversion problem will be introduced.

This CL stops locking |lock_| in the destructor. It is safe because
no one accesses to Content's member variables without a valid
scoped_refptr to the Context.

BUG=651747

Review-Url: https://codereview.chromium.org/2391513002
Cr-Commit-Position: refs/heads/master@{#422614}
parent 048bee10
...@@ -89,19 +89,16 @@ class SharedMemoryDataConsumerHandle::Context final ...@@ -89,19 +89,16 @@ class SharedMemoryDataConsumerHandle::Context final
} }
void ClearQueue() { void ClearQueue() {
lock_.AssertAcquired(); lock_.AssertAcquired();
for (auto* data : queue_) {
delete data;
}
queue_.clear(); queue_.clear();
first_offset_ = 0; first_offset_ = 0;
} }
RequestPeer::ThreadSafeReceivedData* Top() { RequestPeer::ThreadSafeReceivedData* Top() {
lock_.AssertAcquired(); lock_.AssertAcquired();
return queue_.front(); return queue_.front().get();
} }
void Push(std::unique_ptr<RequestPeer::ThreadSafeReceivedData> data) { void Push(std::unique_ptr<RequestPeer::ThreadSafeReceivedData> data) {
lock_.AssertAcquired(); lock_.AssertAcquired();
queue_.push_back(data.release()); queue_.push_back(std::move(data));
} }
size_t first_offset() const { size_t first_offset() const {
lock_.AssertAcquired(); lock_.AssertAcquired();
...@@ -188,7 +185,6 @@ class SharedMemoryDataConsumerHandle::Context final ...@@ -188,7 +185,6 @@ class SharedMemoryDataConsumerHandle::Context final
first_offset_ += s; first_offset_ += s;
auto* top = Top(); auto* top = Top();
if (static_cast<size_t>(top->length()) <= first_offset_) { if (static_cast<size_t>(top->length()) <= first_offset_) {
delete top;
queue_.pop_front(); queue_.pop_front();
first_offset_ = 0; first_offset_ = 0;
} }
...@@ -232,14 +228,8 @@ class SharedMemoryDataConsumerHandle::Context final ...@@ -232,14 +228,8 @@ class SharedMemoryDataConsumerHandle::Context final
} }
void Clear() { void Clear() {
lock_.AssertAcquired(); lock_.AssertAcquired();
for (auto* data : queue_) { ClearQueue();
delete data;
}
queue_.clear();
first_offset_ = 0;
client_ = nullptr; client_ = nullptr;
// Note this doesn't work in the destructor if |on_reader_detached_| is not
// null. We have an assert in the destructor.
ResetOnReaderDetached(); ResetOnReaderDetached();
} }
// Must be called with |lock_| not aquired. // Must be called with |lock_| not aquired.
...@@ -249,21 +239,13 @@ class SharedMemoryDataConsumerHandle::Context final ...@@ -249,21 +239,13 @@ class SharedMemoryDataConsumerHandle::Context final
} }
friend class base::RefCountedThreadSafe<Context>; friend class base::RefCountedThreadSafe<Context>;
~Context() { ~Context() = default;
base::AutoLock lock(lock_);
DCHECK(on_reader_detached_.is_null());
// This is necessary because the queue stores raw pointers.
Clear();
}
base::Lock lock_; base::Lock lock_;
// |result_| stores the ultimate state of this handle if it has. Otherwise, // |result_| stores the ultimate state of this handle if it has. Otherwise,
// |Ok| is set. // |Ok| is set.
Result result_; Result result_;
// TODO(yhirano): Use std::deque<std::unique_ptr<ThreadSafeReceivedData>> std::deque<std::unique_ptr<RequestPeer::ThreadSafeReceivedData>> queue_;
// once it is allowed.
std::deque<RequestPeer::ThreadSafeReceivedData*> queue_;
size_t first_offset_; size_t first_offset_;
Client* client_; Client* client_;
scoped_refptr<base::SingleThreadTaskRunner> notification_task_runner_; scoped_refptr<base::SingleThreadTaskRunner> notification_task_runner_;
......
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#include "base/message_loop/message_loop.h" #include "base/message_loop/message_loop.h"
#include "base/run_loop.h" #include "base/run_loop.h"
#include "base/single_thread_task_runner.h" #include "base/single_thread_task_runner.h"
#include "base/strings/string_split.h"
#include "base/task_runner.h" #include "base/task_runner.h"
#include "base/threading/thread.h" #include "base/threading/thread.h"
#include "base/threading/thread_task_runner_handle.h" #include "base/threading/thread_task_runner_handle.h"
...@@ -974,15 +975,22 @@ TEST(SharedMemoryDataConsumerHandleBackpressureTest, CloseAndReset) { ...@@ -974,15 +975,22 @@ TEST(SharedMemoryDataConsumerHandleBackpressureTest, CloseAndReset) {
reader.reset(); reader.reset();
logger->Add("4"); logger->Add("4");
EXPECT_EQ( std::vector<std::string> log = base::SplitString(
"1\n" logger->log(), "\n", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL);
"2\n"
"3\n" ASSERT_EQ(8u, log.size());
"data1 is destructed.\n" EXPECT_EQ("1", log[0]);
"data2 is destructed.\n" EXPECT_EQ("2", log[1]);
"data3 is destructed.\n" EXPECT_EQ("3", log[2]);
"4\n", EXPECT_EQ("4", log[6]);
logger->log()); EXPECT_EQ("", log[7]);
// The destruction order doesn't matter in this case.
std::vector<std::string> destruction_entries = {log[3], log[4], log[5]};
std::sort(destruction_entries.begin(), destruction_entries.end());
EXPECT_EQ(destruction_entries[0], "data1 is destructed.");
EXPECT_EQ(destruction_entries[1], "data2 is destructed.");
EXPECT_EQ(destruction_entries[2], "data3 is destructed.");
} }
TEST(SharedMemoryDataConsumerHandleWithoutBackpressureTest, AddData) { TEST(SharedMemoryDataConsumerHandleWithoutBackpressureTest, AddData) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment