summaryrefslogtreecommitdiffstats
path: root/net/base
diff options
context:
space:
mode:
authorpaivanof@gmail.com <paivanof@gmail.com@0039d316-1c4b-4281-b951-d872f2087c98>2012-11-06 00:29:51 +0000
committerpaivanof@gmail.com <paivanof@gmail.com@0039d316-1c4b-4281-b951-d872f2087c98>2012-11-06 00:29:51 +0000
commitaab1b9ead19f21af4f752c4a52beed65009d96fb (patch)
tree3679bc3c3bd80678a9fb2ab383aa6834679506d0 /net/base
parent25524c044d4aefd6902d4245b66980c46669e145 (diff)
downloadchromium_src-aab1b9ead19f21af4f752c4a52beed65009d96fb.zip
chromium_src-aab1b9ead19f21af4f752c4a52beed65009d96fb.tar.gz
chromium_src-aab1b9ead19f21af4f752c4a52beed65009d96fb.tar.bz2
net: Implement canceling of all async operations in FileStream.
Canceling of async operations allows to not wait for their completion in FileStream's destructor. Other related changes include: - Got rid of FileStream::Close() and FileStream::CloseSync() methods because reuse of FileStream object doesn't make much sense, it should be destroyed instead. - Changed FileStream to always acquire ownership of the PlatformFile it was given. Fixed usages of FileStream where no ownership was assumed, introduced new helper functions in base/platform_file.h on the way. - FileStream's destructor now always closes the file. If file was opened with PLATFORM_FILE_ASYNC then actual closing is done asynchronously, destructor doesn't wait for that and returns immediately. When file was opened without PLATFORM_FILE_ASYNC closing is done synchronously and the thread doing that should be allowed to do IO operations. - Implementation of FileStream is refactored. FileStream is now just a wrapper around internal object that does all actual work and that can be easily orphaned in the destructor to not block on the actual file descriptor closing. All platform-independent code is extracted into a special file and amount of platform-dependent code is minimized. BUG=115067, 112474 TEST=net_unittests Review URL: https://chromiumcodereview.appspot.com/10701050 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@166091 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net/base')
-rw-r--r--net/base/file_stream.cc234
-rw-r--r--net/base/file_stream.h49
-rw-r--r--net/base/file_stream_context.cc231
-rw-r--r--net/base/file_stream_context.h230
-rw-r--r--net/base/file_stream_context_posix.cc176
-rw-r--r--net/base/file_stream_context_win.cc237
-rw-r--r--net/base/file_stream_posix.cc683
-rw-r--r--net/base/file_stream_posix.h83
-rw-r--r--net/base/file_stream_unittest.cc381
-rw-r--r--net/base/file_stream_win.cc778
-rw-r--r--net/base/file_stream_win.h104
-rw-r--r--net/base/net_log_event_type_list.h4
-rw-r--r--net/base/upload_file_element_reader.cc1
13 files changed, 1167 insertions, 2024 deletions
diff --git a/net/base/file_stream.cc b/net/base/file_stream.cc
index df18ad0..05f1460 100644
--- a/net/base/file_stream.cc
+++ b/net/base/file_stream.cc
@@ -4,99 +4,261 @@
#include "net/base/file_stream.h"
+#include "base/location.h"
+#include "base/message_loop_proxy.h"
+#include "base/task_runner_util.h"
+#include "base/threading/thread_restrictions.h"
+#include "base/threading/worker_pool.h"
+#include "net/base/file_stream_context.h"
+#include "net/base/file_stream_net_log_parameters.h"
+#include "net/base/net_errors.h"
+
namespace net {
-FileStream::FileStream(net::NetLog* net_log)
- : impl_(net_log) {
+FileStream::FileStream(NetLog* net_log)
+ /* To allow never opened stream to be destroyed on any thread we set flags
+ as if stream was opened asynchronously. */
+ : open_flags_(base::PLATFORM_FILE_ASYNC),
+ bound_net_log_(BoundNetLog::Make(net_log, NetLog::SOURCE_FILESTREAM)),
+ context_(new Context(bound_net_log_)) {
+ bound_net_log_.BeginEvent(NetLog::TYPE_FILE_STREAM_ALIVE);
}
-FileStream::FileStream(
- base::PlatformFile file, int flags, net::NetLog* net_log)
- : impl_(file, flags, net_log) {
+FileStream::FileStream(base::PlatformFile file, int flags, NetLog* net_log)
+ : open_flags_(flags),
+ bound_net_log_(BoundNetLog::Make(net_log, NetLog::SOURCE_FILESTREAM)),
+ context_(new Context(file, bound_net_log_, open_flags_)) {
+ bound_net_log_.BeginEvent(NetLog::TYPE_FILE_STREAM_ALIVE);
}
FileStream::~FileStream() {
-}
-
-void FileStream::Close(const CompletionCallback& callback) {
- impl_.Close(callback);
-}
+ if (!is_async()) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ context_->CloseSync();
+ context_.reset();
+ } else {
+ context_.release()->Orphan();
+ }
-void FileStream::CloseSync() {
- impl_.CloseSync();
+ bound_net_log_.EndEvent(NetLog::TYPE_FILE_STREAM_ALIVE);
}
int FileStream::Open(const FilePath& path, int open_flags,
const CompletionCallback& callback) {
- return impl_.Open(path, open_flags, callback);
+ if (IsOpen()) {
+ DLOG(FATAL) << "File is already open!";
+ return ERR_UNEXPECTED;
+ }
+
+ open_flags_ = open_flags;
+ DCHECK(is_async());
+ context_->OpenAsync(path, open_flags, callback);
+ return ERR_IO_PENDING;
}
int FileStream::OpenSync(const FilePath& path, int open_flags) {
- return impl_.OpenSync(path, open_flags);
+ base::ThreadRestrictions::AssertIOAllowed();
+
+ if (IsOpen()) {
+ DLOG(FATAL) << "File is already open!";
+ return ERR_UNEXPECTED;
+ }
+
+ open_flags_ = open_flags;
+ // TODO(satorux): Put a DCHECK once all async clients are migrated
+ // to use Open(). crbug.com/114783
+ //
+ // DCHECK(!is_async());
+ return context_->OpenSync(path, open_flags_);
}
bool FileStream::IsOpen() const {
- return impl_.IsOpen();
+ return context_->file() != base::kInvalidPlatformFileValue;
}
-int FileStream::Seek(Whence whence, int64 offset,
+int FileStream::Seek(Whence whence,
+ int64 offset,
const Int64CompletionCallback& callback) {
- return impl_.Seek(whence, offset, callback);
+ if (!IsOpen())
+ return ERR_UNEXPECTED;
+
+ // Make sure we're async.
+ DCHECK(is_async());
+ context_->SeekAsync(whence, offset, callback);
+ return ERR_IO_PENDING;
}
int64 FileStream::SeekSync(Whence whence, int64 offset) {
- return impl_.SeekSync(whence, 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(!is_async() || !context_->async_in_progress());
+ return context_->SeekSync(whence, offset);
}
int64 FileStream::Available() {
- return impl_.Available();
+ base::ThreadRestrictions::AssertIOAllowed();
+
+ if (!IsOpen())
+ return ERR_UNEXPECTED;
+
+ int64 cur_pos = SeekSync(FROM_CURRENT, 0);
+ if (cur_pos < 0)
+ return cur_pos;
+
+ int64 size = context_->GetFileSize();
+ if (size < 0)
+ return size;
+
+ DCHECK_GT(size, cur_pos);
+ return size - cur_pos;
}
-int FileStream::Read(
- IOBuffer* in_buf, int buf_len, const CompletionCallback& callback) {
- return impl_.Read(in_buf, buf_len, callback);
+int FileStream::Read(IOBuffer* buf,
+ int buf_len,
+ const CompletionCallback& callback) {
+ if (!IsOpen())
+ return ERR_UNEXPECTED;
+
+ // read(..., 0) will return 0, which indicates end-of-file.
+ DCHECK_GT(buf_len, 0);
+ DCHECK(open_flags_ & base::PLATFORM_FILE_READ);
+ DCHECK(is_async());
+
+ return context_->ReadAsync(buf, buf_len, callback);
}
int FileStream::ReadSync(char* buf, int buf_len) {
- return impl_.ReadSync(buf, buf_len);
+ base::ThreadRestrictions::AssertIOAllowed();
+
+ if (!IsOpen())
+ return ERR_UNEXPECTED;
+
+ DCHECK(!is_async());
+ // read(..., 0) will return 0, which indicates end-of-file.
+ DCHECK_GT(buf_len, 0);
+ DCHECK(open_flags_ & base::PLATFORM_FILE_READ);
+
+ return context_->ReadSync(buf, buf_len);
}
int FileStream::ReadUntilComplete(char *buf, int buf_len) {
- return impl_.ReadUntilComplete(buf, buf_len);
+ base::ThreadRestrictions::AssertIOAllowed();
+
+ int to_read = buf_len;
+ int bytes_total = 0;
+
+ 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 FileStream::Write(
- IOBuffer* buf, int buf_len, const CompletionCallback& callback) {
- return impl_.Write(buf, buf_len, callback);
+int FileStream::Write(IOBuffer* buf,
+ int buf_len,
+ const CompletionCallback& callback) {
+ if (!IsOpen())
+ return ERR_UNEXPECTED;
+
+ DCHECK(is_async());
+ DCHECK(open_flags_ & base::PLATFORM_FILE_WRITE);
+ // write(..., 0) will return 0, which indicates end-of-file.
+ DCHECK_GT(buf_len, 0);
+
+ return context_->WriteAsync(buf, buf_len, callback);
}
int FileStream::WriteSync(const char* buf, int buf_len) {
- return impl_.WriteSync(buf, buf_len);
+ base::ThreadRestrictions::AssertIOAllowed();
+
+ if (!IsOpen())
+ return ERR_UNEXPECTED;
+
+ DCHECK(!is_async());
+ DCHECK(open_flags_ & base::PLATFORM_FILE_WRITE);
+ // write(..., 0) will return 0, which indicates end-of-file.
+ DCHECK_GT(buf_len, 0);
+
+ return context_->WriteSync(buf, buf_len);
}
int64 FileStream::Truncate(int64 bytes) {
- return impl_.Truncate(bytes);
+ base::ThreadRestrictions::AssertIOAllowed();
+
+ 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.
+ return context_->Truncate(bytes);
}
int FileStream::Flush(const CompletionCallback& callback) {
- return impl_.Flush(callback);
+ if (!IsOpen())
+ return ERR_UNEXPECTED;
+
+ DCHECK(open_flags_ & base::PLATFORM_FILE_WRITE);
+ // Make sure we're async.
+ DCHECK(is_async());
+
+ context_->FlushAsync(callback);
+ return ERR_IO_PENDING;
}
int FileStream::FlushSync() {
- return impl_.FlushSync();
+ base::ThreadRestrictions::AssertIOAllowed();
+
+ if (!IsOpen())
+ return ERR_UNEXPECTED;
+
+ DCHECK(open_flags_ & base::PLATFORM_FILE_WRITE);
+ return context_->FlushSync();
}
void FileStream::EnableErrorStatistics() {
- impl_.EnableErrorStatistics();
+ context_->set_record_uma(true);
}
-void FileStream::SetBoundNetLogSource(
- const net::BoundNetLog& owner_bound_net_log) {
- impl_.SetBoundNetLogSource(owner_bound_net_log);
+void FileStream::SetBoundNetLogSource(const BoundNetLog& owner_bound_net_log) {
+ if ((owner_bound_net_log.source().id == NetLog::Source::kInvalidId) &&
+ (bound_net_log_.source().id == NetLog::Source::kInvalidId)) {
+ // Both |BoundNetLog|s are invalid.
+ return;
+ }
+
+ // Should never connect to itself.
+ DCHECK_NE(bound_net_log_.source().id, owner_bound_net_log.source().id);
+
+ bound_net_log_.AddEvent(NetLog::TYPE_FILE_STREAM_BOUND_TO_OWNER,
+ owner_bound_net_log.source().ToEventParametersCallback());
+
+ owner_bound_net_log.AddEvent(NetLog::TYPE_FILE_STREAM_SOURCE,
+ bound_net_log_.source().ToEventParametersCallback());
}
base::PlatformFile FileStream::GetPlatformFileForTesting() {
- return impl_.GetPlatformFileForTesting();
+ return context_->file();
}
} // namespace net
diff --git a/net/base/file_stream.h b/net/base/file_stream.h
index f4cfff2..5c64792 100644
--- a/net/base/file_stream.h
+++ b/net/base/file_stream.h
@@ -15,11 +15,6 @@
#include "net/base/file_stream_whence.h"
#include "net/base/net_export.h"
#include "net/base/net_log.h"
-#if defined(OS_WIN)
-#include "net/base/file_stream_win.h"
-#elif defined(OS_POSIX)
-#include "net/base/file_stream_posix.h"
-#endif
class FilePath;
@@ -39,32 +34,13 @@ class NET_EXPORT FileStream {
// opened.
// |net_log| is the net log pointer to use to create a |BoundNetLog|. May be
// NULL if logging is not needed.
- // The already opened file will not be automatically closed when FileStream
- // is destructed.
+ // Note: the new FileStream object takes ownership of the PlatformFile and
+ // will close it on destruction.
FileStream(base::PlatformFile file, int flags, net::NetLog* net_log);
- // If the file stream was opened with Open() or OpenSync(), the underlying
- // file will be closed automatically by the destructor, if not closed
- // manually.
+ // The underlying file is closed automatically.
virtual ~FileStream();
- // Call this method to close the FileStream, which was previously opened in
- // the async mode (PLATFORM_FILE_ASYNC) asynchronously.
- //
- // Once the operation is done, |callback| will be run on the thread where
- // Close() was called, with OK (i.e. an error is not propagated just like
- // CloseSync() does not).
- //
- // It is not OK to call Close() multiple times. The behavior is not defined.
- // Note that there must never be any pending async operations.
- virtual void Close(const CompletionCallback& callback);
-
- // Call this method to close the FileStream synchronously.
- // It is OK to call CloseSync() multiple times. Redundant calls are
- // ignored. Note that if there are any pending async operations, they'll
- // be aborted.
- virtual void CloseSync();
-
// Call this method to open the FileStream asynchronously. The remaining
// methods cannot be used unless the file is opened successfully. Returns
// ERR_IO_PENDING if the operation is started. If the operation cannot be
@@ -237,11 +213,20 @@ class NET_EXPORT FileStream {
base::PlatformFile GetPlatformFileForTesting();
private:
-#if defined(OS_WIN)
- FileStreamWin impl_;
-#elif defined(OS_POSIX)
- FileStreamPosix impl_;
-#endif
+ class Context;
+
+ bool is_async() const { return !!(open_flags_ & base::PLATFORM_FILE_ASYNC); }
+
+ int open_flags_;
+ net::BoundNetLog bound_net_log_;
+
+ // Context performing I/O operations. It was extracted into separate class
+ // to perform asynchronous operations because FileStream can be destroyed
+ // before completion of async operation. Also if async FileStream is destroyed
+ // without explicit closing file should be closed asynchronously without
+ // delaying FileStream's destructor. To perform all that separate object is
+ // necessary.
+ scoped_ptr<Context> context_;
DISALLOW_COPY_AND_ASSIGN(FileStream);
};
diff --git a/net/base/file_stream_context.cc b/net/base/file_stream_context.cc
new file mode 100644
index 0000000..1ed7ef6
--- /dev/null
+++ b/net/base/file_stream_context.cc
@@ -0,0 +1,231 @@
+// Copyright (c) 2012 The Chromium 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 "net/base/file_stream_context.h"
+
+#include "base/location.h"
+#include "base/message_loop_proxy.h"
+#include "base/task_runner_util.h"
+#include "base/threading/thread_restrictions.h"
+#include "base/threading/worker_pool.h"
+#include "net/base/file_stream_net_log_parameters.h"
+#include "net/base/net_errors.h"
+
+namespace {
+
+void CallInt64ToInt(const net::CompletionCallback& callback, int64 result) {
+ callback.Run(static_cast<int>(result));
+}
+
+}
+
+namespace net {
+
+void FileStream::Context::Orphan() {
+ DCHECK(!orphaned_);
+
+ orphaned_ = true;
+ if (file_ != base::kInvalidPlatformFileValue)
+ bound_net_log_.EndEvent(NetLog::TYPE_FILE_STREAM_OPEN);
+
+ if (!async_in_progress_) {
+ CloseAndDelete();
+ } else if (file_ != base::kInvalidPlatformFileValue) {
+ CancelIo(file_);
+ }
+}
+
+void FileStream::Context::OpenAsync(const FilePath& path,
+ int open_flags,
+ const CompletionCallback& callback) {
+ DCHECK(!async_in_progress_);
+
+ BeginOpenEvent(path);
+
+ const bool posted = base::PostTaskAndReplyWithResult(
+ base::WorkerPool::GetTaskRunner(true /* task_is_slow */),
+ FROM_HERE,
+ base::Bind(&Context::OpenFileImpl,
+ base::Unretained(this), path, open_flags),
+ base::Bind(&Context::OnOpenCompleted,
+ base::Unretained(this), callback));
+ DCHECK(posted);
+
+ async_in_progress_ = true;
+}
+
+int FileStream::Context::OpenSync(const FilePath& path, int open_flags) {
+ DCHECK(!async_in_progress_);
+
+ BeginOpenEvent(path);
+ OpenResult result = OpenFileImpl(path, open_flags);
+ file_ = result.file;
+ if (file_ == base::kInvalidPlatformFileValue) {
+ result.error_code = ProcessOpenError(result.error_code);
+ } else {
+ // TODO(satorux): Remove this once all async clients are migrated to use
+ // Open(). crbug.com/114783
+ if (open_flags & base::PLATFORM_FILE_ASYNC)
+ OnAsyncFileOpened();
+ }
+ return result.error_code;
+}
+
+void FileStream::Context::CloseSync() {
+ DCHECK(!async_in_progress_);
+ if (file_ != base::kInvalidPlatformFileValue) {
+ base::ClosePlatformFile(file_);
+ file_ = base::kInvalidPlatformFileValue;
+ bound_net_log_.EndEvent(NetLog::TYPE_FILE_STREAM_OPEN);
+ }
+}
+
+void FileStream::Context::SeekAsync(Whence whence,
+ int64 offset,
+ const Int64CompletionCallback& callback) {
+ DCHECK(!async_in_progress_);
+
+ const bool posted = base::PostTaskAndReplyWithResult(
+ base::WorkerPool::GetTaskRunner(true /* task is slow */),
+ FROM_HERE,
+ base::Bind(&Context::SeekFileImpl,
+ base::Unretained(this), whence, offset),
+ base::Bind(&Context::ProcessAsyncResult,
+ base::Unretained(this), callback, FILE_ERROR_SOURCE_SEEK));
+ DCHECK(posted);
+
+ async_in_progress_ = true;
+}
+
+int64 FileStream::Context::SeekSync(Whence whence, int64 offset) {
+ int64 result = SeekFileImpl(whence, offset);
+ CheckForIOError(&result, FILE_ERROR_SOURCE_SEEK);
+ return result;
+}
+
+void FileStream::Context::FlushAsync(const CompletionCallback& callback) {
+ DCHECK(!async_in_progress_);
+
+ const bool posted = base::PostTaskAndReplyWithResult(
+ base::WorkerPool::GetTaskRunner(true /* task is slow */),
+ FROM_HERE,
+ base::Bind(&Context::FlushFileImpl,
+ base::Unretained(this)),
+ base::Bind(&Context::ProcessAsyncResult,
+ base::Unretained(this), IntToInt64(callback),
+ FILE_ERROR_SOURCE_FLUSH));
+ DCHECK(posted);
+
+ async_in_progress_ = true;
+}
+
+int FileStream::Context::FlushSync() {
+ int64 result = FlushFileImpl();
+ CheckForIOError(&result, FILE_ERROR_SOURCE_FLUSH);
+ return result;
+}
+
+int FileStream::Context::RecordAndMapError(int error,
+ FileErrorSource source) const {
+ // 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 != ERROR_BAD_FILE);
+ Error net_error = MapSystemError(error);
+
+ if (!orphaned_) {
+ bound_net_log_.AddEvent(NetLog::TYPE_FILE_STREAM_ERROR,
+ base::Bind(&NetLogFileStreamErrorCallback,
+ source, error, net_error));
+ }
+ RecordFileError(error, source, record_uma_);
+ return net_error;
+}
+
+void FileStream::Context::BeginOpenEvent(const FilePath& path) {
+ std::string file_name = path.AsUTF8Unsafe();
+ bound_net_log_.BeginEvent(NetLog::TYPE_FILE_STREAM_OPEN,
+ NetLog::StringCallback("file_name", &file_name));
+}
+
+FileStream::Context::OpenResult FileStream::Context::OpenFileImpl(
+ const FilePath& path, int open_flags) {
+ OpenResult result;
+ result.error_code = OK;
+ result.file = base::CreatePlatformFile(path, open_flags, NULL, NULL);
+ if (result.file == base::kInvalidPlatformFileValue)
+ result.error_code = GetLastErrno();
+
+ return result;
+}
+
+int FileStream::Context::ProcessOpenError(int error_code) {
+ bound_net_log_.EndEvent(NetLog::TYPE_FILE_STREAM_OPEN);
+ return RecordAndMapError(error_code, FILE_ERROR_SOURCE_OPEN);
+}
+
+void FileStream::Context::OnOpenCompleted(const CompletionCallback& callback,
+ OpenResult result) {
+ file_ = result.file;
+ if (file_ == base::kInvalidPlatformFileValue)
+ result.error_code = ProcessOpenError(result.error_code);
+ else if (!orphaned_)
+ OnAsyncFileOpened();
+ OnAsyncCompleted(IntToInt64(callback), result.error_code);
+}
+
+void FileStream::Context::CloseAndDelete() {
+ DCHECK(!async_in_progress_);
+
+ if (file_ == base::kInvalidPlatformFileValue) {
+ delete this;
+ } else {
+ const bool posted = base::WorkerPool::PostTaskAndReply(
+ FROM_HERE,
+ base::Bind(base::IgnoreResult(&base::ClosePlatformFile), file_),
+ base::Bind(&Context::OnCloseCompleted, base::Unretained(this)),
+ true /* task_is_slow */);
+ DCHECK(posted);
+ file_ = base::kInvalidPlatformFileValue;
+ }
+}
+
+void FileStream::Context::OnCloseCompleted() {
+ delete this;
+}
+
+Int64CompletionCallback FileStream::Context::IntToInt64(
+ const CompletionCallback& callback) {
+ return base::Bind(&CallInt64ToInt, callback);
+}
+
+void FileStream::Context::CheckForIOError(int64* result,
+ FileErrorSource source) {
+ if (*result < 0)
+ *result = RecordAndMapError(static_cast<int>(*result), source);
+}
+
+void FileStream::Context::ProcessAsyncResult(
+ const Int64CompletionCallback& callback,
+ FileErrorSource source,
+ int64 result) {
+ CheckForIOError(&result, source);
+ OnAsyncCompleted(callback, result);
+}
+
+void FileStream::Context::OnAsyncCompleted(
+ const Int64CompletionCallback& callback,
+ int64 result) {
+ // Reset this before Run() as Run() may issue a new async operation. Also it
+ // should be reset before CloseAsync() because it shouldn't run if any async
+ // operation is in progress.
+ async_in_progress_ = false;
+ if (orphaned_)
+ CloseAndDelete();
+ else
+ callback.Run(result);
+}
+
+} // namespace net
+
diff --git a/net/base/file_stream_context.h b/net/base/file_stream_context.h
new file mode 100644
index 0000000..244c25f
--- /dev/null
+++ b/net/base/file_stream_context.h
@@ -0,0 +1,230 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file defines FileStream::Context class.
+// The general design of FileStream is as follows: file_stream.h defines
+// FileStream class which basically is just an "wrapper" not containing any
+// specific implementation details. It re-routes all its method calls to
+// the instance of FileStream::Context (FileStream holds a scoped_ptr to
+// FileStream::Context instance). Context was extracted into a different class
+// to be able to do and finish all async operations even when FileStream
+// instance is deleted. So FileStream's destructor can schedule file
+// closing to be done by Context in WorkerPool and then just return (releasing
+// Context pointer from scoped_ptr) without waiting for actual closing to
+// complete.
+// Implementation of FileStream::Context is divided in two parts: some methods
+// and members are platform-independent and some depend on the platform. This
+// header file contains the complete definition of Context class including all
+// platform-dependent parts (because of that it has a lot of #if-#else
+// branching). Implementations of all platform-independent methods are
+// located in file_stream_context.cc, and all platform-dependent methods are
+// in file_stream_context_{win,posix}.cc. This separation provides better
+// readability of Context's code. And we tried to make as much Context code
+// platform-independent as possible. So file_stream_context_{win,posix}.cc are
+// much smaller than file_stream_context.cc now.
+
+#ifndef NET_BASE_FILE_STREAM_CONTEXT_H_
+#define NET_BASE_FILE_STREAM_CONTEXT_H_
+
+#include "base/message_loop.h"
+#include "base/platform_file.h"
+#include "net/base/completion_callback.h"
+#include "net/base/file_stream.h"
+#include "net/base/file_stream_metrics.h"
+#include "net/base/file_stream_whence.h"
+#include "net/base/net_log.h"
+
+#if defined(OS_POSIX)
+#include <errno.h>
+#endif
+
+class FilePath;
+
+namespace net {
+
+class IOBuffer;
+
+#if defined(OS_WIN)
+class FileStream::Context : public MessageLoopForIO::IOHandler {
+#elif defined(OS_POSIX)
+class FileStream::Context {
+#endif
+ public:
+ ////////////////////////////////////////////////////////////////////////////
+ // Platform-dependent methods implemented in
+ // file_stream_context_{win,posix}.cc.
+ ////////////////////////////////////////////////////////////////////////////
+
+ explicit Context(const BoundNetLog& bound_net_log);
+ Context(base::PlatformFile file,
+ const BoundNetLog& bound_net_log,
+ int open_flags);
+#if defined(OS_WIN)
+ virtual ~Context();
+#elif defined(OS_POSIX)
+ ~Context();
+#endif
+
+ int64 GetFileSize() const;
+
+ int ReadAsync(IOBuffer* buf,
+ int buf_len,
+ const CompletionCallback& callback);
+ int ReadSync(char* buf, int buf_len);
+
+ int WriteAsync(IOBuffer* buf,
+ int buf_len,
+ const CompletionCallback& callback);
+ int WriteSync(const char* buf, int buf_len);
+
+ int Truncate(int64 bytes);
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Inline methods.
+ ////////////////////////////////////////////////////////////////////////////
+
+ void set_record_uma(bool value) { record_uma_ = value; }
+ base::PlatformFile file() const { return file_; }
+ bool async_in_progress() const { return async_in_progress_; }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Platform-independent methods implemented in file_stream_context.cc.
+ ////////////////////////////////////////////////////////////////////////////
+
+ // Destroys the context. It can be deleted in the method or deletion can be
+ // deferred if some asynchronous operation is now in progress or if file is
+ // not closed yet.
+ void Orphan();
+
+ void OpenAsync(const FilePath& path,
+ int open_flags,
+ const CompletionCallback& callback);
+ int OpenSync(const FilePath& path, int open_flags);
+
+ void CloseSync();
+
+ void SeekAsync(Whence whence,
+ int64 offset,
+ const Int64CompletionCallback& callback);
+ int64 SeekSync(Whence whence, int64 offset);
+
+ void FlushAsync(const CompletionCallback& callback);
+ int FlushSync();
+
+ private:
+ ////////////////////////////////////////////////////////////////////////////
+ // Error code that is platform-dependent but is used in the platform-
+ // independent code implemented in file_stream_context.cc.
+ ////////////////////////////////////////////////////////////////////////////
+ enum {
+#if defined(OS_WIN)
+ ERROR_BAD_FILE = ERROR_INVALID_HANDLE
+#elif defined(OS_POSIX)
+ ERROR_BAD_FILE = EBADF
+#endif
+ };
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Platform-independent methods implemented in file_stream_context.cc.
+ ////////////////////////////////////////////////////////////////////////////
+
+ struct OpenResult {
+ base::PlatformFile file;
+ int error_code;
+ };
+
+ // Map system error into network error code and log it with |bound_net_log_|.
+ int RecordAndMapError(int error, FileErrorSource source) const;
+
+ void BeginOpenEvent(const FilePath& path);
+
+ OpenResult OpenFileImpl(const FilePath& path, int open_flags);
+
+ int ProcessOpenError(int error_code);
+ void OnOpenCompleted(const CompletionCallback& callback, OpenResult result);
+
+ void CloseAndDelete();
+ void OnCloseCompleted();
+
+ Int64CompletionCallback IntToInt64(const CompletionCallback& callback);
+
+ // Checks for IO error that probably happened in async methods.
+ // If there was error reports it.
+ void CheckForIOError(int64* result, FileErrorSource source);
+
+ // Called when asynchronous Seek() is completed.
+ // Reports error if needed and calls callback.
+ void ProcessAsyncResult(const Int64CompletionCallback& callback,
+ FileErrorSource source,
+ int64 result);
+
+ // Called when asynchronous Open() or Seek()
+ // is completed. |result| contains the result or a network error code.
+ void OnAsyncCompleted(const Int64CompletionCallback& callback, int64 result);
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Helper stuff which is platform-dependent but is used in the platform-
+ // independent code implemented in file_stream_context.cc. These helpers were
+ // introduced solely to implement as much of the Context methods as
+ // possible independently from platform.
+ ////////////////////////////////////////////////////////////////////////////
+
+#if defined(OS_WIN)
+ int GetLastErrno() { return GetLastError(); }
+ void OnAsyncFileOpened();
+#elif defined(OS_POSIX)
+ int GetLastErrno() { return errno; }
+ void OnAsyncFileOpened() {}
+ void CancelIo(base::PlatformFile) {}
+#endif
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Platform-dependent methods implemented in
+ // file_stream_context_{win,posix}.cc.
+ ////////////////////////////////////////////////////////////////////////////
+
+ // Adjusts the position from where the data is read.
+ int64 SeekFileImpl(Whence whence, int64 offset);
+
+ // Flushes all data written to the stream.
+ int64 FlushFileImpl();
+
+#if defined(OS_WIN)
+ void IOCompletionIsPending(const CompletionCallback& callback, IOBuffer* buf);
+
+ // Implementation of MessageLoopForIO::IOHandler
+ virtual void OnIOCompleted(MessageLoopForIO::IOContext* context,
+ DWORD bytes_read,
+ DWORD error) OVERRIDE;
+#elif defined(OS_POSIX)
+ // ReadFileImpl() is a simple wrapper around read() that handles EINTR
+ // signals and calls RecordAndMapError() to map errno to net error codes.
+ int64 ReadFileImpl(scoped_refptr<IOBuffer> buf, int buf_len);
+
+ // WriteFileImpl() 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.
+ int64 WriteFileImpl(scoped_refptr<IOBuffer> buf, int buf_len);
+#endif
+
+ base::PlatformFile file_;
+ bool record_uma_;
+ bool async_in_progress_;
+ bool orphaned_;
+ BoundNetLog bound_net_log_;
+
+#if defined(OS_WIN)
+ MessageLoopForIO::IOContext io_context_;
+ CompletionCallback callback_;
+ scoped_refptr<IOBuffer> in_flight_buf_;
+ FileErrorSource error_source_;
+#endif
+
+ DISALLOW_COPY_AND_ASSIGN(Context);
+};
+
+} // namespace net
+
+#endif // NET_BASE_FILE_STREAM_CONTEXT_H_
+
diff --git a/net/base/file_stream_context_posix.cc b/net/base/file_stream_context_posix.cc
new file mode 100644
index 0000000..8316215
--- /dev/null
+++ b/net/base/file_stream_context_posix.cc
@@ -0,0 +1,176 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// For 64-bit file access (off_t = off64_t, lseek64, etc).
+#define _FILE_OFFSET_BITS 64
+
+#include "net/base/file_stream_context.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "base/basictypes.h"
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/callback.h"
+#include "base/eintr_wrapper.h"
+#include "base/file_path.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/metrics/histogram.h"
+#include "base/task_runner_util.h"
+#include "base/threading/worker_pool.h"
+#include "net/base/io_buffer.h"
+#include "net/base/net_errors.h"
+
+#if defined(OS_ANDROID)
+// Android's bionic libc only supports the LFS transitional API.
+#define off_t off64_t
+#define lseek lseek64
+#define stat stat64
+#define fstat fstat64
+#endif
+
+namespace net {
+
+// We cast back and forth, so make sure it's the size we're expecting.
+COMPILE_ASSERT(sizeof(int64) == sizeof(off_t), off_t_64_bit);
+
+// Make sure our Whence mappings match the system headers.
+COMPILE_ASSERT(FROM_BEGIN == SEEK_SET &&
+ FROM_CURRENT == SEEK_CUR &&
+ FROM_END == SEEK_END, whence_matches_system);
+
+FileStream::Context::Context(const BoundNetLog& bound_net_log)
+ : file_(base::kInvalidPlatformFileValue),
+ record_uma_(false),
+ async_in_progress_(false),
+ orphaned_(false),
+ bound_net_log_(bound_net_log) {
+}
+
+FileStream::Context::Context(base::PlatformFile file,
+ const BoundNetLog& bound_net_log,
+ int /* open_flags */)
+ : file_(file),
+ record_uma_(false),
+ async_in_progress_(false),
+ orphaned_(false),
+ bound_net_log_(bound_net_log) {
+}
+
+FileStream::Context::~Context() {
+}
+
+int64 FileStream::Context::GetFileSize() const {
+ struct stat info;
+ if (fstat(file_, &info) != 0)
+ return RecordAndMapError(errno, FILE_ERROR_SOURCE_GET_SIZE);
+
+ return static_cast<int64>(info.st_size);
+}
+
+int FileStream::Context::ReadAsync(IOBuffer* in_buf,
+ int buf_len,
+ const CompletionCallback& callback) {
+ DCHECK(!async_in_progress_);
+
+ scoped_refptr<IOBuffer> buf = in_buf;
+ const bool posted = base::PostTaskAndReplyWithResult(
+ base::WorkerPool::GetTaskRunner(true /* task is slow */),
+ FROM_HERE,
+ base::Bind(&Context::ReadFileImpl,
+ base::Unretained(this), buf, buf_len),
+ base::Bind(&Context::ProcessAsyncResult,
+ base::Unretained(this), IntToInt64(callback),
+ FILE_ERROR_SOURCE_READ));
+ DCHECK(posted);
+
+ async_in_progress_ = true;
+ return ERR_IO_PENDING;
+}
+
+int FileStream::Context::ReadSync(char* in_buf, int buf_len) {
+ scoped_refptr<IOBuffer> buf = new WrappedIOBuffer(in_buf);
+ int64 result = ReadFileImpl(buf, buf_len);
+ CheckForIOError(&result, FILE_ERROR_SOURCE_READ);
+ return result;
+}
+
+int FileStream::Context::WriteAsync(IOBuffer* in_buf,
+ int buf_len,
+ const CompletionCallback& callback) {
+ DCHECK(!async_in_progress_);
+
+ scoped_refptr<IOBuffer> buf = in_buf;
+ const bool posted = base::PostTaskAndReplyWithResult(
+ base::WorkerPool::GetTaskRunner(true /* task is slow */),
+ FROM_HERE,
+ base::Bind(&Context::WriteFileImpl,
+ base::Unretained(this), buf, buf_len),
+ base::Bind(&Context::ProcessAsyncResult,
+ base::Unretained(this), IntToInt64(callback),
+ FILE_ERROR_SOURCE_WRITE));
+ DCHECK(posted);
+
+ async_in_progress_ = true;
+ return ERR_IO_PENDING;
+}
+
+int FileStream::Context::WriteSync(const char* in_buf, int buf_len) {
+ scoped_refptr<IOBuffer> buf = new WrappedIOBuffer(in_buf);
+ int64 result = WriteFileImpl(buf, buf_len);
+ CheckForIOError(&result, FILE_ERROR_SOURCE_WRITE);
+ return result;
+}
+
+int FileStream::Context::Truncate(int64 bytes) {
+ int result = ftruncate(file_, bytes);
+ if (result == 0)
+ return bytes;
+
+ return RecordAndMapError(errno, FILE_ERROR_SOURCE_SET_EOF);
+}
+
+int64 FileStream::Context::SeekFileImpl(Whence whence, int64 offset) {
+ off_t res = lseek(file_, static_cast<off_t>(offset),
+ static_cast<int>(whence));
+ if (res == static_cast<off_t>(-1))
+ return errno;
+
+ return res;
+}
+
+int64 FileStream::Context::FlushFileImpl() {
+ ssize_t res = HANDLE_EINTR(fsync(file_));
+ if (res == -1)
+ return errno;
+
+ return res;
+}
+
+int64 FileStream::Context::ReadFileImpl(scoped_refptr<IOBuffer> buf,
+ int buf_len) {
+ // 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)
+ return errno;
+
+ return res;
+}
+
+int64 FileStream::Context::WriteFileImpl(scoped_refptr<IOBuffer> buf,
+ int buf_len) {
+ ssize_t res = HANDLE_EINTR(write(file_, buf->data(), buf_len));
+ if (res == -1)
+ return errno;
+
+ return res;
+}
+
+} // namespace net
diff --git a/net/base/file_stream_context_win.cc b/net/base/file_stream_context_win.cc
new file mode 100644
index 0000000..a514ab0
--- /dev/null
+++ b/net/base/file_stream_context_win.cc
@@ -0,0 +1,237 @@
+// Copyright (c) 2012 The Chromium 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 "net/base/file_stream_context.h"
+
+#include <windows.h>
+
+#include "base/file_path.h"
+#include "base/logging.h"
+#include "base/metrics/histogram.h"
+#include "base/task_runner_util.h"
+#include "base/threading/worker_pool.h"
+#include "net/base/io_buffer.h"
+#include "net/base/net_errors.h"
+
+namespace net {
+
+// Ensure that we can just use our Whence values directly.
+COMPILE_ASSERT(FROM_BEGIN == FILE_BEGIN, bad_whence_begin);
+COMPILE_ASSERT(FROM_CURRENT == FILE_CURRENT, bad_whence_current);
+COMPILE_ASSERT(FROM_END == FILE_END, bad_whence_end);
+
+namespace {
+
+void SetOffset(OVERLAPPED* overlapped, const LARGE_INTEGER& offset) {
+ overlapped->Offset = offset.LowPart;
+ overlapped->OffsetHigh = offset.HighPart;
+}
+
+void IncrementOffset(OVERLAPPED* overlapped, DWORD count) {
+ LARGE_INTEGER offset;
+ offset.LowPart = overlapped->Offset;
+ offset.HighPart = overlapped->OffsetHigh;
+ offset.QuadPart += static_cast<LONGLONG>(count);
+ SetOffset(overlapped, offset);
+}
+
+} // namespace
+
+FileStream::Context::Context(const BoundNetLog& bound_net_log)
+ : io_context_(),
+ file_(base::kInvalidPlatformFileValue),
+ record_uma_(false),
+ async_in_progress_(false),
+ orphaned_(false),
+ bound_net_log_(bound_net_log),
+ error_source_(FILE_ERROR_SOURCE_COUNT) {
+ io_context_.handler = this;
+}
+
+FileStream::Context::Context(base::PlatformFile file,
+ const BoundNetLog& bound_net_log,
+ int open_flags)
+ : io_context_(),
+ file_(file),
+ record_uma_(false),
+ async_in_progress_(false),
+ orphaned_(false),
+ bound_net_log_(bound_net_log),
+ error_source_(FILE_ERROR_SOURCE_COUNT) {
+ io_context_.handler = this;
+ if (file_ != base::kInvalidPlatformFileValue &&
+ (open_flags & base::PLATFORM_FILE_ASYNC)) {
+ OnAsyncFileOpened();
+ }
+}
+
+FileStream::Context::~Context() {
+}
+
+int64 FileStream::Context::GetFileSize() const {
+ LARGE_INTEGER file_size;
+ if (!GetFileSizeEx(file_, &file_size)) {
+ DWORD error = GetLastError();
+ LOG(WARNING) << "GetFileSizeEx failed: " << error;
+ return RecordAndMapError(error, FILE_ERROR_SOURCE_GET_SIZE);
+ }
+
+ return file_size.QuadPart;
+}
+
+int FileStream::Context::ReadAsync(IOBuffer* buf,
+ int buf_len,
+ const CompletionCallback& callback) {
+ DCHECK(!async_in_progress_);
+ error_source_ = FILE_ERROR_SOURCE_READ;
+
+ int rv = 0;
+
+ DWORD bytes_read;
+ if (!ReadFile(file_, buf->data(), buf_len,
+ &bytes_read, &io_context_.overlapped)) {
+ DWORD error = GetLastError();
+ if (error == ERROR_IO_PENDING) {
+ IOCompletionIsPending(callback, buf);
+ rv = ERR_IO_PENDING;
+ } else if (error == ERROR_HANDLE_EOF) {
+ rv = 0; // Report EOF by returning 0 bytes read.
+ } else {
+ LOG(WARNING) << "ReadFile failed: " << error;
+ rv = RecordAndMapError(error, FILE_ERROR_SOURCE_READ);
+ }
+ } else {
+ IOCompletionIsPending(callback, buf);
+ rv = ERR_IO_PENDING;
+ }
+ return rv;
+}
+
+int FileStream::Context::ReadSync(char* buf, int buf_len) {
+ int rv = 0;
+
+ DWORD bytes_read;
+ if (!ReadFile(file_, buf, buf_len, &bytes_read, NULL)) {
+ DWORD error = GetLastError();
+ if (error == ERROR_HANDLE_EOF) {
+ rv = 0; // Report EOF by returning 0 bytes read.
+ } else {
+ LOG(WARNING) << "ReadFile failed: " << error;
+ rv = RecordAndMapError(error, FILE_ERROR_SOURCE_READ);
+ }
+ } else {
+ rv = static_cast<int>(bytes_read);
+ }
+ return rv;
+}
+
+int FileStream::Context::WriteAsync(IOBuffer* buf,
+ int buf_len,
+ const CompletionCallback& callback) {
+ error_source_ = FILE_ERROR_SOURCE_WRITE;
+
+ int rv = 0;
+ DWORD bytes_written = 0;
+ if (!WriteFile(file_, buf->data(), buf_len,
+ &bytes_written, &io_context_.overlapped)) {
+ DWORD error = GetLastError();
+ if (error == ERROR_IO_PENDING) {
+ IOCompletionIsPending(callback, buf);
+ rv = ERR_IO_PENDING;
+ } else {
+ LOG(WARNING) << "WriteFile failed: " << error;
+ rv = RecordAndMapError(error, FILE_ERROR_SOURCE_WRITE);
+ }
+ } else {
+ IOCompletionIsPending(callback, buf);
+ rv = ERR_IO_PENDING;
+ }
+ return rv;
+}
+
+int FileStream::Context::WriteSync(const char* buf, int buf_len) {
+ int rv = 0;
+ DWORD bytes_written = 0;
+ if (!WriteFile(file_, buf, buf_len, &bytes_written, NULL)) {
+ DWORD error = GetLastError();
+ LOG(WARNING) << "WriteFile failed: " << error;
+ rv = RecordAndMapError(error, FILE_ERROR_SOURCE_WRITE);
+ } else {
+ rv = static_cast<int>(bytes_written);
+ }
+ return rv;
+}
+
+int FileStream::Context::Truncate(int64 bytes) {
+ BOOL result = SetEndOfFile(file_);
+ if (result)
+ return bytes;
+
+ DWORD error = GetLastError();
+ LOG(WARNING) << "SetEndOfFile failed: " << error;
+ return RecordAndMapError(error, FILE_ERROR_SOURCE_SET_EOF);
+}
+
+void FileStream::Context::OnAsyncFileOpened() {
+ MessageLoopForIO::current()->RegisterIOHandler(file_, this);
+}
+
+int64 FileStream::Context::SeekFileImpl(Whence whence, int64 offset) {
+ LARGE_INTEGER distance, res;
+ distance.QuadPart = offset;
+ DWORD move_method = static_cast<DWORD>(whence);
+ if (SetFilePointerEx(file_, distance, &res, move_method)) {
+ SetOffset(&io_context_.overlapped, res);
+ return res.QuadPart;
+ }
+
+ return -static_cast<int>(GetLastError());
+}
+
+int64 FileStream::Context::FlushFileImpl() {
+ if (FlushFileBuffers(file_))
+ return OK;
+
+ return -static_cast<int>(GetLastError());
+}
+
+void FileStream::Context::IOCompletionIsPending(
+ const CompletionCallback& callback,
+ IOBuffer* buf) {
+ DCHECK(callback_.is_null());
+ callback_ = callback;
+ in_flight_buf_ = buf; // Hold until the async operation ends.
+ async_in_progress_ = true;
+}
+
+void FileStream::Context::OnIOCompleted(MessageLoopForIO::IOContext* context,
+ DWORD bytes_read,
+ DWORD error) {
+ DCHECK_EQ(&io_context_, context);
+ DCHECK(!callback_.is_null());
+ DCHECK(async_in_progress_);
+
+ async_in_progress_ = false;
+ if (orphaned_) {
+ callback_.Reset();
+ in_flight_buf_ = NULL;
+ CloseAndDelete();
+ return;
+ }
+
+ int result = static_cast<int>(bytes_read);
+ if (error && error != ERROR_HANDLE_EOF)
+ result = RecordAndMapError(error, error_source_);
+
+ if (bytes_read)
+ IncrementOffset(&io_context_.overlapped, bytes_read);
+
+ CompletionCallback temp_callback = callback_;
+ callback_.Reset();
+ scoped_refptr<IOBuffer> temp_buf = in_flight_buf_;
+ in_flight_buf_ = NULL;
+ temp_callback.Run(result);
+}
+
+} // namespace net
diff --git a/net/base/file_stream_posix.cc b/net/base/file_stream_posix.cc
deleted file mode 100644
index 5613fe3..0000000
--- a/net/base/file_stream_posix.cc
+++ /dev/null
@@ -1,683 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// For 64-bit file access (off_t = off64_t, lseek64, etc).
-#define _FILE_OFFSET_BITS 64
-
-#include "net/base/file_stream.h"
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <errno.h>
-
-#include "base/basictypes.h"
-#include "base/bind.h"
-#include "base/bind_helpers.h"
-#include "base/callback.h"
-#include "base/eintr_wrapper.h"
-#include "base/file_path.h"
-#include "base/logging.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"
-
-#if defined(OS_ANDROID)
-// Android's bionic libc only supports the LFS transitional API.
-#define off_t off64_t
-#define lseek lseek64
-#define stat stat64
-#define fstat fstat64
-#endif
-
-namespace net {
-
-// We cast back and forth, so make sure it's the size we're expecting.
-COMPILE_ASSERT(sizeof(int64) == sizeof(off_t), off_t_64_bit);
-
-// Make sure our Whence mappings match the system headers.
-COMPILE_ASSERT(FROM_BEGIN == SEEK_SET &&
- 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;
-}
-
-// Flushes a file using FlushFile() and signals the completion.
-void FlushFileAndSignal(base::PlatformFile file,
- int* result,
- bool record_uma,
- base::WaitableEvent* on_io_complete,
- const net::BoundNetLog& bound_net_log) {
- *result = FlushFile(file, record_uma, bound_net_log);
- on_io_complete->Signal();
-}
-
-// 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)
- : 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);
-}
-
-FileStreamPosix::FileStreamPosix(
- base::PlatformFile file, int flags, net::NetLog* net_log)
- : 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);
-}
-
-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 FileStreamPosix::Close(const CompletionCallback& callback) {
- 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 */));
-
- // 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.
- 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),
- true /* task_is_slow */);
-
- DCHECK(posted);
-}
-
-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 FileStreamPosix::Open(const FilePath& path, int open_flags,
- const CompletionCallback& callback) {
- if (IsOpen()) {
- DLOG(FATAL) << "File is already open!";
- return ERR_UNEXPECTED;
- }
-
- open_flags_ = open_flags;
- 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 */));
-
- // 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.
- 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;
-}
-
-int FileStreamPosix::OpenSync(const FilePath& path, int open_flags) {
- if (IsOpen()) {
- DLOG(FATAL) << "File is already open!";
- return ERR_UNEXPECTED;
- }
-
- 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;
-}
-
-int FileStreamPosix::Seek(Whence whence, int64 offset,
- const Int64CompletionCallback& callback) {
- if (!IsOpen())
- return ERR_UNEXPECTED;
-
- // 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)),
- true /* task is slow */);
- DCHECK(posted);
- return ERR_IO_PENDING;
-}
-
-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());
-
- off_t result = -1;
- SeekFile(file_, whence, offset, &result, record_uma_, bound_net_log_);
- 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;
-
- struct stat info;
- if (fstat(file_, &info) != 0) {
- return RecordAndMapError(errno,
- FILE_ERROR_SOURCE_GET_SIZE,
- record_uma_,
- bound_net_log_);
- }
-
- int64 size = static_cast<int64>(info.st_size);
- DCHECK_GT(size, cur_pos);
-
- return size - cur_pos;
-}
-
-int FileStreamPosix::Read(
- IOBuffer* in_buf, int buf_len, const CompletionCallback& callback) {
- if (!IsOpen())
- return ERR_UNEXPECTED;
-
- // 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)),
- true /* task is slow */);
- DCHECK(posted);
- 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 result = OK;
- ReadFile(file_, buf, buf_len, record_uma_, &result, bound_net_log_);
- return result;
-}
-
-int FileStreamPosix::ReadUntilComplete(char *buf, int buf_len) {
- int to_read = buf_len;
- int bytes_total = 0;
-
- 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)),
- true /* task is slow */);
- DCHECK(posted);
- 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 result = OK;
- WriteFile(file_, buf, buf_len, record_uma_, &result, bound_net_log_);
- return result;
-}
-
-int64 FileStreamPosix::Truncate(int64 bytes) {
- base::ThreadRestrictions::AssertIOAllowed();
-
- 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 result = ftruncate(file_, bytes);
- if (result == 0)
- return seek_position;
-
- return RecordAndMapError(errno,
- FILE_ERROR_SOURCE_SET_EOF,
- record_uma_,
- bound_net_log_);
-}
-
-int FileStreamPosix::Flush(const CompletionCallback& callback) {
- if (!IsOpen())
- return ERR_UNEXPECTED;
-
- // 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 */));
-
- int* result = new int(OK);
- const bool posted = base::WorkerPool::PostTaskAndReply(
- FROM_HERE,
- base::Bind(&FlushFileAndSignal, file_, result,
- record_uma_, 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;
-}
-
-int FileStreamPosix::FlushSync() {
- if (!IsOpen())
- return ERR_UNEXPECTED;
-
- return FlushFile(file_, record_uma_, bound_net_log_);
-}
-
-void FileStreamPosix::EnableErrorStatistics() {
- record_uma_ = true;
-}
-
-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.
- return;
- }
-
- // Should never connect to itself.
- DCHECK_NE(bound_net_log_.source().id, owner_bound_net_log.source().id);
-
- bound_net_log_.AddEvent(
- net::NetLog::TYPE_FILE_STREAM_BOUND_TO_OWNER,
- owner_bound_net_log.source().ToEventParametersCallback());
-
- owner_bound_net_log.AddEvent(
- net::NetLog::TYPE_FILE_STREAM_SOURCE,
- bound_net_log_.source().ToEventParametersCallback());
-}
-
-base::PlatformFile FileStreamPosix::GetPlatformFileForTesting() {
- return file_;
-}
-
-void FileStreamPosix::ResetOnIOComplete() {
- on_io_complete_.reset();
- weak_ptr_factory_.InvalidateWeakPtrs();
-}
-
-void FileStreamPosix::OnClosed(const CompletionCallback& callback) {
- file_ = base::kInvalidPlatformFileValue;
-
- // Reset this before Run() as Run() may issue a new async operation.
- ResetOnIOComplete();
- callback.Run(OK);
-}
-
-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();
- }
-}
-
-} // namespace net
diff --git a/net/base/file_stream_posix.h b/net/base/file_stream_posix.h
deleted file mode 100644
index 2ac7d63..0000000
--- a/net/base/file_stream_posix.h
+++ /dev/null
@@ -1,83 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// This file implements FileStream for POSIX.
-
-#ifndef NET_BASE_FILE_STREAM_POSIX_H_
-#define NET_BASE_FILE_STREAM_POSIX_H_
-
-#include "base/memory/scoped_ptr.h"
-#include "base/memory/weak_ptr.h"
-#include "base/platform_file.h"
-#include "net/base/completion_callback.h"
-#include "net/base/file_stream_whence.h"
-#include "net/base/net_export.h"
-#include "net/base/net_log.h"
-
-class FilePath;
-
-namespace base {
-class WaitableEvent;
-}
-
-namespace net {
-
-class IOBuffer;
-
-class NET_EXPORT FileStreamPosix {
- public:
- explicit FileStreamPosix(net::NetLog* net_log);
- FileStreamPosix(base::PlatformFile file, int flags, net::NetLog* net_log);
- ~FileStreamPosix();
-
- // FileStream implementations.
- void Close(const CompletionCallback& callback);
- void CloseSync();
- int Open(const FilePath& path, int open_flags,
- const CompletionCallback& callback);
- int OpenSync(const FilePath& path, int open_flags);
- bool IsOpen() const;
- int Seek(Whence whence, int64 offset,
- const Int64CompletionCallback& callback);
- int64 SeekSync(Whence whence, int64 offset);
- int64 Available();
- int Read(IOBuffer* buf, int buf_len, const CompletionCallback& callback);
- int ReadSync(char* buf, int buf_len);
- int ReadUntilComplete(char *buf, int buf_len);
- int Write(IOBuffer* buf, int buf_len, const CompletionCallback& callback);
- int WriteSync(const char* buf, int buf_len);
- int64 Truncate(int64 bytes);
- int Flush(const CompletionCallback& callback);
- int FlushSync();
- void EnableErrorStatistics();
- void SetBoundNetLogSource(
- const net::BoundNetLog& owner_bound_net_log);
- base::PlatformFile GetPlatformFileForTesting();
-
- // Resets on_io_complete_ and WeakPtr's.
- // Called when Read() or Write() is completed.
- void ResetOnIOComplete();
-
- private:
- // Called when the file_ is closed asynchronously.
- void OnClosed(const CompletionCallback& callback);
-
- // Waits until the in-flight async open/close/read/write operation is
- // complete.
- void WaitForIOCompletion();
-
- base::PlatformFile file_;
- int open_flags_;
- bool auto_closed_;
- bool record_uma_;
- net::BoundNetLog bound_net_log_;
- base::WeakPtrFactory<FileStreamPosix> weak_ptr_factory_;
- scoped_ptr<base::WaitableEvent> on_io_complete_;
-
- DISALLOW_COPY_AND_ASSIGN(FileStreamPosix);
-};
-
-} // namespace net
-
-#endif // NET_BASE_FILE_STREAM_POSIX_H
diff --git a/net/base/file_stream_unittest.cc b/net/base/file_stream_unittest.cc
index 1042784..f9882c8 100644
--- a/net/base/file_stream_unittest.cc
+++ b/net/base/file_stream_unittest.cc
@@ -33,46 +33,6 @@ IOBufferWithSize* CreateTestDataBuffer() {
return buf;
}
-// This NetLog is used for notifying when a file stream is closed
-// (i.e. TYPE_FILE_STREAM_CLOSE event is recorded).
-class NetLogForNotifyingFileClosure : public NetLog {
- public:
- NetLogForNotifyingFileClosure()
- : last_id_(0),
- on_closure_(false /* manual_reset */, false /* initially_signaled */) {
- }
-
- // Wait until a file closure event is recorded.
- bool WaitForClosure() {
- const base::TimeDelta timeout(TestTimeouts::action_max_timeout());
- return on_closure_.TimedWait(timeout);
- }
-
- // NetLog overrides:
- virtual void OnAddEntry(const net::NetLog::Entry& entry) OVERRIDE {
- if (entry.type() == TYPE_FILE_STREAM_CLOSE)
- on_closure_.Signal();
- }
-
- virtual uint32 NextID() OVERRIDE { return ++last_id_; }
- virtual LogLevel GetLogLevel() const OVERRIDE { return LOG_ALL; }
- virtual void AddThreadSafeObserver(ThreadSafeObserver* observer,
- LogLevel log_level) OVERRIDE {
- NOTIMPLEMENTED();
- }
- virtual void SetObserverLogLevel(ThreadSafeObserver* observer,
- LogLevel log_level) OVERRIDE {
- NOTIMPLEMENTED();
- }
- virtual void RemoveThreadSafeObserver(ThreadSafeObserver* observer) OVERRIDE {
- NOTIMPLEMENTED();
- }
-
- private:
- uint32 last_id_;
- base::WaitableEvent on_closure_;
-};
-
} // namespace
class FileStreamTest : public PlatformTest {
@@ -113,7 +73,7 @@ TEST_F(FileStreamTest, BasicOpenClose) {
EXPECT_FALSE(base::GetPlatformFileInfo(file, &info));
}
-TEST_F(FileStreamTest, FileHandleLeftOpen) {
+TEST_F(FileStreamTest, FileHandleNotLeftOpen) {
bool created = false;
ASSERT_EQ(kTestDataSize,
file_util::WriteFile(temp_file_path(), kTestData, kTestDataSize));
@@ -129,10 +89,9 @@ TEST_F(FileStreamTest, FileHandleLeftOpen) {
EXPECT_NE(base::kInvalidPlatformFileValue, file);
base::PlatformFileInfo info;
- // The file should still be open.
- EXPECT_TRUE(base::GetPlatformFileInfo(file, &info));
- // Clean up.
- EXPECT_TRUE(base::ClosePlatformFile(file));
+ // The file should be closed.
+ EXPECT_FALSE(base::GetPlatformFileInfo(file, &info));
+ EXPECT_FALSE(base::ClosePlatformFile(file));
}
// Test the use of FileStream with a file handle provided at construction.
@@ -147,26 +106,26 @@ TEST_F(FileStreamTest, UseFileHandle) {
temp_file_path(), flags, &created, NULL);
// Seek to the beginning of the file and read.
- FileStream read_stream(file, flags, NULL);
- ASSERT_EQ(0, read_stream.SeekSync(FROM_BEGIN, 0));
- ASSERT_EQ(kTestDataSize, read_stream.Available());
+ scoped_ptr<FileStream> read_stream(new FileStream(file, flags, NULL));
+ ASSERT_EQ(0, read_stream->SeekSync(FROM_BEGIN, 0));
+ ASSERT_EQ(kTestDataSize, read_stream->Available());
// Read into buffer and compare.
char buffer[kTestDataSize];
ASSERT_EQ(kTestDataSize,
- read_stream.ReadSync(buffer, kTestDataSize));
+ read_stream->ReadSync(buffer, kTestDataSize));
ASSERT_EQ(0, memcmp(kTestData, buffer, kTestDataSize));
- read_stream.CloseSync();
+ read_stream.reset();
// 2. Test writing with a file handle.
file_util::Delete(temp_file_path(), false);
flags = base::PLATFORM_FILE_OPEN_ALWAYS | base::PLATFORM_FILE_WRITE;
file = base::CreatePlatformFile(temp_file_path(), flags, &created, NULL);
- FileStream write_stream(file, flags, NULL);
- ASSERT_EQ(0, write_stream.SeekSync(FROM_BEGIN, 0));
+ scoped_ptr<FileStream> write_stream(new FileStream(file, flags, NULL));
+ ASSERT_EQ(0, write_stream->SeekSync(FROM_BEGIN, 0));
ASSERT_EQ(kTestDataSize,
- write_stream.WriteSync(kTestData, kTestDataSize));
- write_stream.CloseSync();
+ write_stream->WriteSync(kTestData, kTestDataSize));
+ write_stream.reset();
// Read into buffer and compare to make sure the handle worked fine.
ASSERT_EQ(kTestDataSize,
@@ -258,38 +217,6 @@ TEST_F(FileStreamTest, AsyncRead) {
EXPECT_EQ(kTestData, data_read);
}
-TEST_F(FileStreamTest, AsyncRead_EarlyClose) {
- int64 file_size;
- bool ok = file_util::GetFileSize(temp_file_path(), &file_size);
- EXPECT_TRUE(ok);
-
- FileStream stream(NULL);
- int flags = base::PLATFORM_FILE_OPEN |
- base::PLATFORM_FILE_READ |
- base::PLATFORM_FILE_ASYNC;
- int rv = stream.OpenSync(temp_file_path(), flags);
- EXPECT_EQ(OK, rv);
-
- int64 total_bytes_avail = stream.Available();
- EXPECT_EQ(file_size, total_bytes_avail);
-
- TestCompletionCallback callback;
-
- scoped_refptr<IOBufferWithSize> buf = new IOBufferWithSize(4);
- rv = stream.Read(buf, buf->size(), callback.callback());
- stream.CloseSync();
- if (rv < 0) {
- EXPECT_EQ(ERR_IO_PENDING, rv);
- // The callback should not be called if the request is cancelled.
- MessageLoop::current()->RunAllPending();
- EXPECT_FALSE(callback.have_result());
- } else {
- EXPECT_EQ(std::string(kTestData, rv), std::string(buf->data(), rv));
- }
-}
-
-// Similar to AsyncRead_EarlyClose but deletes a stream instead, to ensure
-// that deleting a stream is safe while an async read is in flight.
TEST_F(FileStreamTest, AsyncRead_EarlyDelete) {
int64 file_size;
bool ok = file_util::GetFileSize(temp_file_path(), &file_size);
@@ -320,47 +247,6 @@ TEST_F(FileStreamTest, AsyncRead_EarlyDelete) {
}
}
-// Similar to AsyncRead_EarlyDelete but using a given file handler rather than
-// calling FileStream::Open, to ensure that deleting a stream with in-flight
-// operation without auto-closing feature is also ok.
-TEST_F(FileStreamTest, AsyncRead_EarlyDelete_NoAutoClose) {
- int64 file_size;
- bool ok = file_util::GetFileSize(temp_file_path(), &file_size);
- EXPECT_TRUE(ok);
-
- bool created = false;
- int flags = base::PLATFORM_FILE_OPEN |
- base::PLATFORM_FILE_READ |
- base::PLATFORM_FILE_ASYNC;
- base::PlatformFileError error_code = base::PLATFORM_FILE_ERROR_FAILED;
- base::PlatformFile file = base::CreatePlatformFile(
- temp_file_path(), flags, &created, &error_code);
- EXPECT_EQ(base::PLATFORM_FILE_OK, error_code);
-
- scoped_ptr<FileStream> stream(new FileStream(file, flags, NULL));
- int64 total_bytes_avail = stream->Available();
- EXPECT_EQ(file_size, total_bytes_avail);
-
- TestCompletionCallback callback;
- scoped_refptr<IOBufferWithSize> buf = new IOBufferWithSize(4);
- int rv = stream->Read(buf, buf->size(), callback.callback());
- stream.reset(); // Delete instead of closing it.
- if (rv < 0) {
- EXPECT_EQ(ERR_IO_PENDING, rv);
- // The callback should not be called if the request is cancelled.
- MessageLoop::current()->RunAllPending();
- EXPECT_FALSE(callback.have_result());
- } else {
- EXPECT_EQ(std::string(kTestData, rv), std::string(buf->data(), rv));
- }
-
- base::PlatformFileInfo info;
- // The file should still be open.
- EXPECT_TRUE(base::GetPlatformFileInfo(file, &info));
- // Clean up.
- EXPECT_TRUE(base::ClosePlatformFile(file));
-}
-
TEST_F(FileStreamTest, BasicRead_FromOffset) {
int64 file_size;
bool ok = file_util::GetFileSize(temp_file_path(), &file_size);
@@ -496,10 +382,10 @@ TEST_F(FileStreamTest, AsyncSeekAround) {
}
TEST_F(FileStreamTest, BasicWrite) {
- FileStream stream(NULL);
+ scoped_ptr<FileStream> stream(new FileStream(NULL));
int flags = base::PLATFORM_FILE_CREATE_ALWAYS |
base::PLATFORM_FILE_WRITE;
- int rv = stream.OpenSync(temp_file_path(), flags);
+ int rv = stream->OpenSync(temp_file_path(), flags);
EXPECT_EQ(OK, rv);
int64 file_size;
@@ -507,9 +393,9 @@ TEST_F(FileStreamTest, BasicWrite) {
EXPECT_TRUE(ok);
EXPECT_EQ(0, file_size);
- rv = stream.WriteSync(kTestData, kTestDataSize);
+ rv = stream->WriteSync(kTestData, kTestDataSize);
EXPECT_EQ(kTestDataSize, rv);
- stream.CloseSync();
+ stream.reset();
ok = file_util::GetFileSize(temp_file_path(), &file_size);
EXPECT_TRUE(ok);
@@ -551,12 +437,12 @@ TEST_F(FileStreamTest, AsyncWrite) {
EXPECT_EQ(file_size, total_bytes_written);
}
-TEST_F(FileStreamTest, AsyncWrite_EarlyClose) {
- FileStream stream(NULL);
+TEST_F(FileStreamTest, AsyncWrite_EarlyDelete) {
+ scoped_ptr<FileStream> stream(new FileStream(NULL));
int flags = base::PLATFORM_FILE_CREATE_ALWAYS |
base::PLATFORM_FILE_WRITE |
base::PLATFORM_FILE_ASYNC;
- int rv = stream.OpenSync(temp_file_path(), flags);
+ int rv = stream->OpenSync(temp_file_path(), flags);
EXPECT_EQ(OK, rv);
int64 file_size;
@@ -567,8 +453,8 @@ TEST_F(FileStreamTest, AsyncWrite_EarlyClose) {
TestCompletionCallback callback;
scoped_refptr<IOBufferWithSize> buf = CreateTestDataBuffer();
- rv = stream.Write(buf, buf->size(), callback.callback());
- stream.CloseSync();
+ rv = stream->Write(buf, buf->size(), callback.callback());
+ stream.reset();
if (rv < 0) {
EXPECT_EQ(ERR_IO_PENDING, rv);
// The callback should not be called if the request is cancelled.
@@ -582,10 +468,10 @@ TEST_F(FileStreamTest, AsyncWrite_EarlyClose) {
}
TEST_F(FileStreamTest, BasicWrite_FromOffset) {
- FileStream stream(NULL);
+ scoped_ptr<FileStream> stream(new FileStream(NULL));
int flags = base::PLATFORM_FILE_OPEN |
base::PLATFORM_FILE_WRITE;
- int rv = stream.OpenSync(temp_file_path(), flags);
+ int rv = stream->OpenSync(temp_file_path(), flags);
EXPECT_EQ(OK, rv);
int64 file_size;
@@ -594,12 +480,12 @@ TEST_F(FileStreamTest, BasicWrite_FromOffset) {
EXPECT_EQ(kTestDataSize, file_size);
const int64 kOffset = 0;
- int64 new_offset = stream.SeekSync(FROM_END, kOffset);
+ int64 new_offset = stream->SeekSync(FROM_END, kOffset);
EXPECT_EQ(kTestDataSize, new_offset);
- rv = stream.WriteSync(kTestData, kTestDataSize);
+ rv = stream->WriteSync(kTestData, kTestDataSize);
EXPECT_EQ(kTestDataSize, rv);
- stream.CloseSync();
+ stream.reset();
ok = file_util::GetFileSize(temp_file_path(), &file_size);
EXPECT_TRUE(ok);
@@ -652,14 +538,14 @@ TEST_F(FileStreamTest, BasicReadWrite) {
bool ok = file_util::GetFileSize(temp_file_path(), &file_size);
EXPECT_TRUE(ok);
- FileStream stream(NULL);
+ scoped_ptr<FileStream> stream(new FileStream(NULL));
int flags = base::PLATFORM_FILE_OPEN |
base::PLATFORM_FILE_READ |
base::PLATFORM_FILE_WRITE;
- int rv = stream.OpenSync(temp_file_path(), flags);
+ int rv = stream->OpenSync(temp_file_path(), flags);
EXPECT_EQ(OK, rv);
- int64 total_bytes_avail = stream.Available();
+ int64 total_bytes_avail = stream->Available();
EXPECT_EQ(file_size, total_bytes_avail);
int total_bytes_read = 0;
@@ -667,7 +553,7 @@ TEST_F(FileStreamTest, BasicReadWrite) {
std::string data_read;
for (;;) {
char buf[4];
- rv = stream.ReadSync(buf, arraysize(buf));
+ rv = stream->ReadSync(buf, arraysize(buf));
EXPECT_LE(0, rv);
if (rv <= 0)
break;
@@ -677,9 +563,9 @@ TEST_F(FileStreamTest, BasicReadWrite) {
EXPECT_EQ(file_size, total_bytes_read);
EXPECT_TRUE(data_read == kTestData);
- rv = stream.WriteSync(kTestData, kTestDataSize);
+ rv = stream->WriteSync(kTestData, kTestDataSize);
EXPECT_EQ(kTestDataSize, rv);
- stream.CloseSync();
+ stream.reset();
ok = file_util::GetFileSize(temp_file_path(), &file_size);
EXPECT_TRUE(ok);
@@ -691,23 +577,23 @@ TEST_F(FileStreamTest, BasicWriteRead) {
bool ok = file_util::GetFileSize(temp_file_path(), &file_size);
EXPECT_TRUE(ok);
- FileStream stream(NULL);
+ scoped_ptr<FileStream> stream(new FileStream(NULL));
int flags = base::PLATFORM_FILE_OPEN |
base::PLATFORM_FILE_READ |
base::PLATFORM_FILE_WRITE;
- int rv = stream.OpenSync(temp_file_path(), flags);
+ int rv = stream->OpenSync(temp_file_path(), flags);
EXPECT_EQ(OK, rv);
- int64 total_bytes_avail = stream.Available();
+ int64 total_bytes_avail = stream->Available();
EXPECT_EQ(file_size, total_bytes_avail);
- int64 offset = stream.SeekSync(FROM_END, 0);
+ int64 offset = stream->SeekSync(FROM_END, 0);
EXPECT_EQ(offset, file_size);
- rv = stream.WriteSync(kTestData, kTestDataSize);
+ rv = stream->WriteSync(kTestData, kTestDataSize);
EXPECT_EQ(kTestDataSize, rv);
- offset = stream.SeekSync(FROM_BEGIN, 0);
+ offset = stream->SeekSync(FROM_BEGIN, 0);
EXPECT_EQ(0, offset);
int64 total_bytes_read = 0;
@@ -715,14 +601,14 @@ TEST_F(FileStreamTest, BasicWriteRead) {
std::string data_read;
for (;;) {
char buf[4];
- rv = stream.ReadSync(buf, arraysize(buf));
+ rv = stream->ReadSync(buf, arraysize(buf));
EXPECT_LE(0, rv);
if (rv <= 0)
break;
total_bytes_read += rv;
data_read.append(buf, rv);
}
- stream.CloseSync();
+ stream.reset();
ok = file_util::GetFileSize(temp_file_path(), &file_size);
EXPECT_TRUE(ok);
@@ -739,15 +625,15 @@ TEST_F(FileStreamTest, BasicAsyncReadWrite) {
bool ok = file_util::GetFileSize(temp_file_path(), &file_size);
EXPECT_TRUE(ok);
- FileStream stream(NULL);
+ scoped_ptr<FileStream> stream(new FileStream(NULL));
int flags = base::PLATFORM_FILE_OPEN |
base::PLATFORM_FILE_READ |
base::PLATFORM_FILE_WRITE |
base::PLATFORM_FILE_ASYNC;
- int rv = stream.OpenSync(temp_file_path(), flags);
+ int rv = stream->OpenSync(temp_file_path(), flags);
EXPECT_EQ(OK, rv);
- int64 total_bytes_avail = stream.Available();
+ int64 total_bytes_avail = stream->Available();
EXPECT_EQ(file_size, total_bytes_avail);
TestCompletionCallback callback;
@@ -756,7 +642,7 @@ TEST_F(FileStreamTest, BasicAsyncReadWrite) {
std::string data_read;
for (;;) {
scoped_refptr<IOBufferWithSize> buf = new IOBufferWithSize(4);
- rv = stream.Read(buf, buf->size(), callback.callback());
+ rv = stream->Read(buf, buf->size(), callback.callback());
if (rv == ERR_IO_PENDING)
rv = callback.WaitForResult();
EXPECT_LE(0, rv);
@@ -774,8 +660,8 @@ TEST_F(FileStreamTest, BasicAsyncReadWrite) {
scoped_refptr<DrainableIOBuffer> drainable =
new DrainableIOBuffer(buf, buf->size());
while (total_bytes_written != kTestDataSize) {
- rv = stream.Write(drainable, drainable->BytesRemaining(),
- callback.callback());
+ rv = stream->Write(drainable, drainable->BytesRemaining(),
+ callback.callback());
if (rv == ERR_IO_PENDING)
rv = callback.WaitForResult();
EXPECT_LT(0, rv);
@@ -785,7 +671,7 @@ TEST_F(FileStreamTest, BasicAsyncReadWrite) {
total_bytes_written += rv;
}
- stream.CloseSync();
+ stream.reset();
ok = file_util::GetFileSize(temp_file_path(), &file_size);
EXPECT_TRUE(ok);
@@ -797,19 +683,19 @@ TEST_F(FileStreamTest, BasicAsyncWriteRead) {
bool ok = file_util::GetFileSize(temp_file_path(), &file_size);
EXPECT_TRUE(ok);
- FileStream stream(NULL);
+ scoped_ptr<FileStream> stream(new FileStream(NULL));
int flags = base::PLATFORM_FILE_OPEN |
base::PLATFORM_FILE_READ |
base::PLATFORM_FILE_WRITE |
base::PLATFORM_FILE_ASYNC;
- int rv = stream.OpenSync(temp_file_path(), flags);
+ int rv = stream->OpenSync(temp_file_path(), flags);
EXPECT_EQ(OK, rv);
- int64 total_bytes_avail = stream.Available();
+ int64 total_bytes_avail = stream->Available();
EXPECT_EQ(file_size, total_bytes_avail);
TestInt64CompletionCallback callback64;
- rv = stream.Seek(FROM_END, 0, callback64.callback());
+ rv = stream->Seek(FROM_END, 0, callback64.callback());
ASSERT_EQ(ERR_IO_PENDING, rv);
int64 offset = callback64.WaitForResult();
EXPECT_EQ(offset, file_size);
@@ -821,7 +707,7 @@ TEST_F(FileStreamTest, BasicAsyncWriteRead) {
scoped_refptr<DrainableIOBuffer> drainable =
new DrainableIOBuffer(buf, buf->size());
while (total_bytes_written != kTestDataSize) {
- rv = stream.Write(drainable, drainable->BytesRemaining(),
+ rv = stream->Write(drainable, drainable->BytesRemaining(),
callback.callback());
if (rv == ERR_IO_PENDING)
rv = callback.WaitForResult();
@@ -834,7 +720,7 @@ TEST_F(FileStreamTest, BasicAsyncWriteRead) {
EXPECT_EQ(kTestDataSize, total_bytes_written);
- rv = stream.Seek(FROM_BEGIN, 0, callback64.callback());
+ rv = stream->Seek(FROM_BEGIN, 0, callback64.callback());
ASSERT_EQ(ERR_IO_PENDING, rv);
offset = callback64.WaitForResult();
EXPECT_EQ(0, offset);
@@ -844,7 +730,7 @@ TEST_F(FileStreamTest, BasicAsyncWriteRead) {
std::string data_read;
for (;;) {
scoped_refptr<IOBufferWithSize> buf = new IOBufferWithSize(4);
- rv = stream.Read(buf, buf->size(), callback.callback());
+ rv = stream->Read(buf, buf->size(), callback.callback());
if (rv == ERR_IO_PENDING)
rv = callback.WaitForResult();
EXPECT_LE(0, rv);
@@ -853,7 +739,7 @@ TEST_F(FileStreamTest, BasicAsyncWriteRead) {
total_bytes_read += rv;
data_read.append(buf->data(), rv);
}
- stream.CloseSync();
+ stream.reset();
ok = file_util::GetFileSize(temp_file_path(), &file_size);
EXPECT_TRUE(ok);
@@ -963,34 +849,34 @@ TEST_F(FileStreamTest, AsyncWriteRead) {
bool ok = file_util::GetFileSize(temp_file_path(), &file_size);
EXPECT_TRUE(ok);
- FileStream stream(NULL);
+ scoped_ptr<FileStream> stream(new FileStream(NULL));
int flags = base::PLATFORM_FILE_OPEN |
base::PLATFORM_FILE_READ |
base::PLATFORM_FILE_WRITE |
base::PLATFORM_FILE_ASYNC;
- int rv = stream.OpenSync(temp_file_path(), flags);
+ int rv = stream->OpenSync(temp_file_path(), flags);
EXPECT_EQ(OK, rv);
- int64 total_bytes_avail = stream.Available();
+ int64 total_bytes_avail = stream->Available();
EXPECT_EQ(file_size, total_bytes_avail);
- int64 offset = stream.SeekSync(FROM_END, 0);
+ int64 offset = stream->SeekSync(FROM_END, 0);
EXPECT_EQ(offset, file_size);
int total_bytes_written = 0;
int total_bytes_read = 0;
std::string data_read;
- TestWriteReadCompletionCallback callback(&stream, &total_bytes_written,
+ TestWriteReadCompletionCallback callback(stream.get(), &total_bytes_written,
&total_bytes_read, &data_read);
scoped_refptr<IOBufferWithSize> buf = CreateTestDataBuffer();
- rv = stream.Write(buf, buf->size(), callback.callback());
+ rv = stream->Write(buf, buf->size(), callback.callback());
if (rv == ERR_IO_PENDING)
rv = callback.WaitForResult();
EXPECT_LT(0, rv);
EXPECT_EQ(kTestDataSize, total_bytes_written);
- stream.CloseSync();
+ stream.reset();
ok = file_util::GetFileSize(temp_file_path(), &file_size);
EXPECT_TRUE(ok);
@@ -1046,8 +932,6 @@ class TestWriteCloseCompletionCallback {
rv = callback.WaitForResult();
drainable_->DidConsume(total_bytes_written);
*total_bytes_written_ += total_bytes_written;
- } else { // We're done writing all data. Close the file.
- stream_->CloseSync();
}
result_ = *total_bytes_written_;
@@ -1073,30 +957,32 @@ TEST_F(FileStreamTest, AsyncWriteClose) {
bool ok = file_util::GetFileSize(temp_file_path(), &file_size);
EXPECT_TRUE(ok);
- FileStream stream(NULL);
+ scoped_ptr<FileStream> stream(new FileStream(NULL));
int flags = base::PLATFORM_FILE_OPEN |
base::PLATFORM_FILE_READ |
base::PLATFORM_FILE_WRITE |
base::PLATFORM_FILE_ASYNC;
- int rv = stream.OpenSync(temp_file_path(), flags);
+ int rv = stream->OpenSync(temp_file_path(), flags);
EXPECT_EQ(OK, rv);
- int64 total_bytes_avail = stream.Available();
+ int64 total_bytes_avail = stream->Available();
EXPECT_EQ(file_size, total_bytes_avail);
- int64 offset = stream.SeekSync(FROM_END, 0);
+ int64 offset = stream->SeekSync(FROM_END, 0);
EXPECT_EQ(offset, file_size);
int total_bytes_written = 0;
- TestWriteCloseCompletionCallback callback(&stream, &total_bytes_written);
+ TestWriteCloseCompletionCallback callback(stream.get(), &total_bytes_written);
scoped_refptr<IOBufferWithSize> buf = CreateTestDataBuffer();
- rv = stream.Write(buf, buf->size(), callback.callback());
+ rv = stream->Write(buf, buf->size(), callback.callback());
if (rv == ERR_IO_PENDING)
total_bytes_written = callback.WaitForResult();
EXPECT_LT(0, total_bytes_written);
EXPECT_EQ(kTestDataSize, total_bytes_written);
+ stream.reset();
+
ok = file_util::GetFileSize(temp_file_path(), &file_size);
EXPECT_TRUE(ok);
EXPECT_EQ(kTestDataSize * 2, file_size);
@@ -1106,21 +992,21 @@ TEST_F(FileStreamTest, AsyncWriteClose) {
TEST_F(FileStreamTest, Truncate) {
int flags = base::PLATFORM_FILE_CREATE_ALWAYS | base::PLATFORM_FILE_WRITE;
- FileStream write_stream(NULL);
- ASSERT_EQ(OK, write_stream.OpenSync(temp_file_path(), flags));
+ scoped_ptr<FileStream> write_stream(new FileStream(NULL));
+ ASSERT_EQ(OK, write_stream->OpenSync(temp_file_path(), flags));
// Write some data to the file.
const char test_data[] = "0123456789";
- write_stream.WriteSync(test_data, arraysize(test_data));
+ write_stream->WriteSync(test_data, arraysize(test_data));
// Truncate the file.
- ASSERT_EQ(4, write_stream.Truncate(4));
+ ASSERT_EQ(4, write_stream->Truncate(4));
// Write again.
- write_stream.WriteSync(test_data, 4);
+ write_stream->WriteSync(test_data, 4);
// Close the stream.
- write_stream.CloseSync();
+ write_stream.reset();
// Read in the contents and make sure we get back what we expected.
std::string read_contents;
@@ -1129,97 +1015,6 @@ TEST_F(FileStreamTest, Truncate) {
EXPECT_EQ("01230123", read_contents);
}
-TEST_F(FileStreamTest, AsyncBasicOpenClose) {
- FileStream stream(NULL);
- int flags = base::PLATFORM_FILE_OPEN |
- base::PLATFORM_FILE_READ |
- base::PLATFORM_FILE_ASYNC;
- TestCompletionCallback callback;
- int rv = stream.Open(temp_file_path(), flags, callback.callback());
- EXPECT_EQ(ERR_IO_PENDING, rv);
- EXPECT_EQ(OK, callback.WaitForResult());
- EXPECT_TRUE(stream.IsOpen());
-
- stream.Close(callback.callback());
- EXPECT_EQ(OK, callback.WaitForResult());
- EXPECT_FALSE(stream.IsOpen());
-}
-
-TEST_F(FileStreamTest, SyncCloseTwice) {
- FileStream stream(NULL);
- int flags = base::PLATFORM_FILE_OPEN |
- base::PLATFORM_FILE_READ;
- int rv = stream.OpenSync(temp_file_path(), flags);
- EXPECT_EQ(OK, rv);
- EXPECT_TRUE(stream.IsOpen());
-
- // Closing twice should be safe.
- stream.CloseSync();
- EXPECT_FALSE(stream.IsOpen());
-
- stream.CloseSync();
- EXPECT_FALSE(stream.IsOpen());
-}
-
-TEST_F(FileStreamTest, AsyncCloseTwice) {
- FileStream stream(NULL);
- int flags = base::PLATFORM_FILE_OPEN |
- base::PLATFORM_FILE_READ |
- base::PLATFORM_FILE_ASYNC;
- TestCompletionCallback callback;
- int rv = stream.Open(temp_file_path(), flags, callback.callback());
- EXPECT_EQ(ERR_IO_PENDING, rv);
- EXPECT_EQ(OK, callback.WaitForResult());
- EXPECT_TRUE(stream.IsOpen());
-
- // Closing twice should be safe.
- stream.Close(callback.callback());
- EXPECT_EQ(OK, callback.WaitForResult());
- EXPECT_FALSE(stream.IsOpen());
-
- stream.Close(callback.callback());
- EXPECT_EQ(OK, callback.WaitForResult());
- EXPECT_FALSE(stream.IsOpen());
-}
-
-// TODO(satorux): This should be gone once all once all async clients are
-// migrated to use Close(). crbug.com/114783
-TEST_F(FileStreamTest, AsyncWriteAndCloseSync) {
- FileStream stream(NULL);
- int flags = base::PLATFORM_FILE_OPEN |
- base::PLATFORM_FILE_WRITE |
- base::PLATFORM_FILE_ASYNC;
- TestCompletionCallback callback;
- int rv = stream.Open(temp_file_path(), flags, callback.callback());
- EXPECT_EQ(ERR_IO_PENDING, rv);
- EXPECT_EQ(OK, callback.WaitForResult());
- EXPECT_TRUE(stream.IsOpen());
-
- // Write some data asynchronously.
- scoped_refptr<IOBufferWithSize> buf = CreateTestDataBuffer();
- stream.Write(buf, buf->size(), callback.callback());
-
- // Close the stream without waiting for the completion.
- stream.CloseSync();
-}
-
-// TODO(satorux): This should be gone once all once all async clients are
-// migrated to use Close(). crbug.com/114783
-TEST_F(FileStreamTest, AsyncOpenAndCloseSync) {
- FileStream stream(NULL);
- int flags = base::PLATFORM_FILE_OPEN |
- base::PLATFORM_FILE_WRITE |
- base::PLATFORM_FILE_ASYNC;
- TestCompletionCallback open_callback;
- int rv = stream.Open(temp_file_path(), flags, open_callback.callback());
- EXPECT_EQ(ERR_IO_PENDING, rv);
-
- // Close the stream without waiting for the completion. Should be safe.
- stream.CloseSync();
- // open_callback won't be called.
- EXPECT_FALSE(open_callback.have_result());
-}
-
TEST_F(FileStreamTest, AsyncOpenAndDelete) {
scoped_ptr<FileStream> stream(new FileStream(NULL));
int flags = base::PLATFORM_FILE_OPEN |
@@ -1233,30 +1028,10 @@ TEST_F(FileStreamTest, AsyncOpenAndDelete) {
// complete. Should be safe.
stream.reset();
// open_callback won't be called.
+ MessageLoop::current()->RunUntilIdle();
EXPECT_FALSE(open_callback.have_result());
}
-TEST_F(FileStreamTest, AsyncCloseAndDelete) {
- scoped_ptr<FileStream> stream(new FileStream(NULL));
- int flags = base::PLATFORM_FILE_OPEN |
- base::PLATFORM_FILE_WRITE |
- base::PLATFORM_FILE_ASYNC;
- TestCompletionCallback open_callback;
- int rv = stream->Open(temp_file_path(), flags, open_callback.callback());
- EXPECT_EQ(ERR_IO_PENDING, rv);
- EXPECT_EQ(OK, open_callback.WaitForResult());
- EXPECT_TRUE(stream->IsOpen());
-
- TestCompletionCallback close_callback;
- stream->Close(close_callback.callback());
-
- // Delete the stream without waiting for the close operation to be
- // complete. Should be safe.
- stream.reset();
- // close_callback won't be called.
- EXPECT_FALSE(close_callback.have_result());
-}
-
} // namespace
} // namespace net
diff --git a/net/base/file_stream_win.cc b/net/base/file_stream_win.cc
deleted file mode 100644
index 88e1dc7..0000000
--- a/net/base/file_stream_win.cc
+++ /dev/null
@@ -1,778 +0,0 @@
-// Copyright (c) 2012 The Chromium 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 "net/base/file_stream.h"
-
-#include <windows.h>
-
-#include "base/file_path.h"
-#include "base/logging.h"
-#include "base/message_loop.h"
-#include "base/metrics/histogram.h"
-#include "base/synchronization/waitable_event.h"
-#include "base/threading/thread_restrictions.h"
-#include "base/threading/worker_pool.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"
-
-namespace net {
-
-// Ensure that we can just use our Whence values directly.
-COMPILE_ASSERT(FROM_BEGIN == FILE_BEGIN, bad_whence_begin);
-COMPILE_ASSERT(FROM_CURRENT == FILE_CURRENT, bad_whence_current);
-COMPILE_ASSERT(FROM_END == FILE_END, bad_whence_end);
-
-namespace {
-
-void SetOffset(OVERLAPPED* overlapped, const LARGE_INTEGER& offset) {
- overlapped->Offset = offset.LowPart;
- overlapped->OffsetHigh = offset.HighPart;
-}
-
-void IncrementOffset(OVERLAPPED* overlapped, DWORD count) {
- LARGE_INTEGER offset;
- offset.LowPart = overlapped->Offset;
- offset.HighPart = overlapped->OffsetHigh;
- offset.QuadPart += static_cast<LONGLONG>(count);
- SetOffset(overlapped, offset);
-}
-
-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));
-
- *file = base::CreatePlatformFile(path, open_flags, NULL, NULL);
- if (*file == base::kInvalidPlatformFileValue) {
- DWORD error = GetLastError();
- LOG(WARNING) << "Failed to open file: " << error;
- *result = RecordAndMapError(error,
- FILE_ERROR_SOURCE_OPEN,
- record_uma,
- bound_net_log);
- bound_net_log.EndEvent(net::NetLog::TYPE_FILE_STREAM_OPEN);
- return;
- }
-}
-
-// 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;
-
- CancelIo(file);
-
- 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();
-}
-
-// Invokes a given closure and signals the completion.
-void InvokeAndSignal(const base::Closure& closure,
- base::WaitableEvent* on_io_complete) {
- closure.Run();
- on_io_complete->Signal();
-}
-
-} // namespace
-
-// FileStreamWin::AsyncContext ----------------------------------------------
-
-class FileStreamWin::AsyncContext : public MessageLoopForIO::IOHandler {
- public:
- explicit AsyncContext(const net::BoundNetLog& bound_net_log)
- : context_(), is_closing_(false),
- record_uma_(false), bound_net_log_(bound_net_log),
- error_source_(FILE_ERROR_SOURCE_COUNT) {
- context_.handler = this;
- }
- ~AsyncContext();
-
- void IOCompletionIsPending(const CompletionCallback& callback,
- IOBuffer* buf);
-
- OVERLAPPED* overlapped() { return &context_.overlapped; }
- const CompletionCallback& callback() const { return callback_; }
-
- void set_error_source(FileErrorSource source) { error_source_ = source; }
-
- void EnableErrorStatistics() {
- record_uma_ = true;
- }
-
- private:
- virtual void OnIOCompleted(MessageLoopForIO::IOContext* context,
- DWORD bytes_read, DWORD error) OVERRIDE;
-
- MessageLoopForIO::IOContext context_;
- CompletionCallback callback_;
- scoped_refptr<IOBuffer> in_flight_buf_;
- bool is_closing_;
- bool record_uma_;
- const net::BoundNetLog bound_net_log_;
- FileErrorSource error_source_;
-};
-
-FileStreamWin::AsyncContext::~AsyncContext() {
- is_closing_ = true;
- bool waited = false;
- base::TimeTicks start = base::TimeTicks::Now();
- while (!callback_.is_null()) {
- waited = true;
- MessageLoopForIO::current()->WaitForIOCompletion(INFINITE, this);
- }
- if (waited) {
- // We want to see if we block the message loop for too long.
- UMA_HISTOGRAM_TIMES("AsyncIO.FileStreamClose",
- base::TimeTicks::Now() - start);
- }
-}
-
-void FileStreamWin::AsyncContext::IOCompletionIsPending(
- const CompletionCallback& callback,
- IOBuffer* buf) {
- DCHECK(callback_.is_null());
- callback_ = callback;
- in_flight_buf_ = buf; // Hold until the async operation ends.
-}
-
-void FileStreamWin::AsyncContext::OnIOCompleted(
- MessageLoopForIO::IOContext* context, DWORD bytes_read, DWORD error) {
- DCHECK_EQ(&context_, context);
- DCHECK(!callback_.is_null());
-
- if (is_closing_) {
- callback_.Reset();
- in_flight_buf_ = NULL;
- return;
- }
-
- int result = static_cast<int>(bytes_read);
- if (error && error != ERROR_HANDLE_EOF) {
- result = RecordAndMapError(error, error_source_, record_uma_,
- bound_net_log_);
- }
-
- if (bytes_read)
- IncrementOffset(&context->overlapped, bytes_read);
-
- CompletionCallback temp_callback = callback_;
- callback_.Reset();
- scoped_refptr<IOBuffer> temp_buf = in_flight_buf_;
- in_flight_buf_ = NULL;
- temp_callback.Run(result);
-}
-
-// FileStream ------------------------------------------------------------
-
-FileStreamWin::FileStreamWin(net::NetLog* 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);
-}
-
-FileStreamWin::FileStreamWin(
- base::PlatformFile file, int flags, net::NetLog* net_log)
- : 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);
-
- // If the file handle is opened with base::PLATFORM_FILE_ASYNC, we need to
- // make sure we will perform asynchronous File IO to it.
- if (flags & base::PLATFORM_FILE_ASYNC) {
- async_context_.reset(new AsyncContext(bound_net_log_));
- MessageLoopForIO::current()->RegisterIOHandler(file_,
- async_context_.get());
- }
-}
-
-FileStreamWin::~FileStreamWin() {
- if (open_flags_ & base::PLATFORM_FILE_ASYNC) {
- // Block until the in-flight open/close operation is complete.
- // TODO(satorux): Ideally we should not block. crbug.com/115067
- WaitForIOCompletion();
-
- // Block until the last read/write operation is complete.
- async_context_.reset();
- }
-
- 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 FileStreamWin::Close(const CompletionCallback& callback) {
- 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 */));
-
- // 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.
- const bool posted = base::WorkerPool::PostTaskAndReply(
- FROM_HERE,
- base::Bind(&CloseFileAndSignal, &file_, on_io_complete_.get(),
- bound_net_log_),
- base::Bind(&FileStreamWin::OnClosed,
- weak_ptr_factory_.GetWeakPtr(),
- callback),
- true /* task_is_slow */);
- DCHECK(posted);
-}
-
-void FileStreamWin::CloseSync() {
- // The logic here is similar to CloseFile() but async_context_.reset() is
- // caled in this function.
-
- // Block until the in-flight open operation is complete.
- // TODO(satorux): Replace this with a DCHECK(open_flags & ASYNC) once this
- // once all async clients are migrated to use Close(). crbug.com/114783
- WaitForIOCompletion();
-
- bound_net_log_.AddEvent(net::NetLog::TYPE_FILE_STREAM_CLOSE);
- if (file_ != base::kInvalidPlatformFileValue)
- CancelIo(file_);
-
- // Block until the last read/write operation is complete.
- async_context_.reset();
-
- if (file_ != base::kInvalidPlatformFileValue) {
- if (!base::ClosePlatformFile(file_))
- NOTREACHED();
- file_ = base::kInvalidPlatformFileValue;
-
- bound_net_log_.EndEvent(net::NetLog::TYPE_FILE_STREAM_OPEN);
- }
-}
-
-int FileStreamWin::Open(const FilePath& path, int open_flags,
- const CompletionCallback& callback) {
- if (IsOpen()) {
- DLOG(FATAL) << "File is already open!";
- return ERR_UNEXPECTED;
- }
-
- open_flags_ = open_flags;
- 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 */));
-
- // 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.
- int* result = new int(OK);
- const bool posted = base::WorkerPool::PostTaskAndReply(
- FROM_HERE,
- base::Bind(&InvokeAndSignal,
- base::Bind(&OpenFile, path, open_flags, record_uma_, &file_,
- result, bound_net_log_),
- on_io_complete_.get()),
- base::Bind(&FileStreamWin::OnOpened,
- weak_ptr_factory_.GetWeakPtr(),
- callback, base::Owned(result)),
- true /* task_is_slow */);
- DCHECK(posted);
- return ERR_IO_PENDING;
-}
-
-int FileStreamWin::OpenSync(const FilePath& path, int open_flags) {
- if (IsOpen()) {
- DLOG(FATAL) << "File is already open!";
- return ERR_UNEXPECTED;
- }
-
- open_flags_ = open_flags;
-
- int result = OK;
- OpenFile(path, open_flags_, record_uma_, &file_, &result, bound_net_log_);
- if (result != OK)
- return result;
-
- // TODO(satorux): Remove this once all async clients are migrated to use
- // Open(). crbug.com/114783
- if (open_flags_ & base::PLATFORM_FILE_ASYNC) {
- async_context_.reset(new AsyncContext(bound_net_log_));
- if (record_uma_)
- async_context_->EnableErrorStatistics();
- MessageLoopForIO::current()->RegisterIOHandler(file_,
- async_context_.get());
- }
-
- return OK;
-}
-
-bool FileStreamWin::IsOpen() const {
- return file_ != base::kInvalidPlatformFileValue;
-}
-
-int FileStreamWin::Seek(Whence whence, int64 offset,
- const Int64CompletionCallback& callback) {
- if (!IsOpen())
- return ERR_UNEXPECTED;
-
- // 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());
-
- int64* result = new int64(-1);
- on_io_complete_.reset(new base::WaitableEvent(
- false /* manual_reset */, false /* initially_signaled */));
-
- const bool posted = base::WorkerPool::PostTaskAndReply(
- FROM_HERE,
- base::Bind(&InvokeAndSignal,
- // Unretained should be fine as we wait for a signal on
- // on_io_complete_ at the destructor.
- base::Bind(&FileStreamWin::SeekFile, base::Unretained(this),
- whence, offset, result),
- on_io_complete_.get()),
- base::Bind(&FileStreamWin::OnSeeked,
- weak_ptr_factory_.GetWeakPtr(),
- callback, base::Owned(result)),
- true /* task is slow */);
- DCHECK(posted);
- return ERR_IO_PENDING;
-}
-
-int64 FileStreamWin::SeekSync(Whence whence, int64 offset) {
- if (!IsOpen())
- return ERR_UNEXPECTED;
-
- DCHECK(!async_context_.get() || async_context_->callback().is_null());
- int64 result = -1;
- SeekFile(whence, offset, &result);
- return result;
-}
-
-int64 FileStreamWin::Available() {
- base::ThreadRestrictions::AssertIOAllowed();
-
- if (!IsOpen())
- return ERR_UNEXPECTED;
-
- int64 cur_pos = SeekSync(FROM_CURRENT, 0);
- if (cur_pos < 0)
- return cur_pos;
-
- LARGE_INTEGER file_size;
- if (!GetFileSizeEx(file_, &file_size)) {
- DWORD error = GetLastError();
- LOG(WARNING) << "GetFileSizeEx failed: " << error;
- return RecordAndMapError(error,
- FILE_ERROR_SOURCE_GET_SIZE,
- record_uma_,
- bound_net_log_);
- }
-
- return file_size.QuadPart - cur_pos;
-}
-
-int FileStreamWin::Read(
- IOBuffer* buf, int buf_len, const CompletionCallback& callback) {
- DCHECK(async_context_.get());
-
- if (!IsOpen())
- return ERR_UNEXPECTED;
-
- DCHECK(open_flags_ & base::PLATFORM_FILE_READ);
-
- OVERLAPPED* overlapped = NULL;
- DCHECK(!callback.is_null());
- DCHECK(async_context_->callback().is_null());
- overlapped = async_context_->overlapped();
- async_context_->set_error_source(FILE_ERROR_SOURCE_READ);
-
- int rv = 0;
-
- DWORD bytes_read;
- if (!ReadFile(file_, buf->data(), buf_len, &bytes_read, overlapped)) {
- DWORD error = GetLastError();
- if (error == ERROR_IO_PENDING) {
- async_context_->IOCompletionIsPending(callback, buf);
- rv = ERR_IO_PENDING;
- } else if (error == ERROR_HANDLE_EOF) {
- rv = 0; // Report EOF by returning 0 bytes read.
- } else {
- LOG(WARNING) << "ReadFile failed: " << error;
- rv = RecordAndMapError(error,
- FILE_ERROR_SOURCE_READ,
- record_uma_,
- bound_net_log_);
- }
- } else if (overlapped) {
- async_context_->IOCompletionIsPending(callback, buf);
- rv = ERR_IO_PENDING;
- } else {
- rv = static_cast<int>(bytes_read);
- }
- return rv;
-}
-
-int FileStreamWin::ReadSync(char* buf, int buf_len) {
- DCHECK(!async_context_.get());
- base::ThreadRestrictions::AssertIOAllowed();
-
- if (!IsOpen())
- return ERR_UNEXPECTED;
-
- DCHECK(open_flags_ & base::PLATFORM_FILE_READ);
-
- int rv = 0;
-
- DWORD bytes_read;
- if (!ReadFile(file_, buf, buf_len, &bytes_read, NULL)) {
- DWORD error = GetLastError();
- if (error == ERROR_HANDLE_EOF) {
- rv = 0; // Report EOF by returning 0 bytes read.
- } else {
- LOG(WARNING) << "ReadFile failed: " << error;
- rv = RecordAndMapError(error,
- FILE_ERROR_SOURCE_READ,
- record_uma_,
- bound_net_log_);
- }
- } else {
- rv = static_cast<int>(bytes_read);
- }
- return rv;
-}
-
-int FileStreamWin::ReadUntilComplete(char *buf, int buf_len) {
- int to_read = buf_len;
- int bytes_total = 0;
-
- 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 FileStreamWin::Write(
- IOBuffer* buf, int buf_len, const CompletionCallback& callback) {
- DCHECK(async_context_.get());
-
- if (!IsOpen())
- return ERR_UNEXPECTED;
-
- DCHECK(open_flags_ & base::PLATFORM_FILE_WRITE);
-
- OVERLAPPED* overlapped = NULL;
- DCHECK(!callback.is_null());
- DCHECK(async_context_->callback().is_null());
- overlapped = async_context_->overlapped();
- async_context_->set_error_source(FILE_ERROR_SOURCE_WRITE);
-
- int rv = 0;
- DWORD bytes_written = 0;
- if (!WriteFile(file_, buf->data(), buf_len, &bytes_written, overlapped)) {
- DWORD error = GetLastError();
- if (error == ERROR_IO_PENDING) {
- async_context_->IOCompletionIsPending(callback, buf);
- rv = ERR_IO_PENDING;
- } else {
- LOG(WARNING) << "WriteFile failed: " << error;
- rv = RecordAndMapError(error,
- FILE_ERROR_SOURCE_WRITE,
- record_uma_,
- bound_net_log_);
- }
- } else if (overlapped) {
- async_context_->IOCompletionIsPending(callback, buf);
- rv = ERR_IO_PENDING;
- } else {
- rv = static_cast<int>(bytes_written);
- }
- return rv;
-}
-
-int FileStreamWin::WriteSync(
- const char* buf, int buf_len) {
- DCHECK(!async_context_.get());
- base::ThreadRestrictions::AssertIOAllowed();
-
- if (!IsOpen())
- return ERR_UNEXPECTED;
-
- DCHECK(open_flags_ & base::PLATFORM_FILE_WRITE);
-
- int rv = 0;
- DWORD bytes_written = 0;
- if (!WriteFile(file_, buf, buf_len, &bytes_written, NULL)) {
- DWORD error = GetLastError();
- LOG(WARNING) << "WriteFile failed: " << error;
- rv = RecordAndMapError(error,
- FILE_ERROR_SOURCE_WRITE,
- record_uma_,
- bound_net_log_);
- } else {
- rv = static_cast<int>(bytes_written);
- }
- return rv;
-}
-
-int FileStreamWin::Flush(const CompletionCallback& callback) {
- if (!IsOpen())
- return ERR_UNEXPECTED;
-
- // Make sure we're async and we have no other in-flight async operations.
- DCHECK(open_flags_ & base::PLATFORM_FILE_ASYNC);
- DCHECK(open_flags_ & base::PLATFORM_FILE_WRITE);
- 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);
- const bool posted = base::WorkerPool::PostTaskAndReply(
- FROM_HERE,
- base::Bind(&InvokeAndSignal,
- // Unretained should be fine as we wait for a signal on
- // on_io_complete_ at the destructor.
- base::Bind(&FileStreamWin::FlushFile, base::Unretained(this),
- result),
- on_io_complete_.get()),
- base::Bind(&FileStreamWin::OnFlushed,
- weak_ptr_factory_.GetWeakPtr(),
- callback, base::Owned(result)),
- true /* task is slow */);
- DCHECK(posted);
- return ERR_IO_PENDING;
-}
-
-int FileStreamWin::FlushSync() {
- base::ThreadRestrictions::AssertIOAllowed();
-
- if (!IsOpen())
- return ERR_UNEXPECTED;
-
- DCHECK(open_flags_ & base::PLATFORM_FILE_WRITE);
- if (FlushFileBuffers(file_)) {
- return OK;
- }
-
- return RecordAndMapError(GetLastError(),
- FILE_ERROR_SOURCE_FLUSH,
- record_uma_,
- bound_net_log_);
-}
-
-int64 FileStreamWin::Truncate(int64 bytes) {
- base::ThreadRestrictions::AssertIOAllowed();
-
- 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.
- BOOL result = SetEndOfFile(file_);
- if (!result) {
- DWORD error = GetLastError();
- LOG(WARNING) << "SetEndOfFile failed: " << error;
- return RecordAndMapError(error,
- FILE_ERROR_SOURCE_SET_EOF,
- record_uma_,
- bound_net_log_);
- }
-
- // Success.
- return seek_position;
-}
-
-void FileStreamWin::EnableErrorStatistics() {
- record_uma_ = true;
-
- if (async_context_.get())
- async_context_->EnableErrorStatistics();
-}
-
-void FileStreamWin::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.
- return;
- }
-
- // Should never connect to itself.
- DCHECK_NE(bound_net_log_.source().id, owner_bound_net_log.source().id);
-
- bound_net_log_.AddEvent(
- net::NetLog::TYPE_FILE_STREAM_BOUND_TO_OWNER,
- owner_bound_net_log.source().ToEventParametersCallback());
-
- owner_bound_net_log.AddEvent(
- net::NetLog::TYPE_FILE_STREAM_SOURCE,
- bound_net_log_.source().ToEventParametersCallback());
-}
-
-base::PlatformFile FileStreamWin::GetPlatformFileForTesting() {
- return file_;
-}
-
-void FileStreamWin::OnClosed(const CompletionCallback& callback) {
- file_ = base::kInvalidPlatformFileValue;
-
- // Reset this before Run() as Run() may issue a new async operation.
- ResetOnIOComplete();
- callback.Run(OK);
-}
-
-void FileStreamWin::SeekFile(Whence whence, int64 offset, int64* result) {
- LARGE_INTEGER distance, res;
- distance.QuadPart = offset;
- DWORD move_method = static_cast<DWORD>(whence);
- if (!SetFilePointerEx(file_, distance, &res, move_method)) {
- DWORD error = GetLastError();
- LOG(WARNING) << "SetFilePointerEx failed: " << error;
- *result = RecordAndMapError(error,
- FILE_ERROR_SOURCE_SEEK,
- record_uma_,
- bound_net_log_);
- return;
- }
- if (async_context_.get()) {
- async_context_->set_error_source(FILE_ERROR_SOURCE_SEEK);
- SetOffset(async_context_->overlapped(), res);
- }
- *result = res.QuadPart;
-}
-
-void FileStreamWin::FlushFile(int* result) {
- if (FlushFileBuffers(file_)) {
- *result = OK;
- } else {
- *result = RecordAndMapError(GetLastError(),
- FILE_ERROR_SOURCE_FLUSH,
- record_uma_,
- bound_net_log_);
- }
-}
-
-void FileStreamWin::OnOpened(const CompletionCallback& callback, int* result) {
- if (*result == OK) {
- async_context_.reset(new AsyncContext(bound_net_log_));
- if (record_uma_)
- async_context_->EnableErrorStatistics();
- MessageLoopForIO::current()->RegisterIOHandler(file_,
- async_context_.get());
- }
-
- // Reset this before Run() as Run() may issue a new async operation.
- ResetOnIOComplete();
- callback.Run(*result);
-}
-
-void FileStreamWin::OnSeeked(
- const Int64CompletionCallback& callback,
- int64* result) {
- // Reset this before Run() as Run() may issue a new async operation.
- ResetOnIOComplete();
- callback.Run(*result);
-}
-
-void FileStreamWin::OnFlushed(const CompletionCallback& callback, int* result) {
- // Reset this before Run() as Run() may issue a new async operation.
- ResetOnIOComplete();
- callback.Run(*result);
-}
-
-void FileStreamWin::ResetOnIOComplete() {
- on_io_complete_.reset();
- weak_ptr_factory_.InvalidateWeakPtrs();
-}
-
-void FileStreamWin::WaitForIOCompletion() {
- // http://crbug.com/115067
- base::ThreadRestrictions::ScopedAllowWait allow_wait;
- if (on_io_complete_.get()) {
- on_io_complete_->Wait();
- on_io_complete_.reset();
- }
-}
-
-} // namespace net
diff --git a/net/base/file_stream_win.h b/net/base/file_stream_win.h
deleted file mode 100644
index e241f4c..0000000
--- a/net/base/file_stream_win.h
+++ /dev/null
@@ -1,104 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// This file implements FileStream for Windows.
-
-#ifndef NET_BASE_FILE_STREAM_WIN_H_
-#define NET_BASE_FILE_STREAM_WIN_H_
-
-#include "base/memory/scoped_ptr.h"
-#include "base/memory/weak_ptr.h"
-#include "base/platform_file.h"
-#include "base/synchronization/waitable_event.h"
-#include "net/base/completion_callback.h"
-#include "net/base/file_stream_whence.h"
-#include "net/base/net_export.h"
-#include "net/base/net_log.h"
-
-class FilePath;
-
-namespace base {
-class WaitableEvent;
-}
-
-namespace net {
-
-class IOBuffer;
-
-class NET_EXPORT FileStreamWin {
- public:
- explicit FileStreamWin(net::NetLog* net_log);
- FileStreamWin(base::PlatformFile file, int flags, net::NetLog* net_log);
- ~FileStreamWin();
-
- // FileStream implementations.
- void Close(const CompletionCallback& callback);
- void CloseSync();
- int Open(const FilePath& path, int open_flags,
- const CompletionCallback& callback);
- int OpenSync(const FilePath& path, int open_flags);
- bool IsOpen() const;
- int Seek(Whence whence, int64 offset,
- const Int64CompletionCallback& callback);
- int64 SeekSync(Whence whence, int64 offset);
- int64 Available();
- int Read(IOBuffer* buf, int buf_len, const CompletionCallback& callback);
- int ReadSync(char* buf, int buf_len);
- int ReadUntilComplete(char *buf, int buf_len);
- int Write(IOBuffer* buf, int buf_len, const CompletionCallback& callback);
- int WriteSync(const char* buf, int buf_len);
- int64 Truncate(int64 bytes);
- int Flush(const CompletionCallback& callback);
- int FlushSync();
- void EnableErrorStatistics();
- void SetBoundNetLogSource(const net::BoundNetLog& owner_bound_net_log);
- base::PlatformFile GetPlatformFileForTesting();
-
- private:
- class AsyncContext;
-
- // A helper method for Seek.
- void SeekFile(Whence whence, int64 offset, int64* result);
-
- // A helper method for Flush.
- void FlushFile(int* result);
-
- // Called when the file_ is opened asynchronously. |result| contains the
- // result as a network error code.
- void OnOpened(const CompletionCallback& callback, int* result);
-
- // Called when the file_ is closed asynchronously.
- void OnClosed(const CompletionCallback& callback);
-
- // Called when the file_ is seeked asynchronously.
- void OnSeeked(const Int64CompletionCallback& callback, int64* result);
-
- // Called when the file_ is flushed asynchronously.
- void OnFlushed(const CompletionCallback& callback, int* result);
-
- // Resets on_io_complete_ and WeakPtr's.
- // Called in OnOpened, OnClosed and OnSeeked.
- void ResetOnIOComplete();
-
- // Waits until the in-flight async open/close operation is complete.
- void WaitForIOCompletion();
-
- // This member is used to support asynchronous reads. It is non-null when
- // the FileStreamWin was opened with PLATFORM_FILE_ASYNC.
- scoped_ptr<AsyncContext> async_context_;
-
- base::PlatformFile file_;
- int open_flags_;
- bool auto_closed_;
- bool record_uma_;
- net::BoundNetLog bound_net_log_;
- base::WeakPtrFactory<FileStreamWin> weak_ptr_factory_;
- scoped_ptr<base::WaitableEvent> on_io_complete_;
-
- DISALLOW_COPY_AND_ASSIGN(FileStreamWin);
-};
-
-} // namespace net
-
-#endif // NET_BASE_FILE_STREAM_WIN_H_
diff --git a/net/base/net_log_event_type_list.h b/net/base/net_log_event_type_list.h
index 661fb6c..4cde922 100644
--- a/net/base/net_log_event_type_list.h
+++ b/net/base/net_log_event_type_list.h
@@ -1702,10 +1702,6 @@ EVENT_TYPE(FILE_STREAM_BOUND_TO_OWNER)
// }
EVENT_TYPE(FILE_STREAM_OPEN)
-// This event is created when a file stream's Close() is called.
-// This may occur even when the file is not open.
-EVENT_TYPE(FILE_STREAM_CLOSE)
-
// This event is created when a file stream operation has an error.
// {
// "operation": <open, write, close, etc>,
diff --git a/net/base/upload_file_element_reader.cc b/net/base/upload_file_element_reader.cc
index 11144e6..5658f21 100644
--- a/net/base/upload_file_element_reader.cc
+++ b/net/base/upload_file_element_reader.cc
@@ -41,7 +41,6 @@ int InitInternal(const FilePath& path,
if (rv < 0) {
DLOG(WARNING) << "Failed to seek \"" << path.value()
<< "\" to offset: " << range_offset << " (" << rv << ")";
- file_stream->CloseSync();
file_stream.reset();
}
}