diff options
author | paivanof@gmail.com <paivanof@gmail.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-11-06 00:29:51 +0000 |
---|---|---|
committer | paivanof@gmail.com <paivanof@gmail.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-11-06 00:29:51 +0000 |
commit | aab1b9ead19f21af4f752c4a52beed65009d96fb (patch) | |
tree | 3679bc3c3bd80678a9fb2ab383aa6834679506d0 /net | |
parent | 25524c044d4aefd6902d4245b66980c46669e145 (diff) | |
download | chromium_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')
-rw-r--r-- | net/base/file_stream.cc | 234 | ||||
-rw-r--r-- | net/base/file_stream.h | 49 | ||||
-rw-r--r-- | net/base/file_stream_context.cc | 231 | ||||
-rw-r--r-- | net/base/file_stream_context.h | 230 | ||||
-rw-r--r-- | net/base/file_stream_context_posix.cc | 176 | ||||
-rw-r--r-- | net/base/file_stream_context_win.cc | 237 | ||||
-rw-r--r-- | net/base/file_stream_posix.cc | 683 | ||||
-rw-r--r-- | net/base/file_stream_posix.h | 83 | ||||
-rw-r--r-- | net/base/file_stream_unittest.cc | 381 | ||||
-rw-r--r-- | net/base/file_stream_win.cc | 778 | ||||
-rw-r--r-- | net/base/file_stream_win.h | 104 | ||||
-rw-r--r-- | net/base/net_log_event_type_list.h | 4 | ||||
-rw-r--r-- | net/base/upload_file_element_reader.cc | 1 | ||||
-rw-r--r-- | net/net.gyp | 8 | ||||
-rw-r--r-- | net/url_request/url_request_file_job.cc | 19 | ||||
-rw-r--r-- | net/url_request/url_request_file_job.h | 8 |
16 files changed, 1186 insertions, 2040 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(); } } diff --git a/net/net.gyp b/net/net.gyp index 98bc7ad..b3bd6a7 100644 --- a/net/net.gyp +++ b/net/net.gyp @@ -134,17 +134,17 @@ 'base/expiring_cache.h', 'base/file_stream.cc', 'base/file_stream.h', + 'base/file_stream_context.cc', + 'base/file_stream_context.h', + 'base/file_stream_context_posix.cc', + 'base/file_stream_context_win.cc', 'base/file_stream_metrics.cc', 'base/file_stream_metrics.h', 'base/file_stream_metrics_posix.cc', 'base/file_stream_metrics_win.cc', 'base/file_stream_net_log_parameters.cc', 'base/file_stream_net_log_parameters.h', - 'base/file_stream_posix.cc', - 'base/file_stream_posix.h', 'base/file_stream_whence.h', - 'base/file_stream_win.cc', - 'base/file_stream_win.h', 'base/filter.cc', 'base/filter.h', 'base/gzip_filter.cc', diff --git a/net/url_request/url_request_file_job.cc b/net/url_request/url_request_file_job.cc index 90512dc..c60b313 100644 --- a/net/url_request/url_request_file_job.cc +++ b/net/url_request/url_request_file_job.cc @@ -29,6 +29,7 @@ #include "base/threading/thread_restrictions.h" #include "build/build_config.h" #include "googleurl/src/gurl.h" +#include "net/base/file_stream.h" #include "net/base/io_buffer.h" #include "net/base/load_flags.h" #include "net/base/mime_util.h" @@ -90,7 +91,6 @@ URLRequestFileJob::URLRequestFileJob(URLRequest* request, const FilePath& file_path) : URLRequestJob(request, network_delegate), file_path_(file_path), - stream_(NULL), is_directory_(false), remaining_bytes_(0) { } @@ -133,10 +133,7 @@ void URLRequestFileJob::Start() { } void URLRequestFileJob::Kill() { - // URL requests should not block on the disk! - // http://code.google.com/p/chromium/issues/detail?id=59849 - base::ThreadRestrictions::ScopedAllowIO allow_io; - stream_.CloseSync(); + stream_.reset(); if (async_resolver_) { async_resolver_->Cancel(); @@ -162,9 +159,9 @@ bool URLRequestFileJob::ReadRawData(IOBuffer* dest, int dest_size, return true; } - int rv = stream_.Read(dest, dest_size, - base::Bind(&URLRequestFileJob::DidRead, - base::Unretained(this))); + int rv = stream_->Read(dest, dest_size, + base::Bind(&URLRequestFileJob::DidRead, + base::Unretained(this))); if (rv >= 0) { // Data is immediately available. *bytes_read = rv; @@ -281,6 +278,8 @@ void URLRequestFileJob::DidResolve( if (!exists) { rv = ERR_FILE_NOT_FOUND; } else if (!is_directory_) { + stream_.reset(new FileStream(NULL)); + // URL requests should not block on the disk! // http://code.google.com/p/chromium/issues/detail?id=59849 base::ThreadRestrictions::ScopedAllowIO allow_io; @@ -288,7 +287,7 @@ void URLRequestFileJob::DidResolve( int flags = base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_READ | base::PLATFORM_FILE_ASYNC; - rv = stream_.OpenSync(file_path_, flags); + rv = stream_->OpenSync(file_path_, flags); } if (rv != OK) { @@ -314,7 +313,7 @@ void URLRequestFileJob::DidResolve( if (remaining_bytes_ > 0 && byte_range_.first_byte_position() != 0 && byte_range_.first_byte_position() != - stream_.SeekSync(FROM_BEGIN, byte_range_.first_byte_position())) { + stream_->SeekSync(FROM_BEGIN, byte_range_.first_byte_position())) { NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, ERR_REQUEST_RANGE_NOT_SATISFIABLE)); return; diff --git a/net/url_request/url_request_file_job.h b/net/url_request/url_request_file_job.h index 4f34314..15c7d62 100644 --- a/net/url_request/url_request_file_job.h +++ b/net/url_request/url_request_file_job.h @@ -9,18 +9,22 @@ #include <vector> #include "base/file_path.h" -#include "net/base/file_stream.h" #include "net/base/net_export.h" #include "net/http/http_byte_range.h" #include "net/url_request/url_request.h" #include "net/url_request/url_request_job.h" +namespace base{ +struct PlatformFileInfo; +} namespace file_util { struct FileInfo; } namespace net { +class FileStream; + // A request job that handles reading file URLs class NET_EXPORT URLRequestFileJob : public URLRequestJob { public: @@ -56,7 +60,7 @@ class NET_EXPORT URLRequestFileJob : public URLRequestJob { // Callback after data is asynchronously read from the file. void DidRead(int result); - FileStream stream_; + scoped_ptr<FileStream> stream_; bool is_directory_; HttpByteRange byte_range_; |