blob: 8c5e0751eed67cd821f44f980f50c8f7e37b7bd4 [file] [log] [blame]
// Copyright 2019 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 "media/gpu/v4l2/v4l2_device_poller.h"
#include <string>
#include "base/bind.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "base/threading/thread_checker.h"
#include "media/gpu/macros.h"
#include "media/gpu/v4l2/v4l2_device.h"
namespace media {
V4L2DevicePoller::V4L2DevicePoller(V4L2Device* const device,
const std::string& thread_name)
: device_(device),
poll_thread_(std::move(thread_name)),
trigger_poll_(base::WaitableEvent::ResetPolicy::AUTOMATIC,
base::WaitableEvent::InitialState::NOT_SIGNALED),
stop_polling_(false) {
DETACH_FROM_SEQUENCE(client_sequence_checker_);
}
V4L2DevicePoller::~V4L2DevicePoller() {
// It's possible the V4L2 device poller gets destroyed on a different thread
// than expected if e.g. destroying a decoder immediately after creation. The
// check here is not thread-safe, but using a lock or atomic state doesn't
// make sense as destruction is never thread-safe.
if (poll_thread_.IsRunning())
StopPolling();
}
bool V4L2DevicePoller::StartPolling(EventCallback event_callback,
base::RepeatingClosure error_callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_);
if (IsPolling())
return true;
DVLOGF(4) << "Starting polling";
client_task_runner_ = base::SequencedTaskRunnerHandle::Get();
error_callback_ = error_callback;
if (!poll_thread_.Start()) {
VLOGF(1) << "Failed to start device poll thread";
return false;
}
event_callback_ = std::move(event_callback);
stop_polling_.store(false);
poll_thread_.task_runner()->PostTask(
FROM_HERE, base::BindOnce(&V4L2DevicePoller::DevicePollTask,
base::Unretained(this)));
DVLOGF(3) << "Polling thread started";
SchedulePoll();
return true;
}
bool V4L2DevicePoller::StopPolling() {
DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_);
if (!IsPolling())
return true;
DVLOGF(4) << "Stopping polling";
stop_polling_.store(true);
trigger_poll_.Signal();
if (!device_->SetDevicePollInterrupt()) {
VLOGF(1) << "Failed to interrupt device poll.";
return false;
}
DVLOGF(3) << "Stop device poll thread";
poll_thread_.Stop();
if (!device_->ClearDevicePollInterrupt()) {
VLOGF(1) << "Failed to clear interrupting device poll.";
return false;
}
DVLOGF(4) << "Polling thread stopped";
return true;
}
bool V4L2DevicePoller::IsPolling() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_);
return poll_thread_.IsRunning();
}
void V4L2DevicePoller::SchedulePoll() {
DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_);
// A call to DevicePollTask() will be posted when we actually start polling.
if (!IsPolling())
return;
DVLOGF(4) << "Scheduling poll";
trigger_poll_.Signal();
}
void V4L2DevicePoller::DevicePollTask() {
DCHECK(poll_thread_.task_runner()->RunsTasksInCurrentSequence());
while (true) {
DVLOGF(4) << "Waiting for poll to be scheduled.";
trigger_poll_.Wait();
if (stop_polling_) {
DVLOGF(4) << "Poll stopped, exiting.";
break;
}
bool event_pending = false;
DVLOGF(4) << "Polling device.";
if (!device_->Poll(true, &event_pending)) {
VLOGF(1) << "An error occured while polling, calling error callback";
client_task_runner_->PostTask(FROM_HERE, error_callback_);
return;
}
DVLOGF(4) << "Poll returned, calling event callback.";
client_task_runner_->PostTask(
FROM_HERE, base::BindRepeating(event_callback_, event_pending));
}
}
} // namespace media