Commit 9c8b3a27 authored by Henrik Boström's avatar Henrik Boström Committed by Commit Bot

[Adaptation] Implement ThermalResource.

The ThermalResource reports kOveruse or kUnderuse every 10 seconds
while it has a registered listener and the DeviceThermalMeasurement is
known. Because OnThermalMeasurement() only happens when the thermal
state changes, repeated kOveruse is needed to adapt multiple steps.

Based on [1] and manual observations, we do not want to adapt if
thermals are kNominal or kFair so we map these to kUnderuse. But if
thermals are kSerious or kCritical this is a strong signal from the OS
that "corrective action" or "immediate corrective action" is needed.

It can easily take a minute before the thermal state changes after load
distribution has changed, so the effects of ThermalResource is likely to
result in either maximally adapted or not adapted at all. The repeated
interval of 10 seconds was somewhat arbitrarily chosen but was chosen as
a tradeoff between giving the OS time to measure the new load and not
making the resource too spammy.

This CL implements and tests the resource, but does not wire it up to
the webrtc::PeerConnection or
RTCPeerConnectionHandler::OnThermalMeasurement(). This will be done in a
separate CL.

[1] https://developer.apple.com/library/archive/documentation/Performance/Conceptual/power_efficiency_guidelines_osx/RespondToThermalStateChanges.html

Bug: chromium:1094844
Change-Id: I7dee0842ba786babbacb7e9d19acb58a1c9daa05
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2247769Reviewed-by: default avatarKinuko Yasuda <kinuko@chromium.org>
Reviewed-by: default avatarGuido Urdaneta <guidou@chromium.org>
Reviewed-by: default avatarMirko Bonadei <mbonadei@chromium.org>
Commit-Queue: Henrik Boström <hbos@chromium.org>
Cr-Commit-Position: refs/heads/master@{#780189}
parent a2d572a2
...@@ -403,6 +403,7 @@ jumbo_source_set("unit_tests") { ...@@ -403,6 +403,7 @@ jumbo_source_set("unit_tests") {
"peerconnection/rtc_rtp_sender_impl_test.cc", "peerconnection/rtc_rtp_sender_impl_test.cc",
"peerconnection/rtc_rtp_transceiver_impl_test.cc", "peerconnection/rtc_rtp_transceiver_impl_test.cc",
"peerconnection/rtc_sctp_transport_test.cc", "peerconnection/rtc_sctp_transport_test.cc",
"peerconnection/thermal_resource_test.cc",
"peerconnection/transceiver_state_surfacer_test.cc", "peerconnection/transceiver_state_surfacer_test.cc",
"peerconnection/webrtc_audio_renderer_test.cc", "peerconnection/webrtc_audio_renderer_test.cc",
"peerconnection/webrtc_media_stream_track_adapter_map_test.cc", "peerconnection/webrtc_media_stream_track_adapter_map_test.cc",
......
...@@ -155,6 +155,8 @@ blink_modules_sources("peerconnection") { ...@@ -155,6 +155,8 @@ blink_modules_sources("peerconnection") {
"rtc_void_request_promise_impl.h", "rtc_void_request_promise_impl.h",
"rtc_void_request_script_promise_resolver_impl.cc", "rtc_void_request_script_promise_resolver_impl.cc",
"rtc_void_request_script_promise_resolver_impl.h", "rtc_void_request_script_promise_resolver_impl.h",
"thermal_resource.cc",
"thermal_resource.h",
"transceiver_state_surfacer.cc", "transceiver_state_surfacer.cc",
"transceiver_state_surfacer.h", "transceiver_state_surfacer.h",
"web_rtc_stats_report_callback_resolver.cc", "web_rtc_stats_report_callback_resolver.cc",
......
// Copyright (c) 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/blink/renderer/modules/peerconnection/thermal_resource.h"
#include "third_party/webrtc/rtc_base/ref_counted_object.h"
namespace blink {
namespace {
const int kReportIntervalSeconds = 10;
} // namespace
// static
scoped_refptr<ThermalResource> ThermalResource::Create(
scoped_refptr<base::SequencedTaskRunner> task_runner) {
return new rtc::RefCountedObject<ThermalResource>(std::move(task_runner));
}
ThermalResource::ThermalResource(
scoped_refptr<base::SequencedTaskRunner> task_runner)
: task_runner_(std::move(task_runner)) {}
void ThermalResource::OnThermalMeasurement(
base::PowerObserver::DeviceThermalState measurement) {
base::AutoLock auto_lock(lock_);
measurement_ = measurement;
++measurement_id_;
ReportMeasurementWhileHoldingLock(measurement_id_);
}
std::string ThermalResource::Name() const {
return "ThermalResource";
}
void ThermalResource::SetResourceListener(webrtc::ResourceListener* listener) {
base::AutoLock auto_lock(lock_);
listener_ = listener;
if (listener_ &&
measurement_ != base::PowerObserver::DeviceThermalState::kUnknown) {
ReportMeasurementWhileHoldingLock(measurement_id_);
}
}
void ThermalResource::ReportMeasurement(size_t measurement_id) {
base::AutoLock auto_lock(lock_);
ReportMeasurementWhileHoldingLock(measurement_id);
}
// EXCLUSIVE_LOCKS_REQUIRED(&lock_)
void ThermalResource::ReportMeasurementWhileHoldingLock(size_t measurement_id) {
// Stop repeating measurements if the measurement was invalidated or we don't
// have a listtener.
if (measurement_id != measurement_id_ || !listener_)
return;
switch (measurement_) {
case base::PowerObserver::DeviceThermalState::kUnknown:
// Stop repeating measurements.
return;
case base::PowerObserver::DeviceThermalState::kNominal:
case base::PowerObserver::DeviceThermalState::kFair:
listener_->OnResourceUsageStateMeasured(
this, webrtc::ResourceUsageState::kUnderuse);
break;
case base::PowerObserver::DeviceThermalState::kSerious:
case base::PowerObserver::DeviceThermalState::kCritical:
listener_->OnResourceUsageStateMeasured(
this, webrtc::ResourceUsageState::kOveruse);
break;
}
// Repeat the reporting every 10 seconds until a new measurement is made or
// the listener is unregistered.
task_runner_->PostDelayedTask(
FROM_HERE,
base::BindOnce(&ThermalResource::ReportMeasurement,
scoped_refptr<ThermalResource>(this), measurement_id),
base::TimeDelta::FromSeconds(kReportIntervalSeconds));
}
} // namespace blink
// Copyright (c) 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_PEERCONNECTION_THERMAL_RESOURCE_H_
#define THIRD_PARTY_BLINK_RENDERER_MODULES_PEERCONNECTION_THERMAL_RESOURCE_H_
#include "base/memory/scoped_refptr.h"
#include "base/power_monitor/power_observer.h"
#include "base/single_thread_task_runner.h"
#include "base/synchronization/lock.h"
#include "base/thread_annotations.h"
#include "third_party/blink/renderer/modules/modules_export.h"
#include "third_party/webrtc/api/adaptation/resource.h"
namespace blink {
// The ThermalResource reports kOveruse or kUnderuse every 10 seconds(*) while
// it has a registered listener and the DeviceThermalMeasurement is known.
// Because OnThermalMeasurement() only happens when the thermal state changes,
// repeated kOveruse is needed to adapt multiple steps.
//
// Based on [1] and manual observations, we do not want to adapt if thermals are
// kNominal or kFair so we map these to kUnderuse. But if thermals are kSerious
// or kCritical this is a strong signal from the OS that "corrective action" or
// "immediate corrective action" is needed.
//
// (*) It can easily take a minute before the thermal state changes after load
// distribution has changed, so the effects of ThermalResource is likely to
// result in either maximally adapted or not adapted at all. The repeated
// interval of 10 seconds was somewhat arbitrarily chosen but was chosen as a
// tradeoff between giving the OS time to measure the new load and not making
// the resource too spammy.
//
// [1]
// https://developer.apple.com/library/archive/documentation/Performance/Conceptual/power_efficiency_guidelines_osx/RespondToThermalStateChanges.html
class MODULES_EXPORT ThermalResource : public webrtc::Resource {
public:
static scoped_refptr<ThermalResource> Create(
scoped_refptr<base::SequencedTaskRunner> task_runner);
explicit ThermalResource(
scoped_refptr<base::SequencedTaskRunner> task_runner);
~ThermalResource() override = default;
void OnThermalMeasurement(
base::PowerObserver::DeviceThermalState measurement);
// webrtc::Resource implementation.
std::string Name() const override;
void SetResourceListener(webrtc::ResourceListener* listener) override;
private:
void ReportMeasurement(size_t measurement_id);
void ReportMeasurementWhileHoldingLock(size_t measurement_id)
EXCLUSIVE_LOCKS_REQUIRED(&lock_);
const scoped_refptr<base::SequencedTaskRunner> task_runner_;
base::Lock lock_;
webrtc::ResourceListener* listener_ GUARDED_BY(&lock_) = nullptr;
base::PowerObserver::DeviceThermalState measurement_ GUARDED_BY(&lock_) =
base::PowerObserver::DeviceThermalState::kUnknown;
size_t measurement_id_ GUARDED_BY(&lock_) = 0u;
};
} // namespace blink
#endif // THIRD_PARTY_BLINK_RENDERER_MODULES_PEERCONNECTION_THERMAL_RESOURCE_H_
// Copyright (c) 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/blink/renderer/modules/peerconnection/thermal_resource.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/renderer/platform/testing/testing_platform_support_with_mock_scheduler.h"
#include "third_party/webrtc/api/adaptation/resource.h"
namespace blink {
namespace {
const int64_t kReportIntervalMs = 10000;
class FakeResourceListener : public webrtc::ResourceListener {
public:
~FakeResourceListener() override = default;
void OnResourceUsageStateMeasured(
rtc::scoped_refptr<webrtc::Resource> resource,
webrtc::ResourceUsageState usage_state) override {
latest_measurement_ = usage_state;
++measurement_count_;
}
size_t measurement_count() const { return measurement_count_; }
webrtc::ResourceUsageState latest_measurement() const {
DCHECK(measurement_count_);
return latest_measurement_;
}
private:
size_t measurement_count_ = 0u;
webrtc::ResourceUsageState latest_measurement_ =
webrtc::ResourceUsageState::kUnderuse;
};
class ThermalResourceTest : public ::testing::Test {
public:
ThermalResourceTest()
: task_runner_(platform_->test_task_runner()),
resource_(ThermalResource::Create(task_runner_)) {}
void TearDown() override {
// Give in-flight tasks a chance to run before shutdown.
resource_->SetResourceListener(nullptr);
task_runner_->FastForwardBy(
base::TimeDelta::FromMilliseconds(kReportIntervalMs));
}
protected:
ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
platform_;
// Tasks run on the test thread with fake time, use FastForwardBy() to
// advance time and execute delayed tasks.
scoped_refptr<base::TestMockTimeTaskRunner> task_runner_;
scoped_refptr<ThermalResource> resource_;
FakeResourceListener listener_;
};
} // namespace
TEST_F(ThermalResourceTest, NoMeasurementsByDefault) {
resource_->SetResourceListener(&listener_);
EXPECT_EQ(0u, listener_.measurement_count());
task_runner_->FastForwardBy(
base::TimeDelta::FromMilliseconds(kReportIntervalMs));
EXPECT_EQ(0u, listener_.measurement_count());
}
TEST_F(ThermalResourceTest, NominalTriggersUnderuse) {
resource_->SetResourceListener(&listener_);
resource_->OnThermalMeasurement(
base::PowerObserver::DeviceThermalState::kNominal);
EXPECT_EQ(1u, listener_.measurement_count());
EXPECT_EQ(webrtc::ResourceUsageState::kUnderuse,
listener_.latest_measurement());
}
TEST_F(ThermalResourceTest, FairTriggersUnderuse) {
resource_->SetResourceListener(&listener_);
resource_->OnThermalMeasurement(
base::PowerObserver::DeviceThermalState::kFair);
EXPECT_EQ(1u, listener_.measurement_count());
EXPECT_EQ(webrtc::ResourceUsageState::kUnderuse,
listener_.latest_measurement());
}
TEST_F(ThermalResourceTest, SeriousTriggersOveruse) {
resource_->SetResourceListener(&listener_);
resource_->OnThermalMeasurement(
base::PowerObserver::DeviceThermalState::kSerious);
EXPECT_EQ(1u, listener_.measurement_count());
EXPECT_EQ(webrtc::ResourceUsageState::kOveruse,
listener_.latest_measurement());
}
TEST_F(ThermalResourceTest, CriticalTriggersOveruse) {
resource_->SetResourceListener(&listener_);
resource_->OnThermalMeasurement(
base::PowerObserver::DeviceThermalState::kCritical);
EXPECT_EQ(1u, listener_.measurement_count());
EXPECT_EQ(webrtc::ResourceUsageState::kOveruse,
listener_.latest_measurement());
}
TEST_F(ThermalResourceTest, UnknownDoesNotTriggerUsage) {
resource_->SetResourceListener(&listener_);
resource_->OnThermalMeasurement(
base::PowerObserver::DeviceThermalState::kUnknown);
EXPECT_EQ(0u, listener_.measurement_count());
}
TEST_F(ThermalResourceTest, MeasurementsRepeatEvery10Seconds) {
resource_->SetResourceListener(&listener_);
resource_->OnThermalMeasurement(
base::PowerObserver::DeviceThermalState::kSerious);
size_t expected_count = listener_.measurement_count();
// First Interval.
// No new measurement if we advance less than the interval.
task_runner_->FastForwardBy(
base::TimeDelta::FromMilliseconds(kReportIntervalMs - 1));
EXPECT_EQ(expected_count, listener_.measurement_count());
// When the interval is reached, expect a new measurement.
task_runner_->FastForwardBy(base::TimeDelta::FromMilliseconds(1));
++expected_count;
EXPECT_EQ(expected_count, listener_.measurement_count());
// Second Interval.
// No new measurement if we advance less than the interval.
task_runner_->FastForwardBy(
base::TimeDelta::FromMilliseconds(kReportIntervalMs - 1));
EXPECT_EQ(expected_count, listener_.measurement_count());
// When the interval is reached, expect a new measurement.
task_runner_->FastForwardBy(base::TimeDelta::FromMilliseconds(1));
++expected_count;
EXPECT_EQ(expected_count, listener_.measurement_count());
// Third Interval.
// No new measurement if we advance less than the interval.
task_runner_->FastForwardBy(
base::TimeDelta::FromMilliseconds(kReportIntervalMs - 1));
EXPECT_EQ(expected_count, listener_.measurement_count());
// When the interval is reached, expect a new measurement.
task_runner_->FastForwardBy(base::TimeDelta::FromMilliseconds(1));
++expected_count;
EXPECT_EQ(expected_count, listener_.measurement_count());
}
TEST_F(ThermalResourceTest, NewMeasurementInvalidatesInFlightRepetition) {
resource_->SetResourceListener(&listener_);
resource_->OnThermalMeasurement(
base::PowerObserver::DeviceThermalState::kSerious);
task_runner_->FastForwardBy(
base::TimeDelta::FromMilliseconds(kReportIntervalMs));
// We are repeatedly kOveruse.
EXPECT_EQ(2u, listener_.measurement_count());
EXPECT_EQ(webrtc::ResourceUsageState::kOveruse,
listener_.latest_measurement());
// Fast-forward half an interval. The repeated measurement is still in-flight.
task_runner_->FastForwardBy(
base::TimeDelta::FromMilliseconds(kReportIntervalMs / 2));
EXPECT_EQ(2u, listener_.measurement_count());
EXPECT_EQ(webrtc::ResourceUsageState::kOveruse,
listener_.latest_measurement());
// Trigger kUnderuse.
resource_->OnThermalMeasurement(
base::PowerObserver::DeviceThermalState::kNominal);
EXPECT_EQ(3u, listener_.measurement_count());
EXPECT_EQ(webrtc::ResourceUsageState::kUnderuse,
listener_.latest_measurement());
// Fast-forward another half an interval, giving the previous in-flight task
// a chance to run. No new measurement is expected.
task_runner_->FastForwardBy(
base::TimeDelta::FromMilliseconds(kReportIntervalMs / 2));
EXPECT_EQ(3u, listener_.measurement_count());
EXPECT_EQ(webrtc::ResourceUsageState::kUnderuse,
listener_.latest_measurement());
// Once more, and the repetition of kUnderuse should be observed (one interval
// has passed since the OnThermalMeasurement).
task_runner_->FastForwardBy(
base::TimeDelta::FromMilliseconds(kReportIntervalMs / 2));
EXPECT_EQ(4u, listener_.measurement_count());
EXPECT_EQ(webrtc::ResourceUsageState::kUnderuse,
listener_.latest_measurement());
}
TEST_F(ThermalResourceTest, UnknownStopsRepeatedMeasurements) {
resource_->SetResourceListener(&listener_);
resource_->OnThermalMeasurement(
base::PowerObserver::DeviceThermalState::kSerious);
task_runner_->FastForwardBy(
base::TimeDelta::FromMilliseconds(kReportIntervalMs));
// The measurement is repeating.
EXPECT_EQ(2u, listener_.measurement_count());
resource_->OnThermalMeasurement(
base::PowerObserver::DeviceThermalState::kUnknown);
task_runner_->FastForwardBy(
base::TimeDelta::FromMilliseconds(kReportIntervalMs));
// No more measurements.
EXPECT_EQ(2u, listener_.measurement_count());
}
TEST_F(ThermalResourceTest, UnregisteringStopsRepeatedMeasurements) {
resource_->SetResourceListener(&listener_);
resource_->OnThermalMeasurement(
base::PowerObserver::DeviceThermalState::kSerious);
task_runner_->FastForwardBy(
base::TimeDelta::FromMilliseconds(kReportIntervalMs));
// The measurement is repeating.
EXPECT_EQ(2u, listener_.measurement_count());
resource_->SetResourceListener(nullptr);
// If repeating tasks were not stopped, this line would block forever.
task_runner_->FastForwardUntilNoTasksRemain();
// No more measurements.
EXPECT_EQ(2u, listener_.measurement_count());
}
TEST_F(ThermalResourceTest, RegisteringLateTriggersRepeatedMeasurements) {
resource_->OnThermalMeasurement(
base::PowerObserver::DeviceThermalState::kSerious);
task_runner_->FastForwardBy(
base::TimeDelta::FromMilliseconds(kReportIntervalMs));
EXPECT_EQ(0u, listener_.measurement_count());
// Registering triggers kOveruse.
resource_->SetResourceListener(&listener_);
EXPECT_EQ(1u, listener_.measurement_count());
EXPECT_EQ(webrtc::ResourceUsageState::kOveruse,
listener_.latest_measurement());
// The measurement is repeating.
task_runner_->FastForwardBy(
base::TimeDelta::FromMilliseconds(kReportIntervalMs));
EXPECT_EQ(2u, listener_.measurement_count());
}
} // namespace blink
...@@ -21,6 +21,7 @@ component("webrtc_component") { ...@@ -21,6 +21,7 @@ component("webrtc_component") {
"//third_party/webrtc/api:rtc_stats_api", "//third_party/webrtc/api:rtc_stats_api",
"//third_party/webrtc/api:rtp_parameters", "//third_party/webrtc/api:rtp_parameters",
"//third_party/webrtc/api:scoped_refptr", "//third_party/webrtc/api:scoped_refptr",
"//third_party/webrtc/api/adaptation:resource_adaptation_api",
"//third_party/webrtc/api/audio:aec3_config", "//third_party/webrtc/api/audio:aec3_config",
"//third_party/webrtc/api/audio:aec3_config_json", "//third_party/webrtc/api/audio:aec3_config_json",
"//third_party/webrtc/api/audio:aec3_factory", "//third_party/webrtc/api/audio:aec3_factory",
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment