blob: 161a74a05cd2f68fb46b11d67288ce84794d6ef5 [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 {
32namespace V1_0 {
33namespace implementation {
34
35int AsyncFdWatcher::WatchFdForNonBlockingReads(
36 int file_descriptor, const ReadCallback& on_read_fd_ready_callback) {
37 // Add file descriptor and callback
38 {
39 std::unique_lock<std::mutex> guard(internal_mutex_);
40 read_fd_ = file_descriptor;
41 cb_ = on_read_fd_ready_callback;
42 }
43
44 // Start the thread if not started yet
45 int started = tryStartThread();
46 if (started != 0) {
47 return started;
48 }
49
50 return 0;
51}
52
Myles Watson7d42dca2017-01-24 16:51:39 -080053int AsyncFdWatcher::ConfigureTimeout(
54 const std::chrono::milliseconds timeout,
55 const TimeoutCallback& on_timeout_callback) {
56 // Add timeout and callback
57 {
58 std::unique_lock<std::mutex> guard(timeout_mutex_);
59 timeout_cb_ = on_timeout_callback;
60 timeout_ms_ = timeout;
61 }
62
63 notifyThread();
64 return 0;
65}
66
Andre Eisenbach89ba5282016-10-13 15:45:02 -070067void AsyncFdWatcher::StopWatchingFileDescriptor() { stopThread(); }
68
69AsyncFdWatcher::~AsyncFdWatcher() {}
70
71// Make sure to call this with at least one file descriptor ready to be
72// watched upon or the thread routine will return immediately
73int AsyncFdWatcher::tryStartThread() {
74 if (std::atomic_exchange(&running_, true)) return 0;
75
76 // Set up the communication channel
77 int pipe_fds[2];
78 if (pipe2(pipe_fds, O_NONBLOCK)) return -1;
79
80 notification_listen_fd_ = pipe_fds[0];
81 notification_write_fd_ = pipe_fds[1];
82
83 thread_ = std::thread([this]() { ThreadRoutine(); });
84 if (!thread_.joinable()) return -1;
85
86 return 0;
87}
88
89int AsyncFdWatcher::stopThread() {
90 if (!std::atomic_exchange(&running_, false)) return 0;
91
92 notifyThread();
93 if (std::this_thread::get_id() != thread_.get_id()) {
94 thread_.join();
95 }
96
97 {
98 std::unique_lock<std::mutex> guard(internal_mutex_);
99 cb_ = nullptr;
100 read_fd_ = -1;
101 }
102
Myles Watson7d42dca2017-01-24 16:51:39 -0800103 {
104 std::unique_lock<std::mutex> guard(timeout_mutex_);
105 timeout_cb_ = nullptr;
106 }
107
Andre Eisenbach89ba5282016-10-13 15:45:02 -0700108 return 0;
109}
110
111int AsyncFdWatcher::notifyThread() {
112 uint8_t buffer[] = {0};
113 if (TEMP_FAILURE_RETRY(write(notification_write_fd_, &buffer, 1)) < 0) {
114 return -1;
115 }
116 return 0;
117}
118
119void AsyncFdWatcher::ThreadRoutine() {
120 while (running_) {
121 fd_set read_fds;
122 FD_ZERO(&read_fds);
123 FD_SET(notification_listen_fd_, &read_fds);
124 FD_SET(read_fd_, &read_fds);
125
Myles Watson7d42dca2017-01-24 16:51:39 -0800126 struct timeval timeout;
127 struct timeval* timeout_ptr = NULL;
128 if (timeout_ms_ > std::chrono::milliseconds(0)) {
129 timeout.tv_sec = timeout_ms_.count() / 1000;
130 timeout.tv_usec = (timeout_ms_.count() % 1000) * 1000;
131 timeout_ptr = &timeout;
132 }
Andre Eisenbach89ba5282016-10-13 15:45:02 -0700133
Myles Watson7d42dca2017-01-24 16:51:39 -0800134 // Wait until there is data available to read on some FD.
135 int nfds = std::max(notification_listen_fd_, read_fd_);
136 int retval = select(nfds + 1, &read_fds, NULL, NULL, timeout_ptr);
137
138 // There was some error.
139 if (retval < 0) continue;
140
141 // Timeout.
142 if (retval == 0) {
Myles Watsoneba13122017-02-02 10:47:36 -0800143 // Allow the timeout callback to modify the timeout.
144 TimeoutCallback saved_cb;
145 {
146 std::unique_lock<std::mutex> guard(timeout_mutex_);
147 if (timeout_ms_ > std::chrono::milliseconds(0))
148 saved_cb = timeout_cb_;
149 }
150 if (saved_cb != nullptr)
151 saved_cb();
Myles Watson7d42dca2017-01-24 16:51:39 -0800152 continue;
153 }
154
155 // Read data from the notification FD.
Andre Eisenbach89ba5282016-10-13 15:45:02 -0700156 if (FD_ISSET(notification_listen_fd_, &read_fds)) {
157 char buffer[] = {0};
158 TEMP_FAILURE_RETRY(read(notification_listen_fd_, buffer, 1));
Myles Watson7d42dca2017-01-24 16:51:39 -0800159 continue;
Andre Eisenbach89ba5282016-10-13 15:45:02 -0700160 }
161
Myles Watson7d42dca2017-01-24 16:51:39 -0800162 // Invoke the data ready callback if appropriate.
Andre Eisenbach89ba5282016-10-13 15:45:02 -0700163 if (FD_ISSET(read_fd_, &read_fds)) {
164 std::unique_lock<std::mutex> guard(internal_mutex_);
165 if (cb_) cb_(read_fd_);
166 }
167 }
168}
169
170} // namespace implementation
171} // namespace V1_0
172} // namespace bluetooth
173} // namespace hardware
174} // namespace android