blob: 636b4b6e1194ff1e33a40b77c77496e967ed7aef [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
53void AsyncFdWatcher::StopWatchingFileDescriptor() { stopThread(); }
54
55AsyncFdWatcher::~AsyncFdWatcher() {}
56
57// Make sure to call this with at least one file descriptor ready to be
58// watched upon or the thread routine will return immediately
59int AsyncFdWatcher::tryStartThread() {
60 if (std::atomic_exchange(&running_, true)) return 0;
61
62 // Set up the communication channel
63 int pipe_fds[2];
64 if (pipe2(pipe_fds, O_NONBLOCK)) return -1;
65
66 notification_listen_fd_ = pipe_fds[0];
67 notification_write_fd_ = pipe_fds[1];
68
69 thread_ = std::thread([this]() { ThreadRoutine(); });
70 if (!thread_.joinable()) return -1;
71
72 return 0;
73}
74
75int AsyncFdWatcher::stopThread() {
76 if (!std::atomic_exchange(&running_, false)) return 0;
77
78 notifyThread();
79 if (std::this_thread::get_id() != thread_.get_id()) {
80 thread_.join();
81 }
82
83 {
84 std::unique_lock<std::mutex> guard(internal_mutex_);
85 cb_ = nullptr;
86 read_fd_ = -1;
87 }
88
89 return 0;
90}
91
92int AsyncFdWatcher::notifyThread() {
93 uint8_t buffer[] = {0};
94 if (TEMP_FAILURE_RETRY(write(notification_write_fd_, &buffer, 1)) < 0) {
95 return -1;
96 }
97 return 0;
98}
99
100void AsyncFdWatcher::ThreadRoutine() {
101 while (running_) {
102 fd_set read_fds;
103 FD_ZERO(&read_fds);
104 FD_SET(notification_listen_fd_, &read_fds);
105 FD_SET(read_fd_, &read_fds);
106
107 // Wait until there is data available to read on some FD
108 int nfds = std::max(notification_listen_fd_, read_fd_);
109 int retval = select(nfds + 1, &read_fds, NULL, NULL, NULL);
110 if (retval <= 0) continue; // there was some error or a timeout
111
112 // Read data from the notification FD
113 if (FD_ISSET(notification_listen_fd_, &read_fds)) {
114 char buffer[] = {0};
115 TEMP_FAILURE_RETRY(read(notification_listen_fd_, buffer, 1));
116 }
117
118 // Make sure we're still running
119 if (!running_) break;
120
121 // Invoke the data ready callback if appropriate
122 if (FD_ISSET(read_fd_, &read_fds)) {
123 std::unique_lock<std::mutex> guard(internal_mutex_);
124 if (cb_) cb_(read_fd_);
125 }
126 }
127}
128
129} // namespace implementation
130} // namespace V1_0
131} // namespace bluetooth
132} // namespace hardware
133} // namespace android