MutexLock cs(&crit_);
std::deque<Thread*> all_targets({target}); // We check the pre-existing who-sends-to-who graph for any path from target // to source. This loop is guaranteed to terminate because per the send graph // invariant, there are no cycles in the graph. for (size_t i = 0; i < all_targets.size(); i++) { constauto& targets = send_graph_[all_targets[i]];
all_targets.insert(all_targets.end(), targets.begin(), targets.end());
}
RTC_CHECK_EQ(absl::c_count(all_targets, source), 0)
<< " send loop between " << source->name() << " and " << target->name();
// We may now insert source -> target without creating a cycle, since there // was no path from target to source per the prior CHECK.
send_graph_[source].insert(target);
} #endif
void ThreadManager::ProcessAllMessageQueuesInternal() { // This works by posting a delayed message at the current time and waiting // for it to be dispatched on all queues, which will ensure that all messages // that came before it were also dispatched.
std::atomic<int> queues_not_done(0);
{
MutexLock cs(&crit_); for (Thread* queue : message_queues_) { if (!queue->IsProcessingMessagesForTesting()) { // If the queue is not processing messages, it can // be ignored. If we tried to post a message to it, it would be dropped // or ignored. continue;
}
queues_not_done.fetch_add(1); // Whether the task is processed, or the thread is simply cleared, // queues_not_done gets decremented.
absl::Cleanup sub = [&queues_not_done] { queues_not_done.fetch_sub(1); }; // Post delayed task instead of regular task to wait for all delayed tasks // that are ready for processing.
queue->PostDelayedTask([sub = std::move(sub)] {}, TimeDelta::Zero());
}
}
Thread* current = Thread::Current(); // Note: One of the message queues may have been on this thread, which is // why we can't synchronously wait for queues_not_done to go to 0; we need // to process messages as well. while (queues_not_done.load() > 0) { if (current) {
current->ProcessMessages(0);
}
}
}
if (thread) {
thread->EnsureIsCurrentTaskQueue();
} else {
Thread* current = CurrentThread(); if (current) { // The current thread is being cleared, e.g. as a result of // UnwrapCurrent() being called or when a thread is being stopped // (see PreRun()). This signals that the Thread instance is being detached // from the thread, which also means that TaskQueue::Current() must not // return a pointer to the Thread instance.
current->ClearCurrentTaskQueue();
}
}
void Thread::DoInit() { if (fInitialized_) { return;
}
fInitialized_ = true;
ThreadManager::Add(this);
}
void Thread::DoDestroy() { if (fDestroyed_) { return;
}
fDestroyed_ = true; // The signal is done from here to ensure // that it always gets called when the queue // is going away. if (ss_) {
ss_->SetMessageQueue(nullptr);
}
ThreadManager::Remove(this); // Clear.
CurrentTaskQueueSetter set_current(this);
messages_.clear();
delayed_messages_ = {};
}
int64_t cmsTotal = cmsWait;
int64_t cmsElapsed = 0;
int64_t msStart = TimeMillis();
int64_t msCurrent = msStart; while (true) { // Check for posted events
int64_t cmsDelayNext = kForever;
{ // All queue operations need to be locked, but nothing else in this loop // can happen while holding the `mutex_`.
MutexLock lock(&mutex_); // Check for delayed messages that have been triggered and calculate the // next trigger time. while (!delayed_messages_.empty()) { if (msCurrent < delayed_messages_.top().run_time_ms) {
cmsDelayNext =
TimeDiff(delayed_messages_.top().run_time_ms, msCurrent); break;
}
messages_.push_back(std::move(delayed_messages_.top().functor));
delayed_messages_.pop();
} // Pull a message off the message queue, if available. if (!messages_.empty()) {
absl::AnyInvocable<void() &&> task = std::move(messages_.front());
messages_.pop_front(); return task;
}
}
if (IsQuitting()) break;
// Which is shorter, the delay wait or the asked wait?
{ // Wait and multiplex in the meantime if (!ss_->Wait(cmsNext == kForever ? SocketServer::kForever
: TimeDelta::Millis(cmsNext), /*process_io=*/true)) return nullptr;
}
// Keep thread safe // Add to the priority queue. Gets sorted soonest first. // Signal for the multiplexer to return.
int64_t delay_ms = delay.RoundUpTo(TimeDelta::Millis(1)).ms<int>();
int64_t run_time_ms = TimeAfter(delay_ms);
{
MutexLock lock(&mutex_);
delayed_messages_.push({.delay_ms = delay_ms,
.run_time_ms = run_time_ms,
.message_number = delayed_next_num_,
.functor = std::move(task)}); // If this message queue processes 1 message every millisecond for 50 days, // we will wrap this number. Even then, only messages with identical times // will be misordered, and then only briefly. This is probably ok.
++delayed_next_num_;
RTC_DCHECK_NE(0, delayed_next_num_);
WakeUpSocketServer();
}
}
int Thread::GetDelay() {
MutexLock lock(&mutex_);
if (!messages_.empty()) return0;
if (!delayed_messages_.empty()) { int delay = TimeUntil(delayed_messages_.top().run_time_ms); if (delay < 0)
delay = 0; return delay;
}
return kForever;
}
void Thread::Dispatch(absl::AnyInvocable<void() &&> task) {
TRACE_EVENT0("webrtc", "Thread::Dispatch");
RTC_DCHECK_RUN_ON(this);
int64_t start_time = TimeMillis();
std::move(task)();
int64_t end_time = TimeMillis();
int64_t diff = TimeDiff(end_time, start_time); if (diff >= dispatch_warning_ms_) {
RTC_LOG(LS_INFO) << "Message to " << name() << " took " << diff
<< "ms to dispatch."; // To avoid log spew, move the warning limit to only give warning // for delays that are larger than the one observed.
dispatch_warning_ms_ = diff + 1;
}
}
#ifdefined(WEBRTC_WIN)
::Sleep(milliseconds); returntrue; #else // POSIX has both a usleep() and a nanosleep(), but the former is deprecated, // so we use nanosleep() even though it has greater precision than necessary. struct timespec ts;
ts.tv_sec = milliseconds / 1000;
ts.tv_nsec = (milliseconds % 1000) * 1000000; int ret = nanosleep(&ts, nullptr); if (ret != 0) {
RTC_LOG_ERR(LS_WARNING) << "nanosleep() returning early"; returnfalse;
} returntrue; #endif
}
name_ = std::string(name); if (obj) { // The %p specifier typically produce at most 16 hex digits, possibly with a // 0x prefix. But format is implementation defined, so add some margin. char buf[30];
snprintf(buf, sizeof(buf), " 0x%p", obj);
name_ += buf;
} returntrue;
}
RTC_DCHECK(!IsCurrent()); if (Current() && !Current()->blocking_calls_allowed_) {
RTC_LOG(LS_WARNING) << "Waiting for the thread to join, " "but blocking calls have been disallowed";
}
// Called by the ThreadManager when being set as the current thread. void Thread::EnsureIsCurrentTaskQueue() {
task_queue_registration_ =
std::make_unique<TaskQueueBase::CurrentTaskQueueSetter>(this);
}
// Called by the ThreadManager when being set as the current thread. void Thread::ClearCurrentTaskQueue() {
task_queue_registration_.reset();
}
// Returns true if no policies added or if there is at least one policy // that permits invocation to `target` thread. bool Thread::IsInvokeToThreadAllowed(Thread* target) { #if (!defined(NDEBUG) || RTC_DCHECK_IS_ON)
RTC_DCHECK_RUN_ON(this); if (!invoke_policy_enabled_) { returntrue;
} for (constauto* thread : allowed_threads_) { if (thread == target) { returntrue;
}
} returnfalse; #else returntrue; #endif
}
bool Thread::ProcessMessages(int cmsLoop) { // Using ProcessMessages with a custom clock for testing and a time greater // than 0 doesn't work, since it's not guaranteed to advance the custom // clock's time, and may get stuck in an infinite loop.
RTC_DCHECK(GetClockForTesting() == nullptr || cmsLoop == 0 ||
cmsLoop == kForever);
int64_t msEnd = (kForever == cmsLoop) ? 0 : TimeAfter(cmsLoop); int cmsNext = cmsLoop;
while (true) { #ifdefined(WEBRTC_MAC)
ScopedAutoReleasePool pool; #endif
absl::AnyInvocable<void() &&> task = Get(cmsNext); if (!task) return !IsQuitting();
Dispatch(std::move(task));
if (cmsLoop != kForever) {
cmsNext = static_cast<int>(TimeUntil(msEnd)); if (cmsNext < 0) returntrue;
}
}
}
#ifdefined(WEBRTC_WIN) if (need_synchronize_access) { // We explicitly ask for no rights other than synchronization. // This gives us the best chance of succeeding.
thread_ = OpenThread(SYNCHRONIZE, FALSE, GetCurrentThreadId()); if (!thread_) {
RTC_LOG_GLE(LS_ERROR) << "Unable to get handle to thread."; returnfalse;
}
thread_id_ = GetCurrentThreadId();
} #elifdefined(WEBRTC_POSIX)
thread_ = pthread_self(); #endif
owned_ = false;
thread_manager->SetCurrentThread(this); returntrue;
}
AutoThread::AutoThread()
: Thread(CreateDefaultSocketServer(), /*do_init=*/false) { if (!ThreadManager::Instance()->CurrentThread()) { // DoInit registers with ThreadManager. Do that only if we intend to // be Thread::Current(), otherwise ProcessAllMessageQueuesInternal // will post a message to a queue that no running thread is serving.
DoInit();
ThreadManager::Instance()->SetCurrentThread(this);
}
}
AutoSocketServerThread::AutoSocketServerThread(SocketServer* ss)
: Thread(ss, /*do_init=*/false) {
DoInit();
old_thread_ = ThreadManager::Instance()->CurrentThread(); // Temporarily set the current thread to nullptr so that we can keep checks // around that catch unintentional pointer overwrites.
ThreadManager::Instance()->SetCurrentThread(nullptr);
ThreadManager::Instance()->SetCurrentThread(this); if (old_thread_) {
ThreadManager::Remove(old_thread_);
}
}
AutoSocketServerThread::~AutoSocketServerThread() {
RTC_DCHECK(ThreadManager::Instance()->CurrentThread() == this); // Stop and destroy the thread before clearing it as the current thread. // Sometimes there are messages left in the Thread that will be // destroyed by DoDestroy, and sometimes the destructors of the message and/or // its contents rely on this thread still being set as the current thread.
Stop();
DoDestroy();
ThreadManager::Instance()->SetCurrentThread(nullptr);
ThreadManager::Instance()->SetCurrentThread(old_thread_); if (old_thread_) {
ThreadManager::Add(old_thread_);
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.