blob: a88aa275c910d76eaa0de1a907d965dc577a30bd [file] [log] [blame]
Andre Eisenbach89ba5282016-10-13 15:45:02 -07001//
2// Copyright 2016 The Android Open Source Project
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15//
16
17#include "async_fd_watcher.h"
18
19#include <algorithm>
20#include <atomic>
21#include <condition_variable>
22#include <mutex>
23#include <thread>
24#include <vector>
25#include "fcntl.h"
26#include "sys/select.h"
27#include "unistd.h"
28
29namespace android {
30namespace hardware {
31namespace bluetooth {
Myles Watsonbe6176d2017-02-21 13:27:01 -080032namespace async {
Andre Eisenbach89ba5282016-10-13 15:45:02 -070033
34int AsyncFdWatcher::WatchFdForNonBlockingReads(
35 int file_descriptor, const ReadCallback& on_read_fd_ready_callback) {
36 // Add file descriptor and callback
37 {
38 std::unique_lock<std::mutex> guard(internal_mutex_);
39 read_fd_ = file_descriptor;
40 cb_ = on_read_fd_ready_callback;
41 }
42
43 // Start the thread if not started yet
44 int started = tryStartThread();
45 if (started != 0) {
46 return started;
47 }
48
49 return 0;
50}
51
Myles Watson7d42dca2017-01-24 16:51:39 -080052int AsyncFdWatcher::ConfigureTimeout(
53 const std::chrono::milliseconds timeout,
54 const TimeoutCallback& on_timeout_callback) {
55 // Add timeout and callback
56 {
57 std::unique_lock<std::mutex> guard(timeout_mutex_);
58 timeout_cb_ = on_timeout_callback;
59 timeout_ms_ = timeout;
60 }
61
62 notifyThread();
63 return 0;
64}
65
Andre Eisenbach89ba5282016-10-13 15:45:02 -070066void AsyncFdWatcher::StopWatchingFileDescriptor() { stopThread(); }
67
68AsyncFdWatcher::~AsyncFdWatcher() {}
69
70// Make sure to call this with at least one file descriptor ready to be
71// watched upon or the thread routine will return immediately
72int AsyncFdWatcher::tryStartThread() {
73 if (std::atomic_exchange(&running_, true)) return 0;
74
75 // Set up the communication channel
76 int pipe_fds[2];
77 if (pipe2(pipe_fds, O_NONBLOCK)) return -1;
78
79 notification_listen_fd_ = pipe_fds[0];
80 notification_write_fd_ = pipe_fds[1];
81
82 thread_ = std::thread([this]() { ThreadRoutine(); });
83 if (!thread_.joinable()) return -1;
84
85 return 0;
86}
87
88int AsyncFdWatcher::stopThread() {
89 if (!std::atomic_exchange(&running_, false)) return 0;
90
91 notifyThread();
92 if (std::this_thread::get_id() != thread_.get_id()) {
93 thread_.join();
94 }
95
96 {
97 std::unique_lock<std::mutex> guard(internal_mutex_);
98 cb_ = nullptr;
99 read_fd_ = -1;
100 }
101
Myles Watson7d42dca2017-01-24 16:51:39 -0800102 {
103 std::unique_lock<std::mutex> guard(timeout_mutex_);
104 timeout_cb_ = nullptr;
105 }
106
Andre Eisenbach89ba5282016-10-13 15:45:02 -0700107 return 0;
108}
109
110int AsyncFdWatcher::notifyThread() {
111 uint8_t buffer[] = {0};
112 if (TEMP_FAILURE_RETRY(write(notification_write_fd_, &buffer, 1)) < 0) {
113 return -1;
114 }
115 return 0;
116}
117
118void AsyncFdWatcher::ThreadRoutine() {
119 while (running_) {
120 fd_set read_fds;
121 FD_ZERO(&read_fds);
122 FD_SET(notification_listen_fd_, &read_fds);
123 FD_SET(read_fd_, &read_fds);
124
Myles Watson7d42dca2017-01-24 16:51:39 -0800125 struct timeval timeout;
126 struct timeval* timeout_ptr = NULL;
127 if (timeout_ms_ > std::chrono::milliseconds(0)) {
128 timeout.tv_sec = timeout_ms_.count() / 1000;
129 timeout.tv_usec = (timeout_ms_.count() % 1000) * 1000;
130 timeout_ptr = &timeout;
131 }
Andre Eisenbach89ba5282016-10-13 15:45:02 -0700132
Myles Watson7d42dca2017-01-24 16:51:39 -0800133 // Wait until there is data available to read on some FD.
134 int nfds = std::max(notification_listen_fd_, read_fd_);
135 int retval = select(nfds + 1, &read_fds, NULL, NULL, timeout_ptr);
136
137 // There was some error.
138 if (retval < 0) continue;
139
140 // Timeout.
141 if (retval == 0) {
Myles Watsoneba13122017-02-02 10:47:36 -0800142 // Allow the timeout callback to modify the timeout.
143 TimeoutCallback saved_cb;
144 {
145 std::unique_lock<std::mutex> guard(timeout_mutex_);
146 if (timeout_ms_ > std::chrono::milliseconds(0))
147 saved_cb = timeout_cb_;
148 }
149 if (saved_cb != nullptr)
150 saved_cb();
Myles Watson7d42dca2017-01-24 16:51:39 -0800151 continue;
152 }
153
154 // Read data from the notification FD.
Andre Eisenbach89ba5282016-10-13 15:45:02 -0700155 if (FD_ISSET(notification_listen_fd_, &read_fds)) {
156 char buffer[] = {0};
157 TEMP_FAILURE_RETRY(read(notification_listen_fd_, buffer, 1));
Myles Watson7d42dca2017-01-24 16:51:39 -0800158 continue;
Andre Eisenbach89ba5282016-10-13 15:45:02 -0700159 }
160
Myles Watson7d42dca2017-01-24 16:51:39 -0800161 // Invoke the data ready callback if appropriate.
Andre Eisenbach89ba5282016-10-13 15:45:02 -0700162 if (FD_ISSET(read_fd_, &read_fds)) {
163 std::unique_lock<std::mutex> guard(internal_mutex_);
164 if (cb_) cb_(read_fd_);
165 }
166 }
167}
168
Myles Watsonbe6176d2017-02-21 13:27:01 -0800169} // namespace async
Andre Eisenbach89ba5282016-10-13 15:45:02 -0700170} // namespace bluetooth
171} // namespace hardware
172} // namespace android