blob: beb2e27b9e4e3470d686c973c794d0b5a318636a [file] [log] [blame]
// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "power_manager/powerd/system/async_file_reader.h"
#include <fcntl.h>
#include <sys/stat.h>
#include <cerrno>
#include <cstdio>
#include <base/logging.h>
#include "power_manager/common/util.h"
namespace power_manager {
namespace system {
namespace {
// Since we don't know the file size in advance, we'll have to read successively
// larger chunks. Start with 4 KB and double the chunk size with each new read.
const size_t kInitialFileReadSize = 4096;
// How often to poll for the AIO status.
const int kPollMs = 100;
} // namespace
AsyncFileReader::AsyncFileReader()
: read_in_progress_(false),
fd_(-1),
initial_read_size_(kInitialFileReadSize) {}
AsyncFileReader::~AsyncFileReader() {
Reset();
close(fd_);
}
bool AsyncFileReader::Init(const base::FilePath& path) {
CHECK_EQ(fd_, -1) << "Attempting to open new file when a valid file "
<< "descriptor exists.";
fd_ = open(path.value().c_str(), O_RDONLY, 0);
if (fd_ == -1) {
PLOG(ERROR) << "Could not open file " << path.value();
return false;
}
path_ = path;
return true;
}
bool AsyncFileReader::HasOpenedFile() const {
return (fd_ != -1);
}
void AsyncFileReader::StartRead(
const base::Callback<void(const std::string&)>& read_cb,
const base::Callback<void()>& error_cb) {
Reset();
if (fd_ == -1) {
LOG(ERROR) << "No file handle available.";
if (!error_cb.is_null())
error_cb.Run();
return;
}
if (!AsyncRead(initial_read_size_, 0)) {
if (!error_cb.is_null())
error_cb.Run();
return;
}
read_cb_ = read_cb;
error_cb_ = error_cb;
read_in_progress_ = true;
}
void AsyncFileReader::UpdateState() {
if (!read_in_progress_) {
update_state_timer_.Reset();
return;
}
int status = aio_error(&aio_control_);
// If the read is still in-progress, keep the timer running.
if (status == EINPROGRESS)
return;
// Otherwise, we stop the timer.
update_state_timer_.Stop();
switch (status) {
case ECANCELED:
Reset();
break;
case 0: {
size_t size = aio_return(&aio_control_);
// Save the data that was read, and free the buffer.
stored_data_.insert(stored_data_.end(), aio_buffer_.get(),
aio_buffer_.get() + size);
aio_buffer_ = nullptr;
if (size == aio_control_.aio_nbytes) {
// Read more data if the previous read didn't reach the end of file.
if (AsyncRead(size * 2, aio_control_.aio_offset + size))
break;
}
if (!read_cb_.is_null())
read_cb_.Run(stored_data_);
Reset();
break;
}
default: {
LOG(ERROR) << "Error during read of file " << path_.value()
<< ", status=" << status;
if (!error_cb_.is_null())
error_cb_.Run();
Reset();
break;
}
}
}
void AsyncFileReader::Reset() {
if (!read_in_progress_)
return;
update_state_timer_.Stop();
int cancel_result = aio_cancel(fd_, &aio_control_);
if (cancel_result == -1) {
PLOG(ERROR) << "aio_cancel() failed";
} else if (cancel_result == AIO_NOTCANCELED) {
LOG(INFO) << "aio_cancel() returned AIO_NOTCANCELED; waiting for "
<< "request to complete";
const aiocb* aiocb_list = {&aio_control_};
if (aio_suspend(&aiocb_list, 1, nullptr) == -1)
PLOG(ERROR) << "aio_suspend() failed";
}
aio_buffer_ = nullptr;
stored_data_.clear();
read_cb_.Reset();
error_cb_.Reset();
read_in_progress_ = false;
}
bool AsyncFileReader::AsyncRead(int size, int offset) {
aio_buffer_.reset(new char[size]);
memset(&aio_buffer_[0], 0, size);
memset(&aio_control_, 0, sizeof(aio_control_));
aio_control_.aio_nbytes = size;
aio_control_.aio_fildes = fd_;
aio_control_.aio_offset = offset;
aio_control_.aio_buf = aio_buffer_.get();
if (aio_read(&aio_control_) == -1) {
LOG(ERROR) << "Unable to access " << path_.value();
aio_buffer_ = nullptr;
return false;
}
update_state_timer_.Start(FROM_HERE,
base::TimeDelta::FromMilliseconds(kPollMs), this,
&AsyncFileReader::UpdateState);
return true;
}
} // namespace system
} // namespace power_manager