platform_frameworks_native/cmds/lshal/PipeRelay.cpp
Yifan Hong b1db390e25 lshal: Allow read to timeout in debug()
Start timing when debug() has returned. When the specific
timeout has reached (currently 1s) and the relay thread
has not finish, tell the relay thread to stop.

Test: while true; do date; lshal debug android.hardware.health.storage@1.0::IStorage/default; done
Test: lshal_test

Bug: 111997867
Change-Id: Ib9235d3bd2fc3a54eb316da8d8b59d987988b134
2018-09-27 11:09:17 -07:00

145 lines
3.4 KiB
C++

/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "PipeRelay.h"
#include <sys/select.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <atomic>
#include <android-base/logging.h>
#include <utils/Thread.h>
namespace android {
namespace lshal {
static constexpr struct timeval READ_TIMEOUT { .tv_sec = 1, .tv_usec = 0 };
struct PipeRelay::RelayThread : public Thread {
explicit RelayThread(int fd, std::ostream &os);
bool threadLoop() override;
void setFinished();
private:
int mFd;
std::ostream &mOutStream;
// If we were to use requestExit() and exitPending() instead, threadLoop()
// may not run at all by the time ~PipeRelay is called (i.e. debug() has
// returned from HAL). By using our own flag, we ensure that select() and
// read() are executed until data are drained.
std::atomic_bool mFinished;
DISALLOW_COPY_AND_ASSIGN(RelayThread);
};
////////////////////////////////////////////////////////////////////////////////
PipeRelay::RelayThread::RelayThread(int fd, std::ostream &os)
: mFd(fd), mOutStream(os), mFinished(false) {}
bool PipeRelay::RelayThread::threadLoop() {
char buffer[1024];
fd_set set;
FD_ZERO(&set);
FD_SET(mFd, &set);
struct timeval timeout = READ_TIMEOUT;
int res = TEMP_FAILURE_RETRY(select(mFd + 1, &set, nullptr, nullptr, &timeout));
if (res < 0) {
PLOG(INFO) << "select() failed";
return false;
}
if (res == 0 || !FD_ISSET(mFd, &set)) {
if (mFinished) {
LOG(WARNING) << "debug: timeout reading from pipe, output may be truncated.";
return false;
}
// timeout, but debug() has not returned, so wait for HAL to finish.
return true;
}
// FD_ISSET(mFd, &set) == true. Data available, start reading
ssize_t n = TEMP_FAILURE_RETRY(read(mFd, buffer, sizeof(buffer)));
if (n < 0) {
PLOG(ERROR) << "read() failed";
}
if (n <= 0) {
return false;
}
mOutStream.write(buffer, n);
return true;
}
void PipeRelay::RelayThread::setFinished() {
mFinished = true;
}
////////////////////////////////////////////////////////////////////////////////
PipeRelay::PipeRelay(std::ostream &os)
: mInitCheck(NO_INIT) {
int res = pipe(mFds);
if (res < 0) {
mInitCheck = -errno;
return;
}
mThread = new RelayThread(mFds[0], os);
mInitCheck = mThread->run("RelayThread");
}
void PipeRelay::CloseFd(int *fd) {
if (*fd >= 0) {
close(*fd);
*fd = -1;
}
}
PipeRelay::~PipeRelay() {
CloseFd(&mFds[1]);
if (mThread != nullptr) {
mThread->setFinished();
mThread->join();
mThread.clear();
}
CloseFd(&mFds[0]);
}
status_t PipeRelay::initCheck() const {
return mInitCheck;
}
int PipeRelay::fd() const {
return mFds[1];
}
} // namespace lshal
} // namespace android