blob: 3af58f89a3798fc460bb502c297d0b29b59e07b9 [file] [log] [blame]
// Copyright 2018 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 "remoting/host/file_transfer/buffered_file_writer.h"
#include "base/bind.h"
#include <utility>
namespace remoting {
BufferedFileWriter::BufferedFileWriter(
base::OnceClosure on_complete,
base::OnceCallback<void(protocol::FileTransfer_Error)> on_error)
: on_complete_(std::move(on_complete)),
on_error_(std::move(on_error)),
weak_ptr_factory_(this) {}
BufferedFileWriter::~BufferedFileWriter() = default;
void BufferedFileWriter::Start(FileOperations* file_operations,
const base::FilePath& filename) {
DCHECK_EQ(kNotStarted, state_);
SetState(kWorking);
file_operations->WriteFile(
filename, base::BindOnce(&BufferedFileWriter::OnWriteFileResult,
weak_ptr_factory_.GetWeakPtr()));
}
void BufferedFileWriter::Write(std::string data) {
if (state_ == kFailed) {
return;
}
DCHECK(state_ == kWorking || state_ == kWaiting);
chunks_.push(std::move(data));
if (state_ == kWaiting) {
SetState(kWorking);
WriteNextChunk();
}
}
void BufferedFileWriter::Close() {
if (state_ == kFailed) {
return;
}
DCHECK(state_ == kWorking || state_ == kWaiting);
State old_state = state_;
SetState(kClosing);
if (old_state != kWorking) {
DoClose();
}
}
void BufferedFileWriter::Cancel() {
SetState(kFailed);
// Will implicitly cancel if still in progress.
writer_.reset();
}
void BufferedFileWriter::OnWriteFileResult(
protocol::FileTransferResult<std::unique_ptr<FileOperations::Writer>>
result) {
OnWriteResult(std::move(result).Map([&](auto writer) {
writer_ = std::move(writer);
return kMonostate;
}));
}
void BufferedFileWriter::WriteNextChunk() {
DCHECK(!chunks_.empty());
DCHECK(state_ == kWorking || state_ == kClosing);
std::string data = std::move(chunks_.front());
chunks_.pop();
writer_->WriteChunk(std::move(data),
base::BindOnce(&BufferedFileWriter::OnWriteResult,
weak_ptr_factory_.GetWeakPtr()));
}
// Handles the result from both WriteFile and WriteChunk. For the former, it is
// called by OnWriteFileResult after setting writer_.
void BufferedFileWriter::OnWriteResult(
protocol::FileTransferResult<Monostate> result) {
if (!result) {
SetState(kFailed);
std::move(on_error_).Run(std::move(result.error()));
return;
}
if (!chunks_.empty()) {
WriteNextChunk();
} else if (state_ == kClosing) {
DoClose();
} else {
SetState(kWaiting);
}
}
void BufferedFileWriter::DoClose() {
DCHECK(chunks_.empty());
DCHECK_EQ(kClosing, state_);
writer_->Close(base::BindOnce(&BufferedFileWriter::OnCloseResult,
weak_ptr_factory_.GetWeakPtr()));
}
void BufferedFileWriter::OnCloseResult(
protocol::FileTransferResult<Monostate> result) {
if (!result) {
SetState(kFailed);
std::move(on_error_).Run(std::move(result.error()));
return;
}
SetState(kClosed);
std::move(on_complete_).Run();
}
void BufferedFileWriter::SetState(BufferedFileWriter::State state) {
switch (state) {
case kNotStarted:
// This is the initial state, but should never be reached again.
NOTREACHED();
break;
case kWorking:
DCHECK(state_ == kNotStarted || state_ == kWaiting);
break;
case kWaiting:
DCHECK(state_ == kWorking);
break;
case kClosing:
DCHECK(state_ == kWorking || state_ == kWaiting);
break;
case kClosed:
DCHECK(state_ == kClosing);
break;
case kFailed:
// Any state can change to kFailed.
break;
}
state_ = state;
}
} // namespace remoting