diff --git a/engine/src/flutter/glue/BUILD.gn b/engine/src/flutter/glue/BUILD.gn index c6a7b3a5a76..9a39af5d1c0 100644 --- a/engine/src/flutter/glue/BUILD.gn +++ b/engine/src/flutter/glue/BUILD.gn @@ -4,6 +4,8 @@ source_set("glue") { sources = [ + "data_pipe_utils.cc", + "data_pipe_utils.h", "drain_data_pipe_job.cc", "drain_data_pipe_job.h", "movable_wrapper.h", @@ -20,5 +22,6 @@ source_set("glue") { "//base", "//lib/ftl", "//mojo/data_pipe_utils", + "//mojo/public/cpp/environment", ] } diff --git a/engine/src/flutter/glue/data_pipe_utils.cc b/engine/src/flutter/glue/data_pipe_utils.cc new file mode 100644 index 00000000000..2412e032dc5 --- /dev/null +++ b/engine/src/flutter/glue/data_pipe_utils.cc @@ -0,0 +1,211 @@ +// Copyright 2016 The Fuchsia 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 "glue/data_pipe_utils.h" + +#include +#include + +#include +#include +#include + +#include "lib/ftl/files/file_descriptor.h" +#include "mojo/public/cpp/environment/async_waiter.h" +#include "mojo/public/cpp/environment/environment.h" + +namespace glue { +namespace { + +// CopyToFileHandler ----------------------------------------------------------- + +class CopyToFileHandler { + public: + CopyToFileHandler(mojo::ScopedDataPipeConsumerHandle source, + ftl::UniqueFD destination, + ftl::TaskRunner* task_runner, + const std::function& callback); + + private: + ~CopyToFileHandler(); + + void SendCallback(bool value); + void OnHandleReady(MojoResult result); + static void WaitComplete(void* context, MojoResult result); + + mojo::ScopedDataPipeConsumerHandle source_; + ftl::UniqueFD destination_; + ftl::RefPtr task_runner_; + std::function callback_; + const MojoAsyncWaiter* waiter_; + MojoAsyncWaitID wait_id_; + + FTL_DISALLOW_COPY_AND_ASSIGN(CopyToFileHandler); +}; + +CopyToFileHandler::CopyToFileHandler(mojo::ScopedDataPipeConsumerHandle source, + ftl::UniqueFD destination, + ftl::TaskRunner* task_runner, + const std::function& callback) + : source_(std::move(source)), + destination_(std::move(destination)), + task_runner_(task_runner), + callback_(callback), + waiter_(mojo::Environment::GetDefaultAsyncWaiter()), + wait_id_(0) { + task_runner_->PostTask([this]() { OnHandleReady(MOJO_RESULT_OK); }); +} + +CopyToFileHandler::~CopyToFileHandler() {} + +void CopyToFileHandler::SendCallback(bool value) { + FTL_DCHECK(!wait_id_); + auto callback = callback_; + delete this; + callback(value); +} + +void CopyToFileHandler::OnHandleReady(MojoResult result) { + if (result == MOJO_RESULT_OK) { + const void* buffer = nullptr; + uint32_t size = 0; + result = BeginReadDataRaw(source_.get(), &buffer, &size, + MOJO_READ_DATA_FLAG_NONE); + if (result == MOJO_RESULT_OK) { + bool write_success = ftl::WriteFileDescriptor( + destination_.get(), static_cast(buffer), size); + result = EndReadDataRaw(source_.get(), size); + if (!write_success || result != MOJO_RESULT_OK) { + SendCallback(false); + } else { + task_runner_->PostTask([this]() { OnHandleReady(MOJO_RESULT_OK); }); + } + return; + } + } + if (result == MOJO_RESULT_FAILED_PRECONDITION) { + SendCallback(true); + return; + } + if (result == MOJO_RESULT_SHOULD_WAIT) { + wait_id_ = + waiter_->AsyncWait(source_.get().value(), MOJO_HANDLE_SIGNAL_READABLE, + MOJO_DEADLINE_INDEFINITE, &WaitComplete, this); + return; + } + SendCallback(false); +} + +void CopyToFileHandler::WaitComplete(void* context, MojoResult result) { + CopyToFileHandler* handler = static_cast(context); + handler->wait_id_ = 0; + handler->OnHandleReady(result); +} + +// CopyFromFileHandler --------------------------------------------------------- + +class CopyFromFileHandler { + public: + CopyFromFileHandler(ftl::UniqueFD source, + mojo::ScopedDataPipeProducerHandle destination, + ftl::TaskRunner* task_runner, + const std::function& callback); + + private: + ~CopyFromFileHandler(); + + void SendCallback(bool value); + void OnHandleReady(MojoResult result); + static void WaitComplete(void* context, MojoResult result); + + ftl::UniqueFD source_; + mojo::ScopedDataPipeProducerHandle destination_; + ftl::RefPtr task_runner_; + std::function callback_; + const MojoAsyncWaiter* waiter_; + MojoAsyncWaitID wait_id_; + + FTL_DISALLOW_COPY_AND_ASSIGN(CopyFromFileHandler); +}; + +CopyFromFileHandler::CopyFromFileHandler( + ftl::UniqueFD source, + mojo::ScopedDataPipeProducerHandle destination, + ftl::TaskRunner* task_runner, + const std::function& callback) + : source_(std::move(source)), + destination_(std::move(destination)), + task_runner_(task_runner), + callback_(callback), + waiter_(mojo::Environment::GetDefaultAsyncWaiter()), + wait_id_(0) { + task_runner_->PostTask([this]() { OnHandleReady(MOJO_RESULT_OK); }); +} + +CopyFromFileHandler::~CopyFromFileHandler() {} + +void CopyFromFileHandler::SendCallback(bool value) { + FTL_DCHECK(!wait_id_); + auto callback = callback_; + delete this; + callback(value); +} + +void CopyFromFileHandler::OnHandleReady(MojoResult result) { + if (result == MOJO_RESULT_OK) { + void* buffer = nullptr; + uint32_t size = 0; + result = BeginWriteDataRaw(destination_.get(), &buffer, &size, + MOJO_WRITE_DATA_FLAG_NONE); + if (result == MOJO_RESULT_OK) { + FTL_DCHECK(size < static_cast(std::numeric_limits::max())); + ssize_t bytes_read = ftl::ReadFileDescriptor( + source_.get(), static_cast(buffer), size); + result = EndWriteDataRaw(destination_.get(), + std::max(0l, bytes_read)); + if (bytes_read == -1 || result != MOJO_RESULT_OK) { + SendCallback(false); + } else if (bytes_read < size) { + // Reached EOF. Stop the process. + SendCallback(true); + } else { + task_runner_->PostTask([this]() { OnHandleReady(MOJO_RESULT_OK); }); + } + return; + } + } + if (result == MOJO_RESULT_SHOULD_WAIT) { + wait_id_ = waiter_->AsyncWait( + destination_.get().value(), MOJO_HANDLE_SIGNAL_WRITABLE, + MOJO_DEADLINE_INDEFINITE, &WaitComplete, this); + return; + } + SendCallback(false); +} + +void CopyFromFileHandler::WaitComplete(void* context, MojoResult result) { + CopyFromFileHandler* handler = static_cast(context); + handler->wait_id_ = 0; + handler->OnHandleReady(result); +} + +} // namespace + +void CopyToFileDescriptor(mojo::ScopedDataPipeConsumerHandle source, + ftl::UniqueFD destination, + ftl::TaskRunner* task_runner, + const std::function& callback) { + new CopyToFileHandler(std::move(source), std::move(destination), task_runner, + callback); +} + +void CopyFromFileDescriptor(ftl::UniqueFD source, + mojo::ScopedDataPipeProducerHandle destination, + ftl::TaskRunner* task_runner, + const std::function& callback) { + new CopyFromFileHandler(std::move(source), std::move(destination), + task_runner, callback); +} + +} // namespace glue diff --git a/engine/src/flutter/glue/data_pipe_utils.h b/engine/src/flutter/glue/data_pipe_utils.h new file mode 100644 index 00000000000..4ae71c425eb --- /dev/null +++ b/engine/src/flutter/glue/data_pipe_utils.h @@ -0,0 +1,38 @@ +// Copyright 2016 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. + +#ifndef GLUE_DATA_PIPE_FILES_H_ +#define GLUE_DATA_PIPE_FILES_H_ + +#include + +#include + +#include "lib/ftl/files/unique_fd.h" +#include "lib/ftl/tasks/task_runner.h" +#include "mojo/public/cpp/system/data_pipe.h" + +namespace glue { + +// Asynchronously copies data from source to the destination file descriptor. +// The given |callback| is run upon completion. File writes and |callback| will +// be scheduled on the given |task_runner|. +void CopyToFileDescriptor( + mojo::ScopedDataPipeConsumerHandle source, + ftl::UniqueFD destination, + ftl::TaskRunner* task_runner, + const std::function& callback); + +// Asynchronously copies data from source file to the destination. The given +// |callback| is run upon completion. File reads and |callback| will be +// scheduled to the given |task_runner|. +void CopyFromFileDescriptor( + ftl::UniqueFD source, + mojo::ScopedDataPipeProducerHandle destination, + ftl::TaskRunner* task_runner, + const std::function& callback); + +} // namespace glue + +#endif // GLUE_DATA_PIPE_FILES_H_