Commit 86439789 authored by Austin Sullivan's avatar Austin Sullivan Committed by Chromium LUCI CQ

[FSA] Destroy FileWriter on Close() or Abort()

Simplifies the lifetime of file writers. Previously, if the mojo pipe
outlived the usefulness of the writer (i.e. after the writer had been
either closed or aborted), the object would hang around until the pipe
closed.

Now, the writer is destroyed (and the mojo pipe closed) when the writer
is closed or aborted.

Bug: N/A
Change-Id: I69efecba1fe8d6c94f778defa6f0eaf91d088fc6
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2595905
Commit-Queue: Austin Sullivan <asully@chromium.org>
Reviewed-by: default avatarMarijn Kruisselbrink <mek@chromium.org>
Reviewed-by: default avatarDaniel Cheng <dcheng@chromium.org>
Cr-Commit-Position: refs/heads/master@{#841513}
parent f83b17aa
...@@ -66,7 +66,9 @@ class CONTENT_EXPORT NativeFileSystemFileWriterImpl ...@@ -66,7 +66,9 @@ class CONTENT_EXPORT NativeFileSystemFileWriterImpl
WriteStreamCallback callback) override; WriteStreamCallback callback) override;
void Truncate(uint64_t length, TruncateCallback callback) override; void Truncate(uint64_t length, TruncateCallback callback) override;
// The writer will be destroyed upon completion.
void Close(CloseCallback callback) override; void Close(CloseCallback callback) override;
// The writer will be destroyed upon completion.
void Abort(AbortCallback callback) override; void Abort(AbortCallback callback) override;
using HashCallback = base::OnceCallback< using HashCallback = base::OnceCallback<
...@@ -82,10 +84,13 @@ class CONTENT_EXPORT NativeFileSystemFileWriterImpl ...@@ -82,10 +84,13 @@ class CONTENT_EXPORT NativeFileSystemFileWriterImpl
mojo::Receiver<blink::mojom::FileSystemAccessFileWriter> receiver_; mojo::Receiver<blink::mojom::FileSystemAccessFileWriter> receiver_;
// If the mojo pipe is severed before either Close() or Abort() is invoked,
// the transaction is aborted from the OnDisconnect method. Otherwise, the
// writer will be destroyed upon completion of Close() or Abort().
void OnDisconnect(); void OnDisconnect();
// Delete the FileWriter after Close if the mojo pipe is unbound. // Destroys the file writer after calling the close callback.
void CallCloseCallbackAndMaybeDeleteThis( void CallCloseCallbackAndDeleteThis(
blink::mojom::FileSystemAccessErrorPtr result); blink::mojom::FileSystemAccessErrorPtr result);
void WriteImpl(uint64_t offset, void WriteImpl(uint64_t offset,
...@@ -125,44 +130,13 @@ class CONTENT_EXPORT NativeFileSystemFileWriterImpl ...@@ -125,44 +130,13 @@ class CONTENT_EXPORT NativeFileSystemFileWriterImpl
void ComputeHashForSwapFile(HashCallback callback); void ComputeHashForSwapFile(HashCallback callback);
enum class State { bool is_close_pending() const { return !close_callback_.is_null(); }
// The writer accepts write operations.
kOpen,
// The writer does not accept write operations and is in the process of
// closing.
kClosePending,
// The writer does not accept write operations and has entered an error
// state. A swap file may need to be purged.
kCloseError,
// The writer does not accept write operations. There should be no more swap
// file.
kClosed,
};
bool is_closed() const { return state_ != State::kOpen; }
// Returns whether the File Writer is in a state where any files can be
// deleted. We do not want to delete the files if there are clean-up
// operations in-flight.
bool can_purge() const {
return state_ == State::kOpen || state_ == State::kCloseError;
}
// We write using this file URL. When `Close()` is invoked, we // We write using this file URL. When `Close()` is invoked, we
// execute a move operation from the swap URL to the target URL at `url_`. In // execute a move operation from the swap URL to the target URL at `url_`. In
// most filesystems, this move operation is atomic. // most filesystems, this move operation is atomic.
storage::FileSystemURL swap_url_; storage::FileSystemURL swap_url_;
// NativeFileSystemWriter lifetime management has the following cases:
// 1) The mojo pipe is severed before Close() is invoked.
// - Abort the transaction from the OnDisconnect method.
// 2) The mojo pipe is severed before Close() finishes.
// - The Close() call is allowed to finish.
// - The Writer is destroyed immediately afterwards, via the
// CallCloseCallbackAndMaybeDeleteThis method.
// 3) The mojo pipe exists when Close() finishes.
// - The Writer will exist for as long as the mojo pipe is connected.
// - The Writer is destroyed via the OnDisconnect method.
State state_ = State::kOpen;
// This callback is non-null when the State is kClosePending or kCloseError.
CloseCallback close_callback_; CloseCallback close_callback_;
download::QuarantineConnectionCallback quarantine_connection_callback_; download::QuarantineConnectionCallback quarantine_connection_callback_;
......
...@@ -549,7 +549,7 @@ TEST_F(NativeFileSystemFileWriterImplTest, TruncateGrow) { ...@@ -549,7 +549,7 @@ TEST_F(NativeFileSystemFileWriterImplTest, TruncateGrow) {
EXPECT_EQ(std::string("abc\0\0", 5), ReadFile(test_file_url_)); EXPECT_EQ(std::string("abc\0\0", 5), ReadFile(test_file_url_));
} }
TEST_F(NativeFileSystemFileWriterImplTest, CloseAfterCloseNotOK) { TEST_F(NativeFileSystemFileWriterImplTest, WriterDestroyedAfterClose) {
uint64_t bytes_written; uint64_t bytes_written;
FileSystemAccessStatus result = WriteSync(0, "abc", &bytes_written); FileSystemAccessStatus result = WriteSync(0, "abc", &bytes_written);
EXPECT_EQ(result, FileSystemAccessStatus::kOk); EXPECT_EQ(result, FileSystemAccessStatus::kOk);
...@@ -557,49 +557,13 @@ TEST_F(NativeFileSystemFileWriterImplTest, CloseAfterCloseNotOK) { ...@@ -557,49 +557,13 @@ TEST_F(NativeFileSystemFileWriterImplTest, CloseAfterCloseNotOK) {
result = CloseSync(); result = CloseSync();
EXPECT_EQ(result, FileSystemAccessStatus::kOk); EXPECT_EQ(result, FileSystemAccessStatus::kOk);
result = CloseSync(); EXPECT_TRUE(handle_.WasInvalidated());
EXPECT_EQ(result, FileSystemAccessStatus::kInvalidState); EXPECT_FALSE(storage::AsyncFileTestHelper::FileExists(
} file_system_context_.get(), test_swap_url_,
storage::AsyncFileTestHelper::kDontCheckSize));
TEST_F(NativeFileSystemFileWriterImplTest, TruncateAfterCloseNotOK) {
uint64_t bytes_written;
FileSystemAccessStatus result = WriteSync(0, "abc", &bytes_written);
EXPECT_EQ(result, FileSystemAccessStatus::kOk);
EXPECT_EQ(bytes_written, 3u);
result = CloseSync();
EXPECT_EQ(result, FileSystemAccessStatus::kOk);
result = TruncateSync(0);
EXPECT_EQ(result, FileSystemAccessStatus::kInvalidState);
}
TEST_P(NativeFileSystemFileWriterImplWriteTest, WriteAfterCloseNotOK) {
uint64_t bytes_written;
FileSystemAccessStatus result = WriteSync(0, "abc", &bytes_written);
EXPECT_EQ(result, FileSystemAccessStatus::kOk);
EXPECT_EQ(bytes_written, 3u);
result = CloseSync();
EXPECT_EQ(result, FileSystemAccessStatus::kOk);
result = WriteSync(0, "bcd", &bytes_written);
EXPECT_EQ(result, FileSystemAccessStatus::kInvalidState);
}
TEST_F(NativeFileSystemFileWriterImplTest, AbortAfterCloseNotOK) {
uint64_t bytes_written;
FileSystemAccessStatus result = WriteSync(0, "abc", &bytes_written);
EXPECT_EQ(result, FileSystemAccessStatus::kOk);
EXPECT_EQ(bytes_written, 3u);
result = CloseSync();
EXPECT_EQ(result, FileSystemAccessStatus::kOk);
result = AbortSync();
EXPECT_EQ(result, FileSystemAccessStatus::kInvalidState);
} }
TEST_F(NativeFileSystemFileWriterImplTest, AbortOK) { TEST_F(NativeFileSystemFileWriterImplTest, WriterDestroyedAfterAbort) {
uint64_t bytes_written; uint64_t bytes_written;
FileSystemAccessStatus result = WriteSync(0, "abc", &bytes_written); FileSystemAccessStatus result = WriteSync(0, "abc", &bytes_written);
EXPECT_EQ(result, FileSystemAccessStatus::kOk); EXPECT_EQ(result, FileSystemAccessStatus::kOk);
...@@ -608,44 +572,10 @@ TEST_F(NativeFileSystemFileWriterImplTest, AbortOK) { ...@@ -608,44 +572,10 @@ TEST_F(NativeFileSystemFileWriterImplTest, AbortOK) {
result = AbortSync(); result = AbortSync();
EXPECT_EQ(result, FileSystemAccessStatus::kOk); EXPECT_EQ(result, FileSystemAccessStatus::kOk);
EXPECT_EQ("", ReadFile(test_file_url_)); EXPECT_EQ("", ReadFile(test_file_url_));
} EXPECT_TRUE(handle_.WasInvalidated());
EXPECT_FALSE(storage::AsyncFileTestHelper::FileExists(
TEST_F(NativeFileSystemFileWriterImplTest, TruncateAfterAbortNotOK) { file_system_context_.get(), test_swap_url_,
uint64_t bytes_written; storage::AsyncFileTestHelper::kDontCheckSize));
FileSystemAccessStatus result = WriteSync(0, "abc", &bytes_written);
EXPECT_EQ(result, FileSystemAccessStatus::kOk);
EXPECT_EQ(bytes_written, 3u);
result = AbortSync();
EXPECT_EQ(result, FileSystemAccessStatus::kOk);
result = TruncateSync(0);
EXPECT_EQ(result, FileSystemAccessStatus::kInvalidState);
}
TEST_P(NativeFileSystemFileWriterImplWriteTest, WriteAfterAbortNotOK) {
uint64_t bytes_written;
FileSystemAccessStatus result = WriteSync(0, "abc", &bytes_written);
EXPECT_EQ(result, FileSystemAccessStatus::kOk);
EXPECT_EQ(bytes_written, 3u);
result = AbortSync();
EXPECT_EQ(result, FileSystemAccessStatus::kOk);
result = WriteSync(0, "bcd", &bytes_written);
EXPECT_EQ(result, FileSystemAccessStatus::kInvalidState);
}
TEST_F(NativeFileSystemFileWriterImplTest, CloseAfterAbortNotOK) {
uint64_t bytes_written;
FileSystemAccessStatus result = WriteSync(0, "abc", &bytes_written);
EXPECT_EQ(result, FileSystemAccessStatus::kOk);
EXPECT_EQ(bytes_written, 3u);
result = AbortSync();
EXPECT_EQ(result, FileSystemAccessStatus::kOk);
result = CloseSync();
EXPECT_EQ(result, FileSystemAccessStatus::kInvalidState);
} }
// TODO(mek): More tests, particularly for error conditions. // TODO(mek): More tests, particularly for error conditions.
......
...@@ -32,13 +32,15 @@ interface FileSystemAccessFileWriter { ...@@ -32,13 +32,15 @@ interface FileSystemAccessFileWriter {
// Closes the file writer. This will materialize the writes operations on the // Closes the file writer. This will materialize the writes operations on the
// intended file target in the case of atomic writes. // intended file target in the case of atomic writes.
// The mojo pipe will be destroyed when Close() completes.
// Specify the |autoClose| flag to ensure Close() is automatically invoked // Specify the |autoClose| flag to ensure Close() is automatically invoked
// when the mojo pipe closes. // when the mojo pipe closes.
// Returns whether the operation succeeded. // Returns whether the operation succeeded.
Close() => (FileSystemAccessError result); Close() => (FileSystemAccessError result);
// Aborts the write operation, resulting in the writes not being committed, // Aborts the write operation, resulting in the writes not being committed,
// even if autoClose is specified. All further operations will be rejected. // even if |autoClose| is specified. The mojo pipe will be destroyed when
// Abort() completes.
// Returns whether the write operation was aborted successfully. // Returns whether the write operation was aborted successfully.
Abort() => (FileSystemAccessError result); Abort() => (FileSystemAccessError result);
}; };
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment