1 // Copyright 2019 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 // Note: ported from Chromium commit head: 22d34680c8ac
5
6 #include <v4l2_codec2/common/V4L2DevicePoller.h>
7
8 #include <string>
9
10 #include <base/bind.h>
11 #include <base/threading/sequenced_task_runner_handle.h>
12 #include <base/threading/thread_checker.h>
13 #include <log/log.h>
14
15 #include <v4l2_codec2/common/V4L2Device.h>
16
17 namespace android {
18
V4L2DevicePoller(V4L2Device * const device,const std::string & threadName)19 V4L2DevicePoller::V4L2DevicePoller(V4L2Device* const device, const std::string& threadName)
20 : mDevice(device),
21 mPollThread(std::move(threadName)),
22 mTriggerPoll(base::WaitableEvent::ResetPolicy::AUTOMATIC,
23 base::WaitableEvent::InitialState::NOT_SIGNALED),
24 mStopPolling(false) {}
25
~V4L2DevicePoller()26 V4L2DevicePoller::~V4L2DevicePoller() {
27 ALOG_ASSERT(mClientTaskTunner->RunsTasksInCurrentSequence());
28
29 stopPolling();
30 }
31
startPolling(EventCallback eventCallback,base::RepeatingClosure errorCallback)32 bool V4L2DevicePoller::startPolling(EventCallback eventCallback,
33 base::RepeatingClosure errorCallback) {
34 if (isPolling()) return true;
35
36 ALOGV("Starting polling");
37
38 mClientTaskTunner = base::SequencedTaskRunnerHandle::Get();
39 mErrorCallback = errorCallback;
40
41 if (!mPollThread.Start()) {
42 ALOGE("Failed to start device poll thread");
43 return false;
44 }
45
46 mEventCallback = std::move(eventCallback);
47
48 mStopPolling.store(false);
49 mPollThread.task_runner()->PostTask(
50 FROM_HERE, base::BindOnce(&V4L2DevicePoller::devicePollTask, base::Unretained(this)));
51
52 ALOGV("Polling thread started");
53
54 schedulePoll();
55
56 return true;
57 }
58
stopPolling()59 bool V4L2DevicePoller::stopPolling() {
60 ALOG_ASSERT(mClientTaskTunner->RunsTasksInCurrentSequence());
61
62 if (!isPolling()) return true;
63
64 ALOGV("Stopping polling");
65
66 mStopPolling.store(true);
67
68 mTriggerPoll.Signal();
69
70 if (!mDevice->setDevicePollInterrupt()) {
71 ALOGE("Failed to interrupt device poll.");
72 return false;
73 }
74
75 ALOGV("Stop device poll thread");
76 mPollThread.Stop();
77
78 if (!mDevice->clearDevicePollInterrupt()) {
79 ALOGE("Failed to clear interrupting device poll.");
80 return false;
81 }
82
83 ALOGV("Polling thread stopped");
84
85 return true;
86 }
87
isPolling() const88 bool V4L2DevicePoller::isPolling() const {
89 ALOG_ASSERT(mClientTaskTunner->RunsTasksInCurrentSequence());
90
91 return mPollThread.IsRunning();
92 }
93
schedulePoll()94 void V4L2DevicePoller::schedulePoll() {
95 ALOG_ASSERT(mClientTaskTunner->RunsTasksInCurrentSequence());
96
97 // A call to DevicePollTask() will be posted when we actually start polling.
98 if (!isPolling()) return;
99
100 ALOGV("Scheduling poll");
101
102 mTriggerPoll.Signal();
103 }
104
devicePollTask()105 void V4L2DevicePoller::devicePollTask() {
106 ALOG_ASSERT(mClientTaskTunner->RunsTasksInCurrentSequence());
107
108 while (true) {
109 ALOGV("Waiting for poll to be scheduled.");
110 mTriggerPoll.Wait();
111
112 if (mStopPolling) {
113 ALOGV("Poll stopped, exiting.");
114 break;
115 }
116
117 bool event_pending = false;
118 ALOGV("Polling device.");
119 if (!mDevice->poll(true, &event_pending)) {
120 ALOGE("An error occurred while polling, calling error callback");
121 mClientTaskTunner->PostTask(FROM_HERE, mErrorCallback);
122 return;
123 }
124
125 ALOGV("Poll returned, calling event callback.");
126 mClientTaskTunner->PostTask(FROM_HERE, base::Bind(mEventCallback, event_pending));
127 }
128 }
129
130 } // namespace android
131