1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
  114
  115
  116
  117
  118
  119
  120
  121
  122
  123
  124
  125
  126
  127
  128
  129
  130
  131
  132
  133
  134
  135
  136
  137
  138
  139
  140
  141
  142
  143
  144
  145
  146
  147

media / gpu / v4l2 / v4l2_device_poller.cc [blame]

// Copyright 2019 The Chromium Authors
// 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/functional/bind.h"
#include "base/task/sequenced_task_runner.h"
#include "base/threading/thread_checker.h"
#include "base/threading/thread_restrictions.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()) {
    base::ScopedAllowBaseSyncPrimitivesOutsideBlockingScope allow_wait;
    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::SequencedTaskRunner::GetCurrentDefault();
  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";
  {
    base::ScopedAllowBaseSyncPrimitivesOutsideBlockingScope allow_wait;
    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 occurred 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