Home | History | Annotate | Download | only in lshal
      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