blob: 287d007a73743605d204e99b24ee32c6f4cdd4a0 [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
Myles Watson5ea487b2017-02-21 16:53:34 -080044 return tryStartThread();
Andre Eisenbach89ba5282016-10-13 15:45:02 -070045}
46
Myles Watson7d42dca2017-01-24 16:51:39 -080047int AsyncFdWatcher::ConfigureTimeout(
48 const std::chrono::milliseconds timeout,
49 const TimeoutCallback& on_timeout_callback) {
50 // Add timeout and callback
51 {
52 std::unique_lock<std::mutex> guard(timeout_mutex_);
53 timeout_cb_ = on_timeout_callback;
54 timeout_ms_ = timeout;
55 }
56
57 notifyThread();
58 return 0;
59}
60
Andre Eisenbach89ba5282016-10-13 15:45:02 -070061void AsyncFdWatcher::StopWatchingFileDescriptor() { stopThread(); }
62
63AsyncFdWatcher::~AsyncFdWatcher() {}
64
65// Make sure to call this with at least one file descriptor ready to be
66// watched upon or the thread routine will return immediately
67int AsyncFdWatcher::tryStartThread() {
68 if (std::atomic_exchange(&running_, true)) return 0;
69
70 // Set up the communication channel
71 int pipe_fds[2];
72 if (pipe2(pipe_fds, O_NONBLOCK)) return -1;
73
74 notification_listen_fd_ = pipe_fds[0];
75 notification_write_fd_ = pipe_fds[1];
76
77 thread_ = std::thread([this]() { ThreadRoutine(); });
78 if (!thread_.joinable()) return -1;
79
80 return 0;
81}
82
83int AsyncFdWatcher::stopThread() {
84 if (!std::atomic_exchange(&running_, false)) return 0;
85
86 notifyThread();
87 if (std::this_thread::get_id() != thread_.get_id()) {
88 thread_.join();
89 }
90
91 {
92 std::unique_lock<std::mutex> guard(internal_mutex_);
93 cb_ = nullptr;
94 read_fd_ = -1;
95 }
96
Myles Watson7d42dca2017-01-24 16:51:39 -080097 {
98 std::unique_lock<std::mutex> guard(timeout_mutex_);
99 timeout_cb_ = nullptr;
100 }
101
Andre Eisenbach89ba5282016-10-13 15:45:02 -0700102 return 0;
103}
104
105int AsyncFdWatcher::notifyThread() {
106 uint8_t buffer[] = {0};
107 if (TEMP_FAILURE_RETRY(write(notification_write_fd_, &buffer, 1)) < 0) {
108 return -1;
109 }
110 return 0;
111}
112
113void AsyncFdWatcher::ThreadRoutine() {
114 while (running_) {
115 fd_set read_fds;
116 FD_ZERO(&read_fds);
117 FD_SET(notification_listen_fd_, &read_fds);
118 FD_SET(read_fd_, &read_fds);
119
Myles Watson7d42dca2017-01-24 16:51:39 -0800120 struct timeval timeout;
121 struct timeval* timeout_ptr = NULL;
122 if (timeout_ms_ > std::chrono::milliseconds(0)) {
123 timeout.tv_sec = timeout_ms_.count() / 1000;
124 timeout.tv_usec = (timeout_ms_.count() % 1000) * 1000;
125 timeout_ptr = &timeout;
126 }
Andre Eisenbach89ba5282016-10-13 15:45:02 -0700127
Myles Watson7d42dca2017-01-24 16:51:39 -0800128 // Wait until there is data available to read on some FD.
129 int nfds = std::max(notification_listen_fd_, read_fd_);
130 int retval = select(nfds + 1, &read_fds, NULL, NULL, timeout_ptr);
131
132 // There was some error.
133 if (retval < 0) continue;
134
135 // Timeout.
136 if (retval == 0) {
Myles Watsoneba13122017-02-02 10:47:36 -0800137 // Allow the timeout callback to modify the timeout.
138 TimeoutCallback saved_cb;
139 {
140 std::unique_lock<std::mutex> guard(timeout_mutex_);
141 if (timeout_ms_ > std::chrono::milliseconds(0))
142 saved_cb = timeout_cb_;
143 }
144 if (saved_cb != nullptr)
145 saved_cb();
Myles Watson7d42dca2017-01-24 16:51:39 -0800146 continue;
147 }
148
149 // Read data from the notification FD.
Andre Eisenbach89ba5282016-10-13 15:45:02 -0700150 if (FD_ISSET(notification_listen_fd_, &read_fds)) {
151 char buffer[] = {0};
152 TEMP_FAILURE_RETRY(read(notification_listen_fd_, buffer, 1));
Myles Watson7d42dca2017-01-24 16:51:39 -0800153 continue;
Andre Eisenbach89ba5282016-10-13 15:45:02 -0700154 }
155
Myles Watson7d42dca2017-01-24 16:51:39 -0800156 // Invoke the data ready callback if appropriate.
Andre Eisenbach89ba5282016-10-13 15:45:02 -0700157 if (FD_ISSET(read_fd_, &read_fds)) {
158 std::unique_lock<std::mutex> guard(internal_mutex_);
159 if (cb_) cb_(read_fd_);
160 }
161 }
162}
163
Myles Watsonbe6176d2017-02-21 13:27:01 -0800164} // namespace async
Andre Eisenbach89ba5282016-10-13 15:45:02 -0700165} // namespace bluetooth
166} // namespace hardware
167} // namespace android