blob: 4e9763635b2d4dbcb03ae33e854e635650be5a77 [file] [log] [blame]
Andreas Huber28d35912017-03-24 13:14:11 -07001/*
2 * Copyright (C) 2017 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 "PipeRelay.h"
18
Yifan Hongb1db3902018-09-26 15:49:28 -070019#include <sys/select.h>
20#include <sys/time.h>
21#include <sys/types.h>
22#include <unistd.h>
23
24#include <atomic>
25
Andreas Huber28d35912017-03-24 13:14:11 -070026#include <utils/Thread.h>
27
28namespace android {
29namespace lshal {
30
Yifan Hongb1db3902018-09-26 15:49:28 -070031static constexpr struct timeval READ_TIMEOUT { .tv_sec = 1, .tv_usec = 0 };
32
Yifan Hong8a662472020-09-24 11:50:50 -070033static std::string getThreadName(std::string interfaceName, const std::string &instanceName) {
34 auto dot = interfaceName.rfind(".");
35 if (dot != std::string::npos) interfaceName = interfaceName.substr(dot + 1);
36 return "RelayThread_" + interfaceName + "_" + instanceName;
37}
38
Andreas Huber28d35912017-03-24 13:14:11 -070039struct PipeRelay::RelayThread : public Thread {
Yifan Hong8a662472020-09-24 11:50:50 -070040 explicit RelayThread(int fd, std::ostream &os, const NullableOStream<std::ostream> &err,
41 const std::string &fqName);
Andreas Huber28d35912017-03-24 13:14:11 -070042
43 bool threadLoop() override;
Yifan Hongb1db3902018-09-26 15:49:28 -070044 void setFinished();
Andreas Huber28d35912017-03-24 13:14:11 -070045
46private:
47 int mFd;
48 std::ostream &mOutStream;
Yifan Hong8a662472020-09-24 11:50:50 -070049 NullableOStream<std::ostream> mErrStream;
Andreas Huber28d35912017-03-24 13:14:11 -070050
Yifan Hongb1db3902018-09-26 15:49:28 -070051 // If we were to use requestExit() and exitPending() instead, threadLoop()
52 // may not run at all by the time ~PipeRelay is called (i.e. debug() has
53 // returned from HAL). By using our own flag, we ensure that select() and
54 // read() are executed until data are drained.
55 std::atomic_bool mFinished;
56
Yifan Hong8a662472020-09-24 11:50:50 -070057 std::string mFqName;
58
Andreas Huber28d35912017-03-24 13:14:11 -070059 DISALLOW_COPY_AND_ASSIGN(RelayThread);
60};
61
62////////////////////////////////////////////////////////////////////////////////
63
Yifan Hong8a662472020-09-24 11:50:50 -070064PipeRelay::RelayThread::RelayThread(int fd, std::ostream &os,
65 const NullableOStream<std::ostream> &err,
66 const std::string &fqName)
67 : mFd(fd), mOutStream(os), mErrStream(err), mFinished(false), mFqName(fqName) {}
Andreas Huber28d35912017-03-24 13:14:11 -070068
69bool PipeRelay::RelayThread::threadLoop() {
70 char buffer[1024];
Yifan Hongb1db3902018-09-26 15:49:28 -070071
72 fd_set set;
73 FD_ZERO(&set);
74 FD_SET(mFd, &set);
75
76 struct timeval timeout = READ_TIMEOUT;
77
78 int res = TEMP_FAILURE_RETRY(select(mFd + 1, &set, nullptr, nullptr, &timeout));
79 if (res < 0) {
Yifan Hong8a662472020-09-24 11:50:50 -070080 mErrStream << "debug " << mFqName << ": select() failed";
Yifan Hongb1db3902018-09-26 15:49:28 -070081 return false;
82 }
83
84 if (res == 0 || !FD_ISSET(mFd, &set)) {
85 if (mFinished) {
Yifan Hong8a662472020-09-24 11:50:50 -070086 mErrStream << "debug " << mFqName
87 << ": timeout reading from pipe, output may be truncated.";
Yifan Hongb1db3902018-09-26 15:49:28 -070088 return false;
89 }
90 // timeout, but debug() has not returned, so wait for HAL to finish.
91 return true;
92 }
93
94 // FD_ISSET(mFd, &set) == true. Data available, start reading
95 ssize_t n = TEMP_FAILURE_RETRY(read(mFd, buffer, sizeof(buffer)));
96
97 if (n < 0) {
Yifan Hong8a662472020-09-24 11:50:50 -070098 mErrStream << "debug " << mFqName << ": read() failed";
Yifan Hongb1db3902018-09-26 15:49:28 -070099 }
Andreas Huber28d35912017-03-24 13:14:11 -0700100
101 if (n <= 0) {
102 return false;
103 }
104
105 mOutStream.write(buffer, n);
106
107 return true;
108}
109
Yifan Hongb1db3902018-09-26 15:49:28 -0700110void PipeRelay::RelayThread::setFinished() {
111 mFinished = true;
112}
113
Andreas Huber28d35912017-03-24 13:14:11 -0700114////////////////////////////////////////////////////////////////////////////////
115
Yifan Hong8a662472020-09-24 11:50:50 -0700116PipeRelay::PipeRelay(std::ostream &os, const NullableOStream<std::ostream> &err,
117 const std::string &interfaceName, const std::string &instanceName)
118 : mInitCheck(NO_INIT) {
Andreas Huber7dfac442018-05-22 12:56:38 -0700119 int res = pipe(mFds);
Andreas Huber28d35912017-03-24 13:14:11 -0700120
121 if (res < 0) {
122 mInitCheck = -errno;
123 return;
124 }
125
Yifan Hong8a662472020-09-24 11:50:50 -0700126 mThread = new RelayThread(mFds[0], os, err, interfaceName + "/" + instanceName);
127 mInitCheck = mThread->run(getThreadName(interfaceName, instanceName).c_str());
Andreas Huber28d35912017-03-24 13:14:11 -0700128}
129
Andreas Huber28d35912017-03-24 13:14:11 -0700130void PipeRelay::CloseFd(int *fd) {
131 if (*fd >= 0) {
132 close(*fd);
133 *fd = -1;
134 }
135}
136
137PipeRelay::~PipeRelay() {
Andreas Huber7dfac442018-05-22 12:56:38 -0700138 CloseFd(&mFds[1]);
Andreas Huber28d35912017-03-24 13:14:11 -0700139
Yi Kong19d5c002018-07-20 13:39:55 -0700140 if (mThread != nullptr) {
Yifan Hongb1db3902018-09-26 15:49:28 -0700141 mThread->setFinished();
Andreas Huber28d35912017-03-24 13:14:11 -0700142 mThread->join();
143 mThread.clear();
144 }
Yifan Honga5ae7862018-09-26 16:07:12 -0700145
146 CloseFd(&mFds[0]);
Andreas Huber28d35912017-03-24 13:14:11 -0700147}
148
149status_t PipeRelay::initCheck() const {
150 return mInitCheck;
151}
152
153int PipeRelay::fd() const {
154 return mFds[1];
155}
156
157} // namespace lshal
158} // namespace android