AsyncResolver: avoid hanging the WorkerThread.

There's a problem where the destruction of the contained rtc::Thread
will join the spawned thread blocked on getaddrinfo(). However,
getaddrinfo() is sometimes slow and this behavior hinders packet traffic
when it happens.

Fix this by using the brand new detachable PlatformThread support.

Fixed: b:181572711, webrtc:12659
Change-Id: I0b7e0cca3b8b1b3ed22328d940b1bb95cacb5e24
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/214780
Commit-Queue: Markus Handell <handellm@webrtc.org>
Reviewed-by: Tommi <tommi@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#33804}
This commit is contained in:
Markus Handell 2021-04-21 10:22:34 +02:00 committed by Commit Bot
parent c5bac77159
commit 1366b0f841
3 changed files with 62 additions and 15 deletions

View File

@ -795,6 +795,7 @@ rtc_library("threading") {
":socket_server",
":timeutils",
"../api:function_view",
"../api:refcountedbase",
"../api:scoped_refptr",
"../api:sequence_checker",
"../api/task_queue",

View File

@ -10,9 +10,14 @@
#include "rtc_base/async_resolver.h"
#include <memory>
#include <string>
#include <utility>
#include "api/ref_counted_base.h"
#include "rtc_base/synchronization/mutex.h"
#include "rtc_base/thread_annotations.h"
#if defined(WEBRTC_WIN)
#include <ws2spi.h>
#include <ws2tcpip.h>
@ -30,6 +35,7 @@
#include "api/task_queue/task_queue_base.h"
#include "rtc_base/ip_address.h"
#include "rtc_base/logging.h"
#include "rtc_base/platform_thread.h"
#include "rtc_base/task_queue.h"
#include "rtc_base/task_utils/to_queued_task.h"
#include "rtc_base/third_party/sigslot/sigslot.h" // for signal_with_thread...
@ -87,30 +93,67 @@ int ResolveHostname(const std::string& hostname,
#endif // !__native_client__
}
AsyncResolver::AsyncResolver() : error_(-1) {}
struct AsyncResolver::State : public RefCountedBase {
webrtc::Mutex mutex;
enum class Status {
kLive,
kDead
} status RTC_GUARDED_BY(mutex) = Status::kLive;
};
AsyncResolver::AsyncResolver() : error_(-1), state_(new State) {}
AsyncResolver::~AsyncResolver() {
RTC_DCHECK_RUN_ON(&sequence_checker_);
// Ensure the thread isn't using a stale reference to the current task queue,
// or calling into ResolveDone post destruction.
webrtc::MutexLock lock(&state_->mutex);
state_->status = State::Status::kDead;
}
void RunResolution(void* obj) {
std::function<void()>* function_ptr =
static_cast<std::function<void()>*>(obj);
(*function_ptr)();
delete function_ptr;
}
void AsyncResolver::Start(const SocketAddress& addr) {
RTC_DCHECK_RUN_ON(&sequence_checker_);
RTC_DCHECK(!destroy_called_);
addr_ = addr;
webrtc::TaskQueueBase* current_task_queue = webrtc::TaskQueueBase::Current();
popup_thread_ = Thread::Create();
popup_thread_->Start();
popup_thread_->PostTask(webrtc::ToQueuedTask(
[this, flag = safety_.flag(), addr, current_task_queue] {
auto thread_function =
[this, addr, caller_task_queue = webrtc::TaskQueueBase::Current(),
state = state_] {
std::vector<IPAddress> addresses;
int error =
ResolveHostname(addr.hostname().c_str(), addr.family(), &addresses);
current_task_queue->PostTask(webrtc::ToQueuedTask(
std::move(flag), [this, error, addresses = std::move(addresses)] {
RTC_DCHECK_RUN_ON(&sequence_checker_);
ResolveDone(std::move(addresses), error);
}));
}));
webrtc::MutexLock lock(&state->mutex);
if (state->status == State::Status::kLive) {
caller_task_queue->PostTask(webrtc::ToQueuedTask(
[this, error, addresses = std::move(addresses), state] {
bool live;
{
// ResolveDone can lead to instance destruction, so make sure
// we don't deadlock.
webrtc::MutexLock lock(&state->mutex);
live = state->status == State::Status::kLive;
}
if (live) {
RTC_DCHECK_RUN_ON(&sequence_checker_);
ResolveDone(std::move(addresses), error);
}
}));
}
};
PlatformThread thread(RunResolution,
new std::function<void()>(std::move(thread_function)),
"NameResolution", ThreadAttributes().SetDetached());
thread.Start();
// Although |thread| is detached, the PlatformThread contract mandates to call
// Stop() before destruction. The call doesn't actually stop anything.
thread.Stop();
}
bool AsyncResolver::GetResolvedAddress(int family, SocketAddress* addr) const {

View File

@ -17,12 +17,13 @@
#include <winsock2.h> // NOLINT
#endif
#include <memory>
#include <vector>
#include "api/sequence_checker.h"
#include "rtc_base/async_resolver_interface.h"
#include "rtc_base/event.h"
#include "rtc_base/ip_address.h"
#include "rtc_base/ref_counted_object.h"
#include "rtc_base/socket_address.h"
#include "rtc_base/system/no_unique_address.h"
#include "rtc_base/system/rtc_export.h"
@ -52,6 +53,9 @@ class RTC_EXPORT AsyncResolver : public AsyncResolverInterface {
const std::vector<IPAddress>& addresses() const;
private:
// Fwd decl.
struct State;
void ResolveDone(std::vector<IPAddress> addresses, int error)
RTC_EXCLUSIVE_LOCKS_REQUIRED(sequence_checker_);
void MaybeSelfDestruct();
@ -59,11 +63,10 @@ class RTC_EXPORT AsyncResolver : public AsyncResolverInterface {
SocketAddress addr_ RTC_GUARDED_BY(sequence_checker_);
std::vector<IPAddress> addresses_ RTC_GUARDED_BY(sequence_checker_);
int error_ RTC_GUARDED_BY(sequence_checker_);
webrtc::ScopedTaskSafety safety_ RTC_GUARDED_BY(sequence_checker_);
std::unique_ptr<Thread> popup_thread_ RTC_GUARDED_BY(sequence_checker_);
bool recursion_check_ =
false; // Protects against SignalDone calling into Destroy.
bool destroy_called_ = false;
scoped_refptr<State> state_;
RTC_NO_UNIQUE_ADDRESS webrtc::SequenceChecker sequence_checker_;
};