[M85] Stop using AutoThread in Thread::Send and make it test only.
Send() was creating an instance of AutoThread for every call,
which is equivalent of instantiatiating a whole new instance of
Thread (AutoThread inherits from Thread) and not just ensuring that
a thread instance is registered for the current thread, as the
comments indicated.
(cherry picked from commit 0fd4c4e630e55644228263dd74f60cb9393656bf)
Bug: webrtc:11908
Change-Id: I8bbb43ca83c30d9f5e1928205b3611271ecad053
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/183441
Reviewed-by: Mirko Bonadei <mbonadei@webrtc.org>
Commit-Queue: Tommi <tommi@webrtc.org>
Cr-Original-Commit-Position: refs/heads/master@{#32037}
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/184504
Reviewed-by: Tommi <tommi@webrtc.org>
Commit-Queue: Guido Urdaneta <guidou@webrtc.org>
Cr-Commit-Position: refs/branch-heads/4183@{#3}
Cr-Branched-From: bf1816170b1f54cf989dea2988496735bf6ddb55-refs/heads/master@{#31567}
diff --git a/rtc_base/thread.cc b/rtc_base/thread.cc
index f8e299a..575c0a6 100644
--- a/rtc_base/thread.cc
+++ b/rtc_base/thread.cc
@@ -32,8 +32,10 @@
#include "rtc_base/atomic_ops.h"
#include "rtc_base/checks.h"
#include "rtc_base/critical_section.h"
+#include "rtc_base/event.h"
#include "rtc_base/logging.h"
#include "rtc_base/null_socket_server.h"
+#include "rtc_base/synchronization/sequence_checker.h"
#include "rtc_base/task_utils/to_queued_task.h"
#include "rtc_base/time_utils.h"
#include "rtc_base/trace_event.h"
@@ -163,6 +165,9 @@
void ThreadManager::RegisterSendAndCheckForCycles(Thread* source,
Thread* target) {
+ RTC_DCHECK(source);
+ RTC_DCHECK(target);
+
CritScope cs(&crit_);
std::deque<Thread*> all_targets({target});
// We check the pre-existing who-sends-to-who graph for any path from target
@@ -889,45 +894,62 @@
AssertBlockingIsAllowedOnCurrentThread();
- AutoThread thread;
Thread* current_thread = Thread::Current();
- RTC_DCHECK(current_thread != nullptr); // AutoThread ensures this
+
#if RTC_DCHECK_IS_ON
- ThreadManager::Instance()->RegisterSendAndCheckForCycles(current_thread,
- this);
-#endif
- bool ready = false;
- PostTask(
- webrtc::ToQueuedTask([msg]() mutable { msg.phandler->OnMessage(&msg); },
- [this, &ready, current_thread] {
- CritScope cs(&crit_);
- ready = true;
- current_thread->socketserver()->WakeUp();
- }));
-
- bool waited = false;
- crit_.Enter();
- while (!ready) {
- crit_.Leave();
- current_thread->socketserver()->Wait(kForever, false);
- waited = true;
- crit_.Enter();
+ if (current_thread) {
+ RTC_DCHECK(current_thread->IsInvokeToThreadAllowed(this));
+ ThreadManager::Instance()->RegisterSendAndCheckForCycles(current_thread,
+ this);
}
- crit_.Leave();
+#endif
- // Our Wait loop above may have consumed some WakeUp events for this
- // Thread, that weren't relevant to this Send. Losing these WakeUps can
- // cause problems for some SocketServers.
- //
- // Concrete example:
- // Win32SocketServer on thread A calls Send on thread B. While processing the
- // message, thread B Posts a message to A. We consume the wakeup for that
- // Post while waiting for the Send to complete, which means that when we exit
- // this loop, we need to issue another WakeUp, or else the Posted message
- // won't be processed in a timely manner.
+ // Perhaps down the line we can get rid of this workaround and always require
+ // current_thread to be valid when Send() is called.
+ std::unique_ptr<rtc::Event> done_event;
+ if (!current_thread)
+ done_event.reset(new rtc::Event());
- if (waited) {
- current_thread->socketserver()->WakeUp();
+ bool ready = false;
+ PostTask(webrtc::ToQueuedTask(
+ [&msg]() mutable { msg.phandler->OnMessage(&msg); },
+ [this, &ready, current_thread, done = done_event.get()] {
+ if (current_thread) {
+ CritScope cs(&crit_);
+ ready = true;
+ current_thread->socketserver()->WakeUp();
+ } else {
+ done->Set();
+ }
+ }));
+
+ if (current_thread) {
+ bool waited = false;
+ crit_.Enter();
+ while (!ready) {
+ crit_.Leave();
+ current_thread->socketserver()->Wait(kForever, false);
+ waited = true;
+ crit_.Enter();
+ }
+ crit_.Leave();
+
+ // Our Wait loop above may have consumed some WakeUp events for this
+ // Thread, that weren't relevant to this Send. Losing these WakeUps can
+ // cause problems for some SocketServers.
+ //
+ // Concrete example:
+ // Win32SocketServer on thread A calls Send on thread B. While processing
+ // the message, thread B Posts a message to A. We consume the wakeup for
+ // that Post while waiting for the Send to complete, which means that when
+ // we exit this loop, we need to issue another WakeUp, or else the Posted
+ // message won't be processed in a timely manner.
+
+ if (waited) {
+ current_thread->socketserver()->WakeUp();
+ }
+ } else {
+ done_event->Wait(rtc::Event::kForever);
}
}
@@ -974,6 +996,50 @@
task.release();
}
+void Thread::AllowInvokesToThread(Thread* thread) {
+#if (!defined(NDEBUG) || defined(DCHECK_ALWAYS_ON))
+ if (!IsCurrent()) {
+ PostTask(webrtc::ToQueuedTask(
+ [thread, this]() { AllowInvokesToThread(thread); }));
+ return;
+ }
+ RTC_DCHECK_RUN_ON(this);
+ allowed_threads_.push_back(thread);
+ invoke_policy_enabled_ = true;
+#endif
+}
+
+void Thread::DisallowAllInvokes() {
+#if (!defined(NDEBUG) || defined(DCHECK_ALWAYS_ON))
+ if (!IsCurrent()) {
+ PostTask(webrtc::ToQueuedTask([this]() { DisallowAllInvokes(); }));
+ return;
+ }
+ RTC_DCHECK_RUN_ON(this);
+ allowed_threads_.clear();
+ invoke_policy_enabled_ = true;
+#endif
+}
+
+// Returns true if no policies added or if there is at least one policy
+// that permits invocation to |target| thread.
+bool Thread::IsInvokeToThreadAllowed(rtc::Thread* target) {
+#if (!defined(NDEBUG) || defined(DCHECK_ALWAYS_ON))
+ RTC_DCHECK_RUN_ON(this);
+ if (!invoke_policy_enabled_) {
+ return true;
+ }
+ for (const auto* thread : allowed_threads_) {
+ if (thread == target) {
+ return true;
+ }
+ }
+ return false;
+#else
+ return true;
+#endif
+}
+
void Thread::PostTask(std::unique_ptr<webrtc::QueuedTask> task) {
// Though Post takes MessageData by raw pointer (last parameter), it still
// takes it with ownership.
diff --git a/rtc_base/thread.h b/rtc_base/thread.h
index e25ed4e..8ae93b4 100644
--- a/rtc_base/thread.h
+++ b/rtc_base/thread.h
@@ -338,6 +338,18 @@
InvokeInternal(posted_from, functor);
}
+ // Allows invoke to specified |thread|. Thread never will be dereferenced and
+ // will be used only for reference-based comparison, so instance can be safely
+ // deleted. If NDEBUG is defined and DCHECK_ALWAYS_ON is undefined do nothing.
+ void AllowInvokesToThread(Thread* thread);
+ // If NDEBUG is defined and DCHECK_ALWAYS_ON is undefined do nothing.
+ void DisallowAllInvokes();
+ // Returns true if |target| was allowed by AllowInvokesToThread() or if no
+ // calls were made to AllowInvokesToThread and DisallowAllInvokes. Otherwise
+ // returns false.
+ // If NDEBUG is defined and DCHECK_ALWAYS_ON is undefined always returns true.
+ bool IsInvokeToThreadAllowed(rtc::Thread* target);
+
// Posts a task to invoke the functor on |this| thread asynchronously, i.e.
// without blocking the thread that invoked PostTask(). Ownership of |functor|
// is passed and (usually, see below) destroyed on |this| thread after it is
@@ -566,6 +578,10 @@
MessageList messages_ RTC_GUARDED_BY(crit_);
PriorityQueue delayed_messages_ RTC_GUARDED_BY(crit_);
uint32_t delayed_next_num_ RTC_GUARDED_BY(crit_);
+#if (!defined(NDEBUG) || defined(DCHECK_ALWAYS_ON))
+ std::vector<Thread*> allowed_threads_ RTC_GUARDED_BY(this);
+ bool invoke_policy_enabled_ RTC_GUARDED_BY(this) = false;
+#endif
CriticalSection crit_;
bool fInitialized_;
bool fDestroyed_;
@@ -612,7 +628,9 @@
// AutoThread automatically installs itself at construction
// uninstalls at destruction, if a Thread object is
// _not already_ associated with the current OS thread.
-
+//
+// NOTE: *** This class should only be used by tests ***
+//
class AutoThread : public Thread {
public:
AutoThread();