1 /* 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 19 #include <sys/select.h> 20 #include <sys/time.h> 21 #include <sys/types.h> 22 #include <unistd.h> 23 24 #include <atomic> 25 26 #include <android-base/logging.h> 27 #include <utils/Thread.h> 28 29 namespace android { 30 namespace lshal { 31 32 static constexpr struct timeval READ_TIMEOUT { .tv_sec = 1, .tv_usec = 0 }; 33 34 struct PipeRelay::RelayThread : public Thread { 35 explicit RelayThread(int fd, std::ostream &os); 36 37 bool threadLoop() override; 38 void setFinished(); 39 40 private: 41 int mFd; 42 std::ostream &mOutStream; 43 44 // If we were to use requestExit() and exitPending() instead, threadLoop() 45 // may not run at all by the time ~PipeRelay is called (i.e. debug() has 46 // returned from HAL). By using our own flag, we ensure that select() and 47 // read() are executed until data are drained. 48 std::atomic_bool mFinished; 49 50 DISALLOW_COPY_AND_ASSIGN(RelayThread); 51 }; 52 53 //////////////////////////////////////////////////////////////////////////////// 54 55 PipeRelay::RelayThread::RelayThread(int fd, std::ostream &os) 56 : mFd(fd), mOutStream(os), mFinished(false) {} 57 58 bool PipeRelay::RelayThread::threadLoop() { 59 char buffer[1024]; 60 61 fd_set set; 62 FD_ZERO(&set); 63 FD_SET(mFd, &set); 64 65 struct timeval timeout = READ_TIMEOUT; 66 67 int res = TEMP_FAILURE_RETRY(select(mFd + 1, &set, nullptr, nullptr, &timeout)); 68 if (res < 0) { 69 PLOG(INFO) << "select() failed"; 70 return false; 71 } 72 73 if (res == 0 || !FD_ISSET(mFd, &set)) { 74 if (mFinished) { 75 LOG(WARNING) << "debug: timeout reading from pipe, output may be truncated."; 76 return false; 77 } 78 // timeout, but debug() has not returned, so wait for HAL to finish. 79 return true; 80 } 81 82 // FD_ISSET(mFd, &set) == true. Data available, start reading 83 ssize_t n = TEMP_FAILURE_RETRY(read(mFd, buffer, sizeof(buffer))); 84 85 if (n < 0) { 86 PLOG(ERROR) << "read() failed"; 87 } 88 89 if (n <= 0) { 90 return false; 91 } 92 93 mOutStream.write(buffer, n); 94 95 return true; 96 } 97 98 void PipeRelay::RelayThread::setFinished() { 99 mFinished = true; 100 } 101 102 //////////////////////////////////////////////////////////////////////////////// 103 104 PipeRelay::PipeRelay(std::ostream &os) 105 : mInitCheck(NO_INIT) { 106 int res = pipe(mFds); 107 108 if (res < 0) { 109 mInitCheck = -errno; 110 return; 111 } 112 113 mThread = new RelayThread(mFds[0], os); 114 mInitCheck = mThread->run("RelayThread"); 115 } 116 117 void PipeRelay::CloseFd(int *fd) { 118 if (*fd >= 0) { 119 close(*fd); 120 *fd = -1; 121 } 122 } 123 124 PipeRelay::~PipeRelay() { 125 CloseFd(&mFds[1]); 126 127 if (mThread != nullptr) { 128 mThread->setFinished(); 129 mThread->join(); 130 mThread.clear(); 131 } 132 133 CloseFd(&mFds[0]); 134 } 135 136 status_t PipeRelay::initCheck() const { 137 return mInitCheck; 138 } 139 140 int PipeRelay::fd() const { 141 return mFds[1]; 142 } 143 144 } // namespace lshal 145 } // namespace android 146