/* * Copyright 2016 The WebRTC Project Authors. All rights reserved. * * Use of this source code is governed by a BSD-style license * that can be found in the LICENSE file in the root of the source * tree. An additional intellectual property rights grant can be found * in the file PATENTS. All contributing project authors may * be found in the AUTHORS file in the root of the source tree.
*/
// Mozilla - this file should not be included in Mozilla builds until // win32k API usage is removed. This was once done in Bug 1395259, but // the upstreaming attempt stalled. Until win32k usage is officially // removed upstream, we have reverted to upstream's version of the file // (to reduce or elminate merge conflicts), and a static assert is // placed here to ensure this file isn't accidentally included in the // Mozilla build.
static_assert(false, "This file should not be built, see Bug 1797161.");
#include"rtc_base/task_queue_win.h"
// clang-format off // clang formating would change include order.
// Include winsock2.h before including <windows.h> to maintain consistency with // win32.h. To include win32.h directly, it must be broken out into its own // build target. #include <winsock2.h> #include <windows.h> #include <sal.h> // Must come after windows headers. #include <mmsystem.h> // Must come after windows headers. // clang-format on #include <string.h>
// Required by priority_queue::pop().
DelayedTaskInfo& operator=(DelayedTaskInfo&& other) = default;
// See below for why this method is const. void Run() const {
RTC_DCHECK(task_);
std::move(task_)();
}
Timestamp due_time() const { return due_time_; }
private:
Timestamp due_time_ = Timestamp::Zero();
// `task` needs to be mutable because std::priority_queue::top() returns // a const reference and a key in an ordered queue must not be changed. // There are two basic workarounds, one using const_cast, which would also // make the key (`due_time`), non-const and the other is to make the non-key // (`task`), mutable. // Because of this, the `task` variable is made private and can only be // mutated by calling the `Run()` method. mutable absl::AnyInvocable<void() &&> task_;
};
class MultimediaTimer { public: // Note: We create an event that requires manual reset.
MultimediaTimer() : event_(::CreateEvent(nullptr, true, false, nullptr)) {}
void Cancel() { if (timer_id_) {
::timeKillEvent(timer_id_);
timer_id_ = 0;
} // Now that timer is killed and not able to set the event, reset the event. // Doing it in opposite order is racy because event may be set between // event was reset and timer is killed leaving MultimediaTimer in surprising // state where both event is set and timer is canceled.
::ResetEvent(event_);
}
MultimediaTimer timer_; // Since priority_queue<> by defult orders items in terms of // largest->smallest, using std::less<>, and we want smallest->largest, // we would like to use std::greater<> here.
std::priority_queue<DelayedTaskInfo,
std::vector<DelayedTaskInfo>,
std::greater<DelayedTaskInfo>>
timer_tasks_;
UINT_PTR timer_id_ = 0;
rtc::PlatformThread thread_;
Mutex pending_lock_;
std::queue<absl::AnyInvocable<void() &&>> pending_
RTC_GUARDED_BY(pending_lock_);
HANDLE in_queue_;
};
auto* task_info = new DelayedTaskInfo(delay, std::move(task));
RTC_CHECK(thread_.GetHandle() != std::nullopt); if (!::PostThreadMessage(GetThreadId(*thread_.GetHandle()),
WM_QUEUE_DELAYED_TASK, 0, reinterpret_cast<LPARAM>(task_info))) { delete task_info;
}
}
void TaskQueueWin::RunPendingTasks() { while (true) {
absl::AnyInvocable<void() &&> task;
{
MutexLock lock(&pending_lock_); if (pending_.empty()) break;
task = std::move(pending_.front());
pending_.pop();
}
std::move(task)();
}
}
void TaskQueueWin::RunThreadMain() {
CurrentTaskQueueSetter set_current(this);
HANDLE handles[2] = {*timer_.event_for_wait(), in_queue_}; while (true) { // Make sure we do an alertable wait as that's required to allow APCs to run // (e.g. required for InitializeQueueThread and stopping the thread in // PlatformThread).
DWORD result = ::MsgWaitForMultipleObjectsEx(
arraysize(handles), handles, INFINITE, QS_ALLEVENTS, MWMO_ALERTABLE);
RTC_CHECK_NE(WAIT_FAILED, result); if (result == (WAIT_OBJECT_0 + 2)) { // There are messages in the message queue that need to be handled. if (!ProcessQueuedMessages()) break;
}
if (result == WAIT_OBJECT_0 ||
(!timer_tasks_.empty() &&
::WaitForSingleObject(*timer_.event_for_wait(), 0) == WAIT_OBJECT_0)) { // The multimedia timer was signaled.
timer_.Cancel();
RunDueTasks();
ScheduleNextTimer();
}
if (result == (WAIT_OBJECT_0 + 1)) {
::ResetEvent(in_queue_);
RunPendingTasks();
}
} // Ensure remaining deleted tasks are destroyed with Current() set up to this // task queue.
std::queue<absl::AnyInvocable<void() &&>> pending;
{
MutexLock lock(&pending_lock_);
pending_.swap(pending);
}
pending = {}; #if RTC_DCHECK_IS_ON
MutexLock lock(&pending_lock_);
RTC_DCHECK(pending_.empty()); #endif
}
bool TaskQueueWin::ProcessQueuedMessages() {
MSG msg = {}; // To protect against overly busy message queues, we limit the time // we process tasks to a few milliseconds. If we don't do that, there's // a chance that timer tasks won't ever run. static constexpr TimeDelta kMaxTaskProcessingTime = TimeDelta::Millis(500);
Timestamp start = CurrentTime(); while (::PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE) &&
msg.message != WM_QUIT) { if (!msg.hwnd) { switch (msg.message) { case WM_QUEUE_DELAYED_TASK: {
std::unique_ptr<DelayedTaskInfo> info( reinterpret_cast<DelayedTaskInfo*>(msg.lParam)); bool need_to_schedule_timers =
timer_tasks_.empty() ||
timer_tasks_.top().due_time() > info->due_time();
timer_tasks_.push(std::move(*info)); if (need_to_schedule_timers) {
CancelTimers();
ScheduleNextTimer();
} break;
} case WM_TIMER: {
RTC_DCHECK_EQ(timer_id_, msg.wParam);
::KillTimer(nullptr, msg.wParam);
timer_id_ = 0;
RunDueTasks();
ScheduleNextTimer(); break;
} default:
RTC_DCHECK_NOTREACHED(); break;
}
} else {
::TranslateMessage(&msg);
::DispatchMessage(&msg);
}
void TaskQueueWin::RunDueTasks() {
RTC_DCHECK(!timer_tasks_.empty());
Timestamp now = CurrentTime(); do { constauto& top = timer_tasks_.top(); if (top.due_time() > now) break;
top.Run();
timer_tasks_.pop();
} while (!timer_tasks_.empty());
}
void TaskQueueWin::ScheduleNextTimer() {
RTC_DCHECK_EQ(timer_id_, 0); if (timer_tasks_.empty()) return;
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.