blob: e9657e5adbe0e68a1cfc50a70acccc7754a8a457 [file] [log] [blame]
// Copyright 2018 The Chromium OS 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 "diagnostics/diagnosticsd/diagnosticsd_ec_event_service.h"
#include <errno.h>
#include <fcntl.h>
#include <poll.h>
#include <sys/eventfd.h>
#include <unistd.h>
#include <utility>
#include <base/bind.h>
#include <base/files/file_path.h>
#include <base/logging.h>
#include <base/memory/ref_counted.h>
#include <base/posix/eintr_wrapper.h>
namespace diagnostics {
namespace internal {
// This is the background ("monitoring") thread delegate used by
// |DiagnosticsdEcEventService|.
class EcEventMonitoringThreadDelegate final
: public base::DelegateSimpleThread::Delegate {
public:
using OnEventAvailableCallback =
base::RepeatingCallback<void(const DiagnosticsdEcEventService::EcEvent&)>;
// |DiagnosticsdEcEventService| guarantees that the unowned pointer and file
// descriptors outlive this delegate. This delegate will post
// |on_event_available_callback| on the |foreground_task_runner| when an EC
// event is available and it will post |on_shutdown_callback| on the
// |foreground_task_runner| when it is shutting down.
EcEventMonitoringThreadDelegate(
int event_fd,
int16_t event_fd_events,
int shutdown_fd,
scoped_refptr<base::SequencedTaskRunner> foreground_task_runner,
OnEventAvailableCallback on_event_available_callback,
base::OnceClosure on_shutdown_callback)
: foreground_task_runner_(foreground_task_runner),
on_event_available_callback_(std::move(on_event_available_callback)),
on_shutdown_callback_(std::move(on_shutdown_callback)) {
fds[0] = pollfd{event_fd, event_fd_events, 0};
fds[1] = pollfd{shutdown_fd, POLLIN, 0};
}
~EcEventMonitoringThreadDelegate() override = default;
void Run() override {
while (true) {
int retval =
HANDLE_EINTR(poll(fds, 2 /* nfds */, -1 /* infinite timeout */));
if (retval < 0) {
if (errno == EINTR) {
// Non-critical error, we must retry.
continue;
}
PLOG(ERROR)
<< "EC event poll error. Shutting down EC monitoring thread";
break;
}
if (fds[1].events & fds[1].revents) {
// Exit: the main thread requested our shutdown by writing data into
// |shutdown_fd_|.
break;
}
if ((fds[0].revents & POLLERR) || (fds[1].revents & POLLERR)) {
LOG(ERROR) << "EC event POLLERR poll error. Shutting down EC"
" monitoring thread";
break;
}
if ((fds[0].events & fds[0].revents) == 0) {
// No data available for reading from |event_fd_|, so proceed to poll()
// to wait for new events.
continue;
}
DiagnosticsdEcEventService::EcEvent ec_event;
if (HANDLE_EINTR(read(fds[0].fd, &ec_event, sizeof(ec_event))) > 0) {
foreground_task_runner_->PostTask(
FROM_HERE, base::BindOnce(on_event_available_callback_, ec_event));
if (lseek(fds[0].fd, 0, SEEK_SET) == -1) {
VPLOG(2) << "Unable to lseek event file";
}
}
}
foreground_task_runner_->PostTask(FROM_HERE,
std::move(on_shutdown_callback_));
}
private:
// Pollfd array, where |fds[0]| is a real sysfs fd and |fds[1]| is a fake fd
// used to shutdown this monitoring thread delegate.
// Not owned.
pollfd fds[2];
// The |SequencedTaskRunner| this object is posting tasks to. It is accessed
// from the monitoring thread.
scoped_refptr<base::SequencedTaskRunner> foreground_task_runner_;
OnEventAvailableCallback on_event_available_callback_;
base::OnceClosure on_shutdown_callback_;
};
} // namespace internal
DiagnosticsdEcEventService::DiagnosticsdEcEventService(Delegate* delegate)
: message_loop_(base::MessageLoop::current()), delegate_(delegate) {
DCHECK(message_loop_);
DCHECK(delegate_);
}
DiagnosticsdEcEventService::~DiagnosticsdEcEventService() {
DCHECK(sequence_checker_.CalledOnValidSequence());
DCHECK(!monitoring_thread_);
}
bool DiagnosticsdEcEventService::Start() {
DCHECK(sequence_checker_.CalledOnValidSequence());
DCHECK(!monitoring_thread_);
auto event_file_path = root_dir_.Append(kEcEventSysfsPath);
event_fd_.reset(HANDLE_EINTR(
open(event_file_path.value().c_str(), O_RDONLY | O_NONBLOCK)));
if (!event_fd_.is_valid()) {
PLOG(ERROR) << "Unable to open sysfs event file: "
<< event_file_path.value();
return false;
}
shutdown_fd_.reset(eventfd(0, EFD_NONBLOCK));
if (!shutdown_fd_.is_valid()) {
PLOG(ERROR) << "Unable to create eventfd";
return false;
}
monitoring_thread_delegate_ =
std::make_unique<internal::EcEventMonitoringThreadDelegate>(
event_fd_.get(), event_fd_events_, shutdown_fd_.get(),
message_loop_->task_runner(),
base::BindRepeating(&DiagnosticsdEcEventService::OnEventAvailable,
base::Unretained(this)),
base::BindOnce(&DiagnosticsdEcEventService::OnShutdown,
base::Unretained(this)));
monitoring_thread_ = std::make_unique<base::DelegateSimpleThread>(
monitoring_thread_delegate_.get(),
"DiagnosticsdEcEventMonitoring" /* name_prefix */);
monitoring_thread_->Start();
return true;
}
void DiagnosticsdEcEventService::Shutdown(base::Closure on_shutdown_callback) {
DCHECK(sequence_checker_.CalledOnValidSequence());
DCHECK(on_shutdown_callback_.is_null());
DCHECK(!on_shutdown_callback.is_null());
if (!monitoring_thread_) {
on_shutdown_callback.Run();
return;
}
on_shutdown_callback_ = on_shutdown_callback;
ShutdownMonitoringThread();
}
void DiagnosticsdEcEventService::ShutdownMonitoringThread() {
// Due to |eventfd| documentation to invoke |poll()| on |shutdown_fd_| file
// descriptor we must write any 8-byte value greater than 0 except
// |0xffffffffffffffff|.
uint64_t counter = 1;
if (HANDLE_EINTR(write(shutdown_fd_.get(), &counter, sizeof(counter)) !=
sizeof(counter))) {
PLOG(ERROR)
<< "Unable to write data in fake fd to shutdown EC event service";
}
}
void DiagnosticsdEcEventService::OnEventAvailable(const EcEvent& ec_event) {
DCHECK(sequence_checker_.CalledOnValidSequence());
delegate_->SendGrpcEcEventToDiagnosticsProcessor(ec_event);
}
void DiagnosticsdEcEventService::OnShutdown() {
DCHECK(sequence_checker_.CalledOnValidSequence());
monitoring_thread_->Join();
monitoring_thread_.reset();
monitoring_thread_delegate_.reset();
if (!on_shutdown_callback_.is_null()) {
on_shutdown_callback_.Run();
on_shutdown_callback_.Reset();
}
}
} // namespace diagnostics