Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(104)

Side by Side Diff: webrtc/base/asyncinvoker.cc

Issue 2694723004: Making AsyncInvoker destructor thread-safe. (Closed)
Patch Set: Switch to MessageQueueManager::Clear, for when multiple threads are blocked. Created 3 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 /* 1 /*
2 * Copyright 2014 The WebRTC Project Authors. All rights reserved. 2 * Copyright 2014 The WebRTC Project Authors. All rights reserved.
3 * 3 *
4 * Use of this source code is governed by a BSD-style license 4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source 5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found 6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may 7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree. 8 * be found in the AUTHORS file in the root of the source tree.
9 */ 9 */
10 10
11 #include "webrtc/base/asyncinvoker.h" 11 #include "webrtc/base/asyncinvoker.h"
12 12
13 #include "webrtc/base/atomicops.h"
13 #include "webrtc/base/checks.h" 14 #include "webrtc/base/checks.h"
14 #include "webrtc/base/logging.h" 15 #include "webrtc/base/logging.h"
15 16
16 namespace rtc { 17 namespace rtc {
17 18
18 AsyncInvoker::AsyncInvoker() : destroying_(false) {} 19 AsyncInvoker::AsyncInvoker() : invocation_complete_(false, false) {}
19 20
20 AsyncInvoker::~AsyncInvoker() { 21 AsyncInvoker::~AsyncInvoker() {
21 destroying_ = true; 22 destroying_ = true;
22 SignalInvokerDestroyed(); 23 SignalInvokerDestroyed();
23 // Messages for this need to be cleared *before* our destructor is complete. 24 // Messages for this need to be cleared *before* our destructor is complete.
24 MessageQueueManager::Clear(this); 25 MessageQueueManager::Clear(this);
26 // And we need to wait for any invocations that are still in progress on
27 // other threads.
28 while (AtomicOps::AcquireLoad(&pending_invocations_)) {
tommi 2017/02/22 11:19:53 is there a chance that we could get 0 returned her
Taylor Brandstetter 2017/02/23 01:19:00 That would mean that the invoker is being destroye
29 // If the destructor was called while AsyncInvoke was being called by
30 // another thread, WITHIN an AsyncInvoked functor, it may do another
31 // Thread::Post even after we called MessageQueueManager::Clear(this). So
32 // we need to keep calling Clear to discard these posts.
33 MessageQueueManager::Clear(this);
34 invocation_complete_.Wait(Event::kForever);
35 }
25 } 36 }
26 37
27 void AsyncInvoker::OnMessage(Message* msg) { 38 void AsyncInvoker::OnMessage(Message* msg) {
28 // Get the AsyncClosure shared ptr from this message's data. 39 // Get the AsyncClosure shared ptr from this message's data.
29 ScopedRefMessageData<AsyncClosure>* data = 40 ScopedRefMessageData<AsyncClosure>* data =
30 static_cast<ScopedRefMessageData<AsyncClosure>*>(msg->pdata); 41 static_cast<ScopedRefMessageData<AsyncClosure>*>(msg->pdata);
31 scoped_refptr<AsyncClosure> closure = data->data(); 42 scoped_refptr<AsyncClosure> closure = data->data();
32 delete msg->pdata; 43 delete msg->pdata;
33 msg->pdata = NULL; 44 msg->pdata = NULL;
34 45
(...skipping 20 matching lines...) Expand all
55 } 66 }
56 67
57 void AsyncInvoker::DoInvoke(const Location& posted_from, 68 void AsyncInvoker::DoInvoke(const Location& posted_from,
58 Thread* thread, 69 Thread* thread,
59 const scoped_refptr<AsyncClosure>& closure, 70 const scoped_refptr<AsyncClosure>& closure,
60 uint32_t id) { 71 uint32_t id) {
61 if (destroying_) { 72 if (destroying_) {
62 LOG(LS_WARNING) << "Tried to invoke while destroying the invoker."; 73 LOG(LS_WARNING) << "Tried to invoke while destroying the invoker.";
63 return; 74 return;
64 } 75 }
76 AtomicOps::Increment(&pending_invocations_);
65 thread->Post(posted_from, this, id, 77 thread->Post(posted_from, this, id,
66 new ScopedRefMessageData<AsyncClosure>(closure)); 78 new ScopedRefMessageData<AsyncClosure>(closure));
67 } 79 }
68 80
69 void AsyncInvoker::DoInvokeDelayed(const Location& posted_from, 81 void AsyncInvoker::DoInvokeDelayed(const Location& posted_from,
70 Thread* thread, 82 Thread* thread,
71 const scoped_refptr<AsyncClosure>& closure, 83 const scoped_refptr<AsyncClosure>& closure,
72 uint32_t delay_ms, 84 uint32_t delay_ms,
73 uint32_t id) { 85 uint32_t id) {
74 if (destroying_) { 86 if (destroying_) {
75 LOG(LS_WARNING) << "Tried to invoke while destroying the invoker."; 87 LOG(LS_WARNING) << "Tried to invoke while destroying the invoker.";
76 return; 88 return;
77 } 89 }
90 AtomicOps::Increment(&pending_invocations_);
pthatcher1 2017/02/17 18:19:38 Do we have any sense of what perf impact these add
Taylor Brandstetter 2017/02/17 19:00:46 Assuming the CPU architecture supports atomic oper
78 thread->PostDelayed(posted_from, delay_ms, this, id, 91 thread->PostDelayed(posted_from, delay_ms, this, id,
79 new ScopedRefMessageData<AsyncClosure>(closure)); 92 new ScopedRefMessageData<AsyncClosure>(closure));
80 } 93 }
81 94
82 GuardedAsyncInvoker::GuardedAsyncInvoker() : thread_(Thread::Current()) { 95 GuardedAsyncInvoker::GuardedAsyncInvoker() : thread_(Thread::Current()) {
83 thread_->SignalQueueDestroyed.connect(this, 96 thread_->SignalQueueDestroyed.connect(this,
84 &GuardedAsyncInvoker::ThreadDestroyed); 97 &GuardedAsyncInvoker::ThreadDestroyed);
85 } 98 }
86 99
87 GuardedAsyncInvoker::~GuardedAsyncInvoker() { 100 GuardedAsyncInvoker::~GuardedAsyncInvoker() {
88 } 101 }
89 102
90 bool GuardedAsyncInvoker::Flush(uint32_t id) { 103 bool GuardedAsyncInvoker::Flush(uint32_t id) {
91 rtc::CritScope cs(&crit_); 104 rtc::CritScope cs(&crit_);
92 if (thread_ == nullptr) 105 if (thread_ == nullptr)
93 return false; 106 return false;
94 invoker_.Flush(thread_, id); 107 invoker_.Flush(thread_, id);
95 return true; 108 return true;
96 } 109 }
97 110
98 void GuardedAsyncInvoker::ThreadDestroyed() { 111 void GuardedAsyncInvoker::ThreadDestroyed() {
99 rtc::CritScope cs(&crit_); 112 rtc::CritScope cs(&crit_);
100 // We should never get more than one notification about the thread dying. 113 // We should never get more than one notification about the thread dying.
101 RTC_DCHECK(thread_ != nullptr); 114 RTC_DCHECK(thread_ != nullptr);
102 thread_ = nullptr; 115 thread_ = nullptr;
103 } 116 }
104 117
118 AsyncClosure::~AsyncClosure() {
119 AtomicOps::Decrement(&invoker_->pending_invocations_);
120 invoker_->invocation_complete_.Set();
pthatcher1 2017/02/17 18:19:38 Why does it need the friend class to touch a priva
Taylor Brandstetter 2017/02/17 19:00:46 Because only AsyncClosure should be able to do thi
121 }
122
105 NotifyingAsyncClosureBase::NotifyingAsyncClosureBase( 123 NotifyingAsyncClosureBase::NotifyingAsyncClosureBase(
106 AsyncInvoker* invoker, 124 AsyncInvoker* invoker,
107 const Location& callback_posted_from, 125 const Location& callback_posted_from,
108 Thread* calling_thread) 126 Thread* calling_thread)
109 : invoker_(invoker), 127 : AsyncClosure(invoker),
110 callback_posted_from_(callback_posted_from), 128 callback_posted_from_(callback_posted_from),
111 calling_thread_(calling_thread) { 129 calling_thread_(calling_thread) {
112 calling_thread->SignalQueueDestroyed.connect( 130 calling_thread->SignalQueueDestroyed.connect(
113 this, &NotifyingAsyncClosureBase::CancelCallback); 131 this, &NotifyingAsyncClosureBase::CancelCallback);
114 invoker->SignalInvokerDestroyed.connect( 132 invoker->SignalInvokerDestroyed.connect(
115 this, &NotifyingAsyncClosureBase::CancelCallback); 133 this, &NotifyingAsyncClosureBase::CancelCallback);
116 } 134 }
117 135
118 NotifyingAsyncClosureBase::~NotifyingAsyncClosureBase() { 136 NotifyingAsyncClosureBase::~NotifyingAsyncClosureBase() {
119 disconnect_all(); 137 disconnect_all();
(...skipping 10 matching lines...) Expand all
130 void NotifyingAsyncClosureBase::CancelCallback() { 148 void NotifyingAsyncClosureBase::CancelCallback() {
131 // If the callback is triggering when this is called, block the 149 // If the callback is triggering when this is called, block the
132 // destructor of the dying object here by waiting until the callback 150 // destructor of the dying object here by waiting until the callback
133 // is done triggering. 151 // is done triggering.
134 CritScope cs(&crit_); 152 CritScope cs(&crit_);
135 // calling_thread_ == NULL means do not trigger the callback. 153 // calling_thread_ == NULL means do not trigger the callback.
136 calling_thread_ = NULL; 154 calling_thread_ = NULL;
137 } 155 }
138 156
139 } // namespace rtc 157 } // namespace rtc
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698