blob: 1e159d89a3b38bd3368a7827053eb2d7efbd6a0c [file] [log] [blame]
// Copyright 2024 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "patchpanel/lifeline_fd_service.h"
#include <memory>
#include <utility>
#include <vector>
#include <base/functional/bind.h>
#include <base/functional/callback.h>
#include <base/logging.h>
namespace patchpanel {
LifelineFDService::LifelineFDService() = default;
LifelineFDService::~LifelineFDService() = default;
LifelineFDService::LifelineFDInfo::LifelineFDInfo(
base::ScopedFD lifeline_fd,
base::OnceClosure on_lifeline_fd_event,
std::unique_ptr<base::FileDescriptorWatcher::Controller> watcher)
: lifeline_fd(std::move(lifeline_fd)),
on_lifeline_fd_event(std::move(on_lifeline_fd_event)),
watcher(std::move(watcher)) {}
std::vector<int> LifelineFDService::get_lifeline_fds_for_testing() {
std::vector<int> fds;
for (const auto& [fd, _] : lifeline_fds_) {
fds.push_back(fd);
}
return fds;
}
base::ScopedClosureRunner LifelineFDService::AddLifelineFD(
base::ScopedFD lifeline_fd, base::OnceClosure on_lifeline_fd_event) {
if (!lifeline_fd.is_valid()) {
LOG(ERROR) << __func__ << ": Invalid client file descriptor";
return base::ScopedClosureRunner();
}
int fd = lifeline_fd.get();
std::unique_ptr<base::FileDescriptorWatcher::Controller> watcher =
base::FileDescriptorWatcher::WatchReadable(
fd, base::BindRepeating(&LifelineFDService::DeleteLifelineFD,
// The callback will not outlive the object.
base::Unretained(this), /*is_autoclose=*/true,
fd));
lifeline_fds_.try_emplace(fd, std::move(lifeline_fd),
std::move(on_lifeline_fd_event),
std::move(watcher));
return base::ScopedClosureRunner(
base::BindOnce(&LifelineFDService::DeleteLifelineFD,
weak_factory_.GetWeakPtr(), /*is_autoclose=*/false, fd));
}
void LifelineFDService::DeleteLifelineFD(bool is_autoclose, int lifeline_fd) {
// Check if |lifeline_fd| is still registered. Double DeleteLifelineFD calls
// for the same fd is expected to happen when the local service that
// registered the client file descriptor deletes its ScopedClosureRunner after
// being notified.
auto it = lifeline_fds_.find(lifeline_fd);
if (it == lifeline_fds_.end()) {
return;
}
// Running |on_lifeline_fd_event| may cause DeleteLifelineFD to be called a
// second time while the first call is still on the stack. It is therefore
// necessary to ensure that the map entry is removed before running
// |on_lifeline_fd_event|. In addition, LifelineFDInfo must be preserved and
// only released after fully running |on_lifeline_fd_event|.
LifelineFDInfo lifeline_fd_info = std::move(it->second);
lifeline_fds_.erase(it);
// Only run |on_lifeline_fd_event| if |lifeline_fd| was remotely invalidated.
if (is_autoclose) {
std::move(lifeline_fd_info.on_lifeline_fd_event).Run();
}
}
} // namespace patchpanel