| Index: net/base/file_stream_posix.cc
|
| ===================================================================
|
| --- net/base/file_stream_posix.cc (revision 151422)
|
| +++ net/base/file_stream_posix.cc (working copy)
|
| @@ -5,7 +5,7 @@
|
| // For 64-bit file access (off_t = off64_t, lseek64, etc).
|
| #define _FILE_OFFSET_BITS 64
|
|
|
| -#include "net/base/file_stream.h"
|
| +#include "net/base/file_stream_posix.h"
|
|
|
| #include <sys/types.h>
|
| #include <sys/stat.h>
|
| @@ -20,13 +20,12 @@
|
| #include "base/eintr_wrapper.h"
|
| #include "base/file_path.h"
|
| #include "base/logging.h"
|
| +#include "base/memory/ref_counted.h"
|
| #include "base/message_loop.h"
|
| #include "base/metrics/histogram.h"
|
| #include "base/string_util.h"
|
| #include "base/threading/thread_restrictions.h"
|
| #include "base/threading/worker_pool.h"
|
| -#include "base/synchronization/waitable_event.h"
|
| -#include "net/base/file_stream_metrics.h"
|
| #include "net/base/file_stream_net_log_parameters.h"
|
| #include "net/base/io_buffer.h"
|
| #include "net/base/net_errors.h"
|
| @@ -49,600 +48,364 @@
|
| FROM_CURRENT == SEEK_CUR &&
|
| FROM_END == SEEK_END, whence_matches_system);
|
|
|
| -namespace {
|
| -
|
| -int RecordAndMapError(int error,
|
| - FileErrorSource source,
|
| - bool record_uma,
|
| - const net::BoundNetLog& bound_net_log) {
|
| - net::Error net_error = MapSystemError(error);
|
| -
|
| - bound_net_log.AddEvent(
|
| - net::NetLog::TYPE_FILE_STREAM_ERROR,
|
| - base::Bind(&NetLogFileStreamErrorCallback,
|
| - source, error, net_error));
|
| -
|
| - RecordFileError(error, source, record_uma);
|
| -
|
| - return net_error;
|
| -}
|
| -
|
| -// Opens a file with some network logging.
|
| -// The opened file and the result code are written to |file| and |result|.
|
| -void OpenFile(const FilePath& path,
|
| - int open_flags,
|
| - bool record_uma,
|
| - base::PlatformFile* file,
|
| - int* result,
|
| - const net::BoundNetLog& bound_net_log) {
|
| - std::string file_name = path.AsUTF8Unsafe();
|
| - bound_net_log.BeginEvent(
|
| - net::NetLog::TYPE_FILE_STREAM_OPEN,
|
| - NetLog::StringCallback("file_name", &file_name));
|
| -
|
| - *result = OK;
|
| - *file = base::CreatePlatformFile(path, open_flags, NULL, NULL);
|
| - if (*file == base::kInvalidPlatformFileValue) {
|
| - bound_net_log.EndEvent(net::NetLog::TYPE_FILE_STREAM_OPEN);
|
| - *result = RecordAndMapError(errno, FILE_ERROR_SOURCE_OPEN, record_uma,
|
| - bound_net_log);
|
| - }
|
| -}
|
| -
|
| -// Opens a file using OpenFile() and then signals the completion.
|
| -void OpenFileAndSignal(const FilePath& path,
|
| - int open_flags,
|
| - bool record_uma,
|
| - base::PlatformFile* file,
|
| - int* result,
|
| - base::WaitableEvent* on_io_complete,
|
| - const net::BoundNetLog& bound_net_log) {
|
| - OpenFile(path, open_flags, record_uma, file, result, bound_net_log);
|
| - on_io_complete->Signal();
|
| -}
|
| -
|
| -// Closes a file with some network logging.
|
| -void CloseFile(base::PlatformFile file,
|
| - const net::BoundNetLog& bound_net_log) {
|
| - bound_net_log.AddEvent(net::NetLog::TYPE_FILE_STREAM_CLOSE);
|
| - if (file == base::kInvalidPlatformFileValue)
|
| - return;
|
| -
|
| - if (!base::ClosePlatformFile(file))
|
| - NOTREACHED();
|
| - bound_net_log.EndEvent(net::NetLog::TYPE_FILE_STREAM_OPEN);
|
| -}
|
| -
|
| -// Closes a file with CloseFile() and signals the completion.
|
| -void CloseFileAndSignal(base::PlatformFile* file,
|
| - base::WaitableEvent* on_io_complete,
|
| - const net::BoundNetLog& bound_net_log) {
|
| - CloseFile(*file, bound_net_log);
|
| - *file = base::kInvalidPlatformFileValue;
|
| - on_io_complete->Signal();
|
| -}
|
| -
|
| -// Adjusts the position from where the data is read.
|
| -void SeekFile(base::PlatformFile file,
|
| - Whence whence,
|
| - int64 offset,
|
| - int64* result,
|
| - bool record_uma,
|
| - const net::BoundNetLog& bound_net_log) {
|
| - off_t res = lseek(file, static_cast<off_t>(offset),
|
| - static_cast<int>(whence));
|
| - if (res == static_cast<off_t>(-1)) {
|
| - *result = RecordAndMapError(errno,
|
| - FILE_ERROR_SOURCE_SEEK,
|
| - record_uma,
|
| - bound_net_log);
|
| - return;
|
| - }
|
| - *result = res;
|
| -}
|
| -
|
| -// Seeks a file by calling SeekSync() and signals the completion.
|
| -void SeekFileAndSignal(base::PlatformFile file,
|
| - Whence whence,
|
| - int64 offset,
|
| - int64* result,
|
| - bool record_uma,
|
| - base::WaitableEvent* on_io_complete,
|
| - const net::BoundNetLog& bound_net_log) {
|
| - SeekFile(file, whence, offset, result, record_uma, bound_net_log);
|
| - on_io_complete->Signal();
|
| -}
|
| -
|
| -// ReadFile() is a simple wrapper around read() that handles EINTR signals and
|
| -// calls MapSystemError() to map errno to net error codes.
|
| -void ReadFile(base::PlatformFile file,
|
| - char* buf,
|
| - int buf_len,
|
| - bool record_uma,
|
| - int* result,
|
| - const net::BoundNetLog& bound_net_log) {
|
| - base::ThreadRestrictions::AssertIOAllowed();
|
| - // read(..., 0) returns 0 to indicate end-of-file.
|
| -
|
| - // Loop in the case of getting interrupted by a signal.
|
| - ssize_t res = HANDLE_EINTR(read(file, buf, static_cast<size_t>(buf_len)));
|
| - if (res == -1) {
|
| - res = RecordAndMapError(errno, FILE_ERROR_SOURCE_READ,
|
| - record_uma, bound_net_log);
|
| - }
|
| - *result = res;
|
| -}
|
| -
|
| -// Reads a file using ReadFile() and signals the completion.
|
| -void ReadFileAndSignal(base::PlatformFile file,
|
| - scoped_refptr<IOBuffer> buf,
|
| - int buf_len,
|
| - bool record_uma,
|
| - int* result,
|
| - base::WaitableEvent* on_io_complete,
|
| - const net::BoundNetLog& bound_net_log) {
|
| - ReadFile(file, buf->data(), buf_len, record_uma, result, bound_net_log);
|
| - on_io_complete->Signal();
|
| -}
|
| -
|
| -// WriteFile() is a simple wrapper around write() that handles EINTR signals and
|
| -// calls MapSystemError() to map errno to net error codes. It tries to write to
|
| -// completion.
|
| -void WriteFile(base::PlatformFile file,
|
| - const char* buf,
|
| - int buf_len,
|
| - bool record_uma,
|
| - int* result,
|
| - const net::BoundNetLog& bound_net_log) {
|
| - base::ThreadRestrictions::AssertIOAllowed();
|
| -
|
| - ssize_t res = HANDLE_EINTR(write(file, buf, buf_len));
|
| - if (res == -1) {
|
| - res = RecordAndMapError(errno, FILE_ERROR_SOURCE_WRITE, record_uma,
|
| - bound_net_log);
|
| - }
|
| - *result = res;
|
| -}
|
| -
|
| -// Writes a file using WriteFile() and signals the completion.
|
| -void WriteFileAndSignal(base::PlatformFile file,
|
| - scoped_refptr<IOBuffer> buf,
|
| - int buf_len,
|
| - bool record_uma,
|
| - int* result,
|
| - base::WaitableEvent* on_io_complete,
|
| - const net::BoundNetLog& bound_net_log) {
|
| - WriteFile(file, buf->data(), buf_len, record_uma, result, bound_net_log);
|
| - on_io_complete->Signal();
|
| -}
|
| -
|
| -// FlushFile() is a simple wrapper around fsync() that handles EINTR signals and
|
| -// calls MapSystemError() to map errno to net error codes. It tries to flush to
|
| -// completion.
|
| -int FlushFile(base::PlatformFile file,
|
| - bool record_uma,
|
| - const net::BoundNetLog& bound_net_log) {
|
| - base::ThreadRestrictions::AssertIOAllowed();
|
| - ssize_t res = HANDLE_EINTR(fsync(file));
|
| - if (res == -1) {
|
| - res = RecordAndMapError(errno, FILE_ERROR_SOURCE_FLUSH, record_uma,
|
| - bound_net_log);
|
| - }
|
| - return res;
|
| -}
|
| -
|
| -// Called when Read(), Write() or Seek() is completed.
|
| -// |result| contains the result or a network error code.
|
| -template <typename R>
|
| -void OnIOComplete(const base::WeakPtr<FileStreamPosix>& stream,
|
| - const base::Callback<void(R)>& callback,
|
| - R* result) {
|
| - if (!stream.get())
|
| - return;
|
| -
|
| - // Reset this before Run() as Run() may issue a new async operation.
|
| - stream->ResetOnIOComplete();
|
| - callback.Run(*result);
|
| -}
|
| -
|
| -} // namespace
|
| -
|
| -// FileStreamPosix ------------------------------------------------------------
|
| -
|
| -FileStreamPosix::FileStreamPosix(net::NetLog* net_log)
|
| +FileStream::AsyncContext::AsyncContext(const BoundNetLog& bound_net_log)
|
| : file_(base::kInvalidPlatformFileValue),
|
| - open_flags_(0),
|
| - auto_closed_(true),
|
| record_uma_(false),
|
| - bound_net_log_(net::BoundNetLog::Make(net_log,
|
| - net::NetLog::SOURCE_FILESTREAM)),
|
| - weak_ptr_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) {
|
| - bound_net_log_.BeginEvent(net::NetLog::TYPE_FILE_STREAM_ALIVE);
|
| + async_in_progress_(false),
|
| + destroyed_(false),
|
| + bound_net_log_(bound_net_log) {
|
| }
|
|
|
| -FileStreamPosix::FileStreamPosix(
|
| - base::PlatformFile file, int flags, net::NetLog* net_log)
|
| +FileStream::AsyncContext::AsyncContext(base::PlatformFile file,
|
| + const BoundNetLog& bound_net_log,
|
| + int /* open_flags */)
|
| : file_(file),
|
| - open_flags_(flags),
|
| - auto_closed_(false),
|
| record_uma_(false),
|
| - bound_net_log_(net::BoundNetLog::Make(net_log,
|
| - net::NetLog::SOURCE_FILESTREAM)),
|
| - weak_ptr_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) {
|
| - bound_net_log_.BeginEvent(net::NetLog::TYPE_FILE_STREAM_ALIVE);
|
| + async_in_progress_(false),
|
| + destroyed_(false),
|
| + bound_net_log_(bound_net_log) {
|
| }
|
|
|
| -FileStreamPosix::~FileStreamPosix() {
|
| - if (open_flags_ & base::PLATFORM_FILE_ASYNC) {
|
| - // Block until the last open/close/read/write operation is complete.
|
| - // TODO(satorux): Ideally we should not block. crbug.com/115067
|
| - WaitForIOCompletion();
|
| - }
|
| -
|
| - if (auto_closed_) {
|
| - if (open_flags_ & base::PLATFORM_FILE_ASYNC) {
|
| - // Close the file in the background.
|
| - if (IsOpen()) {
|
| - const bool posted = base::WorkerPool::PostTask(
|
| - FROM_HERE,
|
| - base::Bind(&CloseFile, file_, bound_net_log_),
|
| - true /* task_is_slow */);
|
| - DCHECK(posted);
|
| - }
|
| - } else {
|
| - CloseSync();
|
| - }
|
| - }
|
| -
|
| - bound_net_log_.EndEvent(net::NetLog::TYPE_FILE_STREAM_ALIVE);
|
| +void FileStream::AsyncContext::Destroy() {
|
| + destroyed_ = true;
|
| + if (!async_in_progress_)
|
| + DeleteAbandoned();
|
| }
|
|
|
| -void FileStreamPosix::Close(const CompletionCallback& callback) {
|
| - DCHECK(open_flags_ & base::PLATFORM_FILE_ASYNC);
|
| +void FileStream::AsyncContext::OpenAsync(
|
| + const FilePath& path,
|
| + int open_flags,
|
| + const CompletionCallback& callback) {
|
| + DCHECK(!async_in_progress_);
|
|
|
| - DCHECK(!weak_ptr_factory_.HasWeakPtrs());
|
| - DCHECK(!on_io_complete_.get());
|
| - on_io_complete_.reset(new base::WaitableEvent(
|
| - false /* manual_reset */, false /* initially_signaled */));
|
| + BeginOpenEvent(path);
|
|
|
| - // Passing &file_ to a thread pool looks unsafe but it's safe here as the
|
| - // destructor ensures that the close operation is complete with
|
| - // WaitForIOCompletion(). See also the destructor.
|
| + int* result = new int(OK);
|
| const bool posted = base::WorkerPool::PostTaskAndReply(
|
| FROM_HERE,
|
| - base::Bind(&CloseFileAndSignal, &file_, on_io_complete_.get(),
|
| - bound_net_log_),
|
| - base::Bind(&FileStreamPosix::OnClosed,
|
| - weak_ptr_factory_.GetWeakPtr(),
|
| - callback),
|
| + base::Bind(&AsyncContext::OpenFileImpl, base::Unretained(this),
|
| + path, open_flags, result),
|
| + base::Bind(&AsyncContext::OnOpenCompleted,
|
| + base::Unretained(this),
|
| + callback, base::Owned(result)),
|
| true /* task_is_slow */);
|
| -
|
| DCHECK(posted);
|
| +
|
| + async_in_progress_ = true;
|
| }
|
|
|
| -void FileStreamPosix::CloseSync() {
|
| - // TODO(satorux): Replace the following async stuff with a
|
| - // DCHECK(open_flags & ASYNC) once once all async clients are migrated to
|
| - // use Close(). crbug.com/114783
|
| -
|
| - // Abort any existing asynchronous operations.
|
| - weak_ptr_factory_.InvalidateWeakPtrs();
|
| - // Block until the last open/read/write operation is complete.
|
| - // TODO(satorux): Ideally we should not block. crbug.com/115067
|
| - WaitForIOCompletion();
|
| -
|
| - CloseFile(file_, bound_net_log_);
|
| - file_ = base::kInvalidPlatformFileValue;
|
| +int FileStream::AsyncContext::OpenSync(const FilePath& path, int open_flags) {
|
| + int result = OK;
|
| + BeginOpenEvent(path);
|
| + OpenFileImpl(path, open_flags, &result);
|
| + CheckForOpenError(&result);
|
| + return result;
|
| }
|
|
|
| -int FileStreamPosix::Open(const FilePath& path, int open_flags,
|
| - const CompletionCallback& callback) {
|
| - if (IsOpen()) {
|
| - DLOG(FATAL) << "File is already open!";
|
| - return ERR_UNEXPECTED;
|
| - }
|
| +void FileStream::AsyncContext::CloseAsync(const CompletionCallback& callback) {
|
| + DCHECK(!async_in_progress_);
|
|
|
| - open_flags_ = open_flags;
|
| - DCHECK(open_flags_ & base::PLATFORM_FILE_ASYNC);
|
| + bound_net_log_.AddEvent(net::NetLog::TYPE_FILE_STREAM_CLOSE);
|
|
|
| - DCHECK(!weak_ptr_factory_.HasWeakPtrs());
|
| - DCHECK(!on_io_complete_.get());
|
| - on_io_complete_.reset(new base::WaitableEvent(
|
| - false /* manual_reset */, false /* initially_signaled */));
|
| -
|
| - // Passing &file_ to a thread pool looks unsafe but it's safe here as the
|
| - // destructor ensures that the open operation is complete with
|
| - // WaitForIOCompletion(). See also the destructor.
|
| + // Value OK will never be changed in AsyncContext::CloseFile() and is needed
|
| + // here just to use the same AsyncContext::OnAsyncCompleted().
|
| int* result = new int(OK);
|
| - const bool posted = base::WorkerPool::PostTaskAndReply(
|
| - FROM_HERE,
|
| - base::Bind(&OpenFileAndSignal,
|
| - path, open_flags, record_uma_, &file_, result,
|
| - on_io_complete_.get(), bound_net_log_),
|
| - base::Bind(&OnIOComplete<int>, weak_ptr_factory_.GetWeakPtr(),
|
| - callback, base::Owned(result)),
|
| - true /* task_is_slow */);
|
| - DCHECK(posted);
|
| - return ERR_IO_PENDING;
|
| -}
|
| + if (file_ == base::kInvalidPlatformFileValue) {
|
| + MessageLoop::current()->PostTask(
|
| + FROM_HERE,
|
| + base::Bind(&AsyncContext::OnAsyncCompleted<int>,
|
| + base::Unretained(this),
|
| + callback, base::Owned(result)));
|
| + } else {
|
| + const bool posted = base::WorkerPool::PostTaskAndReply(
|
| + FROM_HERE,
|
| + base::Bind(&AsyncContext::CloseFileImpl, base::Unretained(this)),
|
| + base::Bind(&AsyncContext::OnCloseCompleted,
|
| + base::Unretained(this),
|
| + callback, base::Owned(result)),
|
| + true /* task_is_slow */);
|
| + DCHECK(posted);
|
|
|
| -int FileStreamPosix::OpenSync(const FilePath& path, int open_flags) {
|
| - if (IsOpen()) {
|
| - DLOG(FATAL) << "File is already open!";
|
| - return ERR_UNEXPECTED;
|
| + async_in_progress_ = true;
|
| }
|
| -
|
| - open_flags_ = open_flags;
|
| - // TODO(satorux): Put a DCHECK once once all async clients are migrated
|
| - // to use Open(). crbug.com/114783
|
| - //
|
| - // DCHECK(!(open_flags_ & base::PLATFORM_FILE_ASYNC));
|
| -
|
| - int result = OK;
|
| - OpenFile(path, open_flags_, record_uma_, &file_, &result, bound_net_log_);
|
| - return result;
|
| }
|
|
|
| -bool FileStreamPosix::IsOpen() const {
|
| - return file_ != base::kInvalidPlatformFileValue;
|
| +void FileStream::AsyncContext::CloseSync() {
|
| + DCHECK(!async_in_progress_);
|
| + bound_net_log_.AddEvent(net::NetLog::TYPE_FILE_STREAM_CLOSE);
|
| + if (file_ != base::kInvalidPlatformFileValue) {
|
| + CloseFileImpl();
|
| + bound_net_log_.EndEvent(net::NetLog::TYPE_FILE_STREAM_OPEN);
|
| + }
|
| }
|
|
|
| -int FileStreamPosix::Seek(Whence whence, int64 offset,
|
| - const Int64CompletionCallback& callback) {
|
| - if (!IsOpen())
|
| - return ERR_UNEXPECTED;
|
| +void FileStream::AsyncContext::SeekAsync(
|
| + Whence whence,
|
| + int64 offset,
|
| + const Int64CompletionCallback& callback) {
|
| + DCHECK(!async_in_progress_);
|
|
|
| - // Make sure we're async and we have no other in-flight async operations.
|
| - DCHECK(open_flags_ & base::PLATFORM_FILE_ASYNC);
|
| - DCHECK(!weak_ptr_factory_.HasWeakPtrs());
|
| - DCHECK(!on_io_complete_.get());
|
| -
|
| - on_io_complete_.reset(new base::WaitableEvent(
|
| - false /* manual_reset */, false /* initially_signaled */));
|
| -
|
| int64* result = new int64(-1);
|
| const bool posted = base::WorkerPool::PostTaskAndReply(
|
| FROM_HERE,
|
| - base::Bind(&SeekFileAndSignal, file_, whence, offset, result,
|
| - record_uma_, on_io_complete_.get(), bound_net_log_),
|
| - base::Bind(&OnIOComplete<int64>,
|
| - weak_ptr_factory_.GetWeakPtr(),
|
| - callback, base::Owned(result)),
|
| + base::Bind(&AsyncContext::SeekFileImpl, base::Unretained(this),
|
| + whence, offset, result),
|
| + base::Bind(&AsyncContext::OnIOCompleted<int64>,
|
| + base::Unretained(this),
|
| + callback, base::Owned(result),
|
| + FILE_ERROR_SOURCE_SEEK),
|
| true /* task is slow */);
|
| DCHECK(posted);
|
| - return ERR_IO_PENDING;
|
| +
|
| + async_in_progress_ = true;
|
| }
|
|
|
| -int64 FileStreamPosix::SeekSync(Whence whence, int64 offset) {
|
| - base::ThreadRestrictions::AssertIOAllowed();
|
| -
|
| - if (!IsOpen())
|
| - return ERR_UNEXPECTED;
|
| -
|
| - // If we're in async, make sure we don't have a request in flight.
|
| - DCHECK(!(open_flags_ & base::PLATFORM_FILE_ASYNC) ||
|
| - !on_io_complete_.get());
|
| -
|
| +int64 FileStream::AsyncContext::SeekSync(Whence whence, int64 offset) {
|
| off_t result = -1;
|
| - SeekFile(file_, whence, offset, &result, record_uma_, bound_net_log_);
|
| + SeekFileImpl(whence, offset, &result);
|
| + CheckForIOError(&result, FILE_ERROR_SOURCE_SEEK);
|
| return result;
|
| }
|
|
|
| -int64 FileStreamPosix::Available() {
|
| - base::ThreadRestrictions::AssertIOAllowed();
|
| -
|
| - if (!IsOpen())
|
| - return ERR_UNEXPECTED;
|
| -
|
| - int64 cur_pos = SeekSync(FROM_CURRENT, 0);
|
| - if (cur_pos < 0)
|
| - return cur_pos;
|
| -
|
| +int64 FileStream::AsyncContext::GetFileSize() {
|
| struct stat info;
|
| - if (fstat(file_, &info) != 0) {
|
| - return RecordAndMapError(errno,
|
| - FILE_ERROR_SOURCE_GET_SIZE,
|
| - record_uma_,
|
| - bound_net_log_);
|
| - }
|
| + if (fstat(file_, &info) != 0)
|
| + return RecordAndMapError(errno, FILE_ERROR_SOURCE_GET_SIZE);
|
|
|
| - int64 size = static_cast<int64>(info.st_size);
|
| - DCHECK_GT(size, cur_pos);
|
| -
|
| - return size - cur_pos;
|
| + return static_cast<int64>(info.st_size);
|
| }
|
|
|
| -int FileStreamPosix::Read(
|
| - IOBuffer* in_buf, int buf_len, const CompletionCallback& callback) {
|
| - if (!IsOpen())
|
| - return ERR_UNEXPECTED;
|
| +int FileStream::AsyncContext::ReadAsync(
|
| + IOBuffer* in_buf,
|
| + int buf_len,
|
| + const CompletionCallback& callback) {
|
| + DCHECK(!async_in_progress_);
|
|
|
| - // read(..., 0) will return 0, which indicates end-of-file.
|
| - DCHECK_GT(buf_len, 0);
|
| - DCHECK(open_flags_ & base::PLATFORM_FILE_READ);
|
| - DCHECK(open_flags_ & base::PLATFORM_FILE_ASYNC);
|
| -
|
| - // Make sure we don't have a request in flight.
|
| - DCHECK(!weak_ptr_factory_.HasWeakPtrs());
|
| - DCHECK(!on_io_complete_.get());
|
| -
|
| - on_io_complete_.reset(new base::WaitableEvent(
|
| - false /* manual_reset */, false /* initially_signaled */));
|
| -
|
| int* result = new int(OK);
|
| scoped_refptr<IOBuffer> buf = in_buf;
|
| const bool posted = base::WorkerPool::PostTaskAndReply(
|
| FROM_HERE,
|
| - base::Bind(&ReadFileAndSignal, file_, buf, buf_len,
|
| - record_uma_, result, on_io_complete_.get(), bound_net_log_),
|
| - base::Bind(&OnIOComplete<int>,
|
| - weak_ptr_factory_.GetWeakPtr(),
|
| - callback, base::Owned(result)),
|
| + base::Bind(&AsyncContext::ReadFileImpl, base::Unretained(this),
|
| + buf, buf_len, result),
|
| + base::Bind(&AsyncContext::OnIOCompleted<int>,
|
| + base::Unretained(this),
|
| + callback, base::Owned(result),
|
| + FILE_ERROR_SOURCE_READ),
|
| true /* task is slow */);
|
| DCHECK(posted);
|
| +
|
| + async_in_progress_ = true;
|
| return ERR_IO_PENDING;
|
| }
|
|
|
| -int FileStreamPosix::ReadSync(char* buf, int buf_len) {
|
| - if (!IsOpen())
|
| - return ERR_UNEXPECTED;
|
| -
|
| - DCHECK(!(open_flags_ & base::PLATFORM_FILE_ASYNC));
|
| - // read(..., 0) will return 0, which indicates end-of-file.
|
| - DCHECK_GT(buf_len, 0);
|
| - DCHECK(open_flags_ & base::PLATFORM_FILE_READ);
|
| -
|
| +int FileStream::AsyncContext::ReadSync(char* in_buf, int buf_len) {
|
| int result = OK;
|
| - ReadFile(file_, buf, buf_len, record_uma_, &result, bound_net_log_);
|
| + scoped_refptr<IOBuffer> buf = new WrappedIOBuffer(in_buf);
|
| + ReadFileImpl(buf, buf_len, &result);
|
| + CheckForIOError(&result, FILE_ERROR_SOURCE_READ);
|
| return result;
|
| }
|
|
|
| -int FileStreamPosix::ReadUntilComplete(char *buf, int buf_len) {
|
| - int to_read = buf_len;
|
| - int bytes_total = 0;
|
| +int FileStream::AsyncContext::WriteAsync(
|
| + IOBuffer* in_buf,
|
| + int buf_len,
|
| + const CompletionCallback& callback) {
|
| + DCHECK(!async_in_progress_);
|
|
|
| - do {
|
| - int bytes_read = ReadSync(buf, to_read);
|
| - if (bytes_read <= 0) {
|
| - if (bytes_total == 0)
|
| - return bytes_read;
|
| -
|
| - return bytes_total;
|
| - }
|
| -
|
| - bytes_total += bytes_read;
|
| - buf += bytes_read;
|
| - to_read -= bytes_read;
|
| - } while (bytes_total < buf_len);
|
| -
|
| - return bytes_total;
|
| -}
|
| -
|
| -int FileStreamPosix::Write(
|
| - IOBuffer* in_buf, int buf_len, const CompletionCallback& callback) {
|
| - if (!IsOpen())
|
| - return ERR_UNEXPECTED;
|
| -
|
| - DCHECK(open_flags_ & base::PLATFORM_FILE_ASYNC);
|
| - DCHECK(open_flags_ & base::PLATFORM_FILE_WRITE);
|
| - // write(..., 0) will return 0, which indicates end-of-file.
|
| - DCHECK_GT(buf_len, 0);
|
| -
|
| - // Make sure we don't have a request in flight.
|
| - DCHECK(!weak_ptr_factory_.HasWeakPtrs());
|
| - DCHECK(!on_io_complete_.get());
|
| - on_io_complete_.reset(new base::WaitableEvent(
|
| - false /* manual_reset */, false /* initially_signaled */));
|
| -
|
| int* result = new int(OK);
|
| scoped_refptr<IOBuffer> buf = in_buf;
|
| const bool posted = base::WorkerPool::PostTaskAndReply(
|
| FROM_HERE,
|
| - base::Bind(&WriteFileAndSignal, file_, buf, buf_len,
|
| - record_uma_, result, on_io_complete_.get(), bound_net_log_),
|
| - base::Bind(&OnIOComplete<int>,
|
| - weak_ptr_factory_.GetWeakPtr(),
|
| - callback, base::Owned(result)),
|
| + base::Bind(&AsyncContext::WriteFileImpl, base::Unretained(this),
|
| + buf, buf_len, result),
|
| + base::Bind(&AsyncContext::OnIOCompleted<int>,
|
| + base::Unretained(this),
|
| + callback, base::Owned(result),
|
| + FILE_ERROR_SOURCE_WRITE),
|
| true /* task is slow */);
|
| DCHECK(posted);
|
| +
|
| + async_in_progress_ = true;
|
| return ERR_IO_PENDING;
|
| }
|
|
|
| -int FileStreamPosix::WriteSync(
|
| - const char* buf, int buf_len) {
|
| - if (!IsOpen())
|
| - return ERR_UNEXPECTED;
|
| -
|
| - DCHECK(!(open_flags_ & base::PLATFORM_FILE_ASYNC));
|
| - DCHECK(open_flags_ & base::PLATFORM_FILE_WRITE);
|
| - // write(..., 0) will return 0, which indicates end-of-file.
|
| - DCHECK_GT(buf_len, 0);
|
| -
|
| +int FileStream::AsyncContext::WriteSync(const char* in_buf, int buf_len) {
|
| int result = OK;
|
| - WriteFile(file_, buf, buf_len, record_uma_, &result, bound_net_log_);
|
| + scoped_refptr<IOBuffer> buf = new WrappedIOBuffer(in_buf);
|
| + WriteFileImpl(buf, buf_len, &result);
|
| + CheckForIOError(&result, FILE_ERROR_SOURCE_WRITE);
|
| return result;
|
| }
|
|
|
| -int64 FileStreamPosix::Truncate(int64 bytes) {
|
| - base::ThreadRestrictions::AssertIOAllowed();
|
| +int FileStream::AsyncContext::Flush() {
|
| + ssize_t res = HANDLE_EINTR(fsync(file_));
|
| + if (res == -1)
|
| + res = RecordAndMapError(errno, FILE_ERROR_SOURCE_FLUSH);
|
| + return res;
|
| +}
|
|
|
| - if (!IsOpen())
|
| - return ERR_UNEXPECTED;
|
| -
|
| - // We'd better be open for writing.
|
| - DCHECK(open_flags_ & base::PLATFORM_FILE_WRITE);
|
| -
|
| - // Seek to the position to truncate from.
|
| - int64 seek_position = SeekSync(FROM_BEGIN, bytes);
|
| - if (seek_position != bytes)
|
| - return ERR_UNEXPECTED;
|
| -
|
| - // And truncate the file.
|
| +int FileStream::AsyncContext::Truncate(int64 bytes) {
|
| int result = ftruncate(file_, bytes);
|
| if (result == 0)
|
| - return seek_position;
|
| + return bytes;
|
|
|
| - return RecordAndMapError(errno,
|
| - FILE_ERROR_SOURCE_SET_EOF,
|
| - record_uma_,
|
| - bound_net_log_);
|
| + return RecordAndMapError(errno, FILE_ERROR_SOURCE_SET_EOF);
|
| }
|
|
|
| -int FileStreamPosix::Flush() {
|
| - if (!IsOpen())
|
| - return ERR_UNEXPECTED;
|
| +int FileStream::AsyncContext::RecordAndMapError(int error,
|
| + FileErrorSource source) {
|
| + // The following check is against incorrect use or bug. File descriptor
|
| + // shouldn't ever be closed outside of FileStream while it still tries to do
|
| + // something with it.
|
| + DCHECK(error != EBADF);
|
| + net::Error net_error = MapSystemError(error);
|
|
|
| - return FlushFile(file_, record_uma_, bound_net_log_);
|
| + if (!destroyed_) {
|
| + bound_net_log_.AddEvent(net::NetLog::TYPE_FILE_STREAM_ERROR,
|
| + base::Bind(&NetLogFileStreamErrorCallback,
|
| + source, error, net_error));
|
| + }
|
| + RecordFileError(error, source, record_uma_);
|
| + return net_error;
|
| }
|
|
|
| -void FileStreamPosix::EnableErrorStatistics() {
|
| - record_uma_ = true;
|
| +void FileStream::AsyncContext::BeginOpenEvent(const FilePath& path) {
|
| + std::string file_name = path.AsUTF8Unsafe();
|
| + bound_net_log_.BeginEvent(net::NetLog::TYPE_FILE_STREAM_OPEN,
|
| + NetLog::StringCallback("file_name", &file_name));
|
| }
|
|
|
| -void FileStreamPosix::SetBoundNetLogSource(
|
| - const net::BoundNetLog& owner_bound_net_log) {
|
| - if ((owner_bound_net_log.source().id == net::NetLog::Source::kInvalidId) &&
|
| - (bound_net_log_.source().id == net::NetLog::Source::kInvalidId)) {
|
| - // Both |BoundNetLog|s are invalid.
|
| +void FileStream::AsyncContext::OpenFileImpl(const FilePath& path,
|
| + int open_flags,
|
| + int* result) {
|
| + if (destroyed_)
|
| return;
|
| +
|
| + *result = OK;
|
| + file_ = base::CreatePlatformFile(path, open_flags, NULL, NULL);
|
| + if (file_ == base::kInvalidPlatformFileValue)
|
| + *result = errno;
|
| +}
|
| +
|
| +void FileStream::AsyncContext::CheckForOpenError(int* result) {
|
| + if (file_ == base::kInvalidPlatformFileValue) {
|
| + bound_net_log_.EndEvent(net::NetLog::TYPE_FILE_STREAM_OPEN);
|
| + *result = RecordAndMapError(*result, FILE_ERROR_SOURCE_OPEN);
|
| }
|
| +}
|
|
|
| - // Should never connect to itself.
|
| - DCHECK_NE(bound_net_log_.source().id, owner_bound_net_log.source().id);
|
| +void FileStream::AsyncContext::OnOpenCompleted(
|
| + const CompletionCallback& callback,
|
| + int* result) {
|
| + CheckForOpenError(result);
|
| + OnAsyncCompleted(callback, result);
|
| +}
|
|
|
| - bound_net_log_.AddEvent(
|
| - net::NetLog::TYPE_FILE_STREAM_BOUND_TO_OWNER,
|
| - owner_bound_net_log.source().ToEventParametersCallback());
|
| +void FileStream::AsyncContext::CloseFileImpl() {
|
| + if (!base::ClosePlatformFile(file_))
|
| + NOTREACHED();
|
| + file_ = base::kInvalidPlatformFileValue;
|
| +}
|
|
|
| - owner_bound_net_log.AddEvent(
|
| - net::NetLog::TYPE_FILE_STREAM_SOURCE,
|
| - bound_net_log_.source().ToEventParametersCallback());
|
| +void FileStream::AsyncContext::OnCloseCompleted(
|
| + const CompletionCallback& callback,
|
| + int* result) {
|
| + if (!destroyed_)
|
| + bound_net_log_.EndEvent(net::NetLog::TYPE_FILE_STREAM_OPEN);
|
| + OnAsyncCompleted(callback, result);
|
| }
|
|
|
| -base::PlatformFile FileStreamPosix::GetPlatformFileForTesting() {
|
| - return file_;
|
| +void FileStream::AsyncContext::SeekFileImpl(Whence whence,
|
| + int64 offset,
|
| + int64* result) {
|
| + base::ThreadRestrictions::AssertIOAllowed();
|
| +
|
| + // If context has been already destroyed nobody waits for operation results.
|
| + if (destroyed_)
|
| + return;
|
| +
|
| + off_t res = lseek(file_, static_cast<off_t>(offset),
|
| + static_cast<int>(whence));
|
| + if (res == static_cast<off_t>(-1))
|
| + *result = errno;
|
| + else
|
| + *result = res;
|
| }
|
|
|
| -void FileStreamPosix::ResetOnIOComplete() {
|
| - on_io_complete_.reset();
|
| - weak_ptr_factory_.InvalidateWeakPtrs();
|
| +void FileStream::AsyncContext::ReadFileImpl(scoped_refptr<IOBuffer> buf,
|
| + int buf_len,
|
| + int* result) {
|
| + base::ThreadRestrictions::AssertIOAllowed();
|
| +
|
| + // If context has been already destroyed nobody waits for operation results.
|
| + if (destroyed_)
|
| + return;
|
| +
|
| + // Loop in the case of getting interrupted by a signal.
|
| + ssize_t res = HANDLE_EINTR(read(file_, buf->data(),
|
| + static_cast<size_t>(buf_len)));
|
| + if (res == -1)
|
| + *result = errno;
|
| + else
|
| + *result = res;
|
| }
|
|
|
| -void FileStreamPosix::OnClosed(const CompletionCallback& callback) {
|
| - file_ = base::kInvalidPlatformFileValue;
|
| +void FileStream::AsyncContext::WriteFileImpl(scoped_refptr<IOBuffer> buf,
|
| + int buf_len,
|
| + int* result) {
|
| + base::ThreadRestrictions::AssertIOAllowed();
|
|
|
| - // Reset this before Run() as Run() may issue a new async operation.
|
| - ResetOnIOComplete();
|
| - callback.Run(OK);
|
| + // If context has been already destroyed nobody waits for operation results.
|
| + if (destroyed_)
|
| + return;
|
| +
|
| + ssize_t res = HANDLE_EINTR(write(file_, buf->data(), buf_len));
|
| + if (res == -1)
|
| + *result = errno;
|
| + else
|
| + *result = res;
|
| }
|
|
|
| -void FileStreamPosix::WaitForIOCompletion() {
|
| - // http://crbug.com/115067
|
| - base::ThreadRestrictions::ScopedAllowWait allow_wait;
|
| - if (on_io_complete_.get()) {
|
| - on_io_complete_->Wait();
|
| - on_io_complete_.reset();
|
| +template <typename R>
|
| +void FileStream::AsyncContext::CheckForIOError(R* result,
|
| + FileErrorSource source) {
|
| + if (*result < 0)
|
| + *result = RecordAndMapError(static_cast<int>(*result), source);
|
| +}
|
| +
|
| +template <typename R>
|
| +void FileStream::AsyncContext::OnIOCompleted(
|
| + const base::Callback<void(R)>& callback,
|
| + R* result,
|
| + FileErrorSource source) {
|
| + CheckForIOError(result, source);
|
| + OnAsyncCompleted(callback, result);
|
| +}
|
| +
|
| +template <typename R>
|
| +void FileStream::AsyncContext::OnAsyncCompleted(
|
| + const base::Callback<void(R)>& callback,
|
| + R* result) {
|
| + if (destroyed_) {
|
| + DeleteAbandoned();
|
| + } else {
|
| + // Reset this before Run() as Run() may issue a new async operation.
|
| + async_in_progress_ = false;
|
| + callback.Run(*result);
|
| }
|
| }
|
|
|
| +void FileStream::AsyncContext::DeleteAbandoned() {
|
| + if (file_ != base::kInvalidPlatformFileValue) {
|
| + const bool posted = base::WorkerPool::PostTask(
|
| + FROM_HERE,
|
| + // Context should be deleted after closing, thus Owned().
|
| + base::Bind(&AsyncContext::CloseFileImpl, base::Owned(this)),
|
| + true /* task_is_slow */);
|
| + DCHECK(posted);
|
| + } else {
|
| + delete this;
|
| + }
|
| +}
|
| +
|
| } // namespace net
|
|
|