blob: e69a6112b82c5bd5746cde310f4a88eba5a330c5 [file] [log] [blame]
// Copyright 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 "remoting/host/zombie_host_detector.h"
#include "base/check.h"
#include "base/logging.h"
#include "net/base/network_change_notifier.h"
namespace {
bool IsMaxDurationExceeded(base::TimeTicks event_time,
base::TimeDelta max_duration,
const std::string& event_name) {
if (event_time.is_null()) {
VLOG(1) << "Last " << event_name << " is null.";
return false;
}
base::TimeDelta event_duration = base::TimeTicks::Now() - event_time;
DCHECK_GE(event_duration, base::TimeDelta()) << "Event duration is negative.";
bool is_exceeded = event_duration > max_duration;
if (is_exceeded) {
LOG(ERROR) << "Last " << event_name << " happened " << event_duration
<< " ago, exceeding max duration of " << max_duration;
} else {
VLOG(1) << "Last " << event_name << " happened " << event_duration
<< " ago.";
}
return is_exceeded;
}
} // namespace
namespace remoting {
// static
constexpr base::TimeDelta ZombieHostDetector::kZombieStateDetectionInterval;
constexpr base::TimeDelta ZombieHostDetector::kMaxHeartbeatInterval;
constexpr base::TimeDelta ZombieHostDetector::kMaxSignalingActiveInterval;
ZombieHostDetector::ZombieHostDetector(
base::OnceClosure on_zombie_state_detected) {
DCHECK(on_zombie_state_detected);
on_zombie_state_detected_ = std::move(on_zombie_state_detected);
}
ZombieHostDetector::~ZombieHostDetector() = default;
void ZombieHostDetector::Start() {
check_zombie_state_timer_.Start(FROM_HERE, kZombieStateDetectionInterval,
this, &ZombieHostDetector::CheckZombieState);
}
void ZombieHostDetector::OnHeartbeatSent() {
last_heartbeat_time_ = base::TimeTicks::Now();
}
void ZombieHostDetector::OnSignalingActive() {
last_signaling_active_time_ = base::TimeTicks::Now();
}
base::TimeTicks ZombieHostDetector::GetNextDetectionTime() const {
return check_zombie_state_timer_.desired_run_time();
}
void ZombieHostDetector::CheckZombieState() {
VLOG(1) << "Detecting zombie state...";
if (net::NetworkChangeNotifier::GetConnectionType() ==
net::NetworkChangeNotifier::CONNECTION_NONE) {
// The host shouldn't be considered in zombie state if it has no connection.
VLOG(1) << "No internet connectivity. Skipping zombie state check...";
previously_offline_ = true;
return;
}
if (previously_offline_) {
// If the host was previously offline, heartbeat/signaling might not have
// happened at this point due to backoff. Reset them to |now| to allow them
// to come through.
previously_offline_ = false;
VLOG(1) << "Host is going online. Previous heartbeat time: "
<< last_heartbeat_time_ << ", previous signaling active time: "
<< last_signaling_active_time_ << ". These will be reset to |now|.";
// Don't reset if they are null, which happens when the first
// heartbeat/signaling attempt has not succeeded yet.
if (!last_heartbeat_time_.is_null()) {
OnHeartbeatSent();
}
if (!last_signaling_active_time_.is_null()) {
OnSignalingActive();
}
return;
}
if (IsMaxDurationExceeded(last_heartbeat_time_, kMaxHeartbeatInterval,
"heartbeat") ||
IsMaxDurationExceeded(last_signaling_active_time_,
kMaxSignalingActiveInterval,
"signaling activity")) {
LOG(ERROR) << "Host zombie state detected.";
check_zombie_state_timer_.Stop();
std::move(on_zombie_state_detected_).Run();
return;
}
VLOG(1) << "No zombie state detected.";
}
} // namespace remoting