| // 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 "src/storage/lib/vfs/cpp/managed_vfs.h" |
| |
| #include <lib/async/cpp/task.h> |
| #include <lib/fit/function.h> |
| #include <lib/sync/completion.h> |
| #include <zircon/errors.h> |
| |
| #include <memory> |
| #include <utility> |
| |
| #include "src/storage/lib/vfs/cpp/connection/connection.h" |
| |
| namespace fs { |
| |
| ManagedVfs::ManagedVfs(async_dispatcher_t* dispatcher) : FuchsiaVfs(dispatcher) { |
| ZX_DEBUG_ASSERT(dispatcher); |
| } |
| |
| ManagedVfs::~ManagedVfs() { ZX_DEBUG_ASSERT(connections_.is_empty()); } |
| |
| bool ManagedVfs::NoMoreClients() const { |
| return is_shutting_down_.load() && connections_.is_empty(); |
| } |
| |
| // Asynchronously drop all connections. |
| void ManagedVfs::Shutdown(ShutdownCallback handler) { |
| ZX_DEBUG_ASSERT(handler); |
| ZX_DEBUG_ASSERT(!is_shutting_down_.load()); |
| zx_status_t status = |
| async::PostTask(dispatcher(), [this, closure = std::move(handler)]() mutable { |
| std::lock_guard lock(lock_); |
| ZX_DEBUG_ASSERT(!shutdown_handler_); |
| shutdown_handler_ = std::move(closure); |
| is_shutting_down_.store(true); |
| |
| // Signal the teardown on channels in a way that doesn't potentially pull them out from |
| // underneath async callbacks. |
| std::for_each(connections_.begin(), connections_.end(), |
| std::mem_fn(&internal::Connection::Unbind)); |
| |
| MaybeAsyncFinishShutdown(); |
| }); |
| ZX_DEBUG_ASSERT_MSG(status == ZX_OK, "status = %s", zx_status_get_string(status)); |
| } |
| |
| void ManagedVfs::CloseAllConnectionsForVnode(const Vnode& node, |
| CloseAllConnectionsForVnodeCallback callback) { |
| async::PostTask(dispatcher(), [this, &node, callback = std::move(callback)]() mutable { |
| // Each connection to the Vnode takes a copy of the shared pointer of the AutoCall. When a |
| // connection is finished closing, |UnregisterConnection| will drop the copy and when the last |
| // copy is dropped the AutoCall will call |callback|. |
| auto closer = std::make_shared<fit::deferred_action<fit::callback<void()>>>( |
| [callback = std::move(callback)]() mutable { |
| if (callback) { |
| callback(); |
| } |
| }); // Must go before |lock|. |
| std::lock_guard lock(lock_); |
| for (internal::Connection& connection : connections_) { |
| if (connection.vnode().get() == &node) { |
| [[maybe_unused]] zx::result result = connection.Unbind(); |
| closing_connections_.emplace(&connection, closer); |
| } |
| } |
| // |closer| will call the callback here if no connections needed closing. |
| }); |
| } |
| |
| // Trigger "OnShutdownComplete" if all preconditions have been met. |
| void ManagedVfs::MaybeAsyncFinishShutdown() { |
| if (NoMoreClients()) { |
| shutdown_task_.Post(dispatcher()); |
| } |
| } |
| |
| void ManagedVfs::FinishShutdown(async_dispatcher_t*, async::TaskBase*, |
| zx_status_t dispatcher_status) { |
| // Call the shutdown function outside of the lock since it can cause |this| to be deleted which |
| // will in turn delete the lock object itself. |
| ShutdownCallback handler; |
| { |
| std::lock_guard lock(lock_); |
| ZX_ASSERT_MSG(NoMoreClients(), "Failed to complete VFS shutdown: dispatcher status = %d\n", |
| dispatcher_status); |
| ZX_DEBUG_ASSERT(shutdown_handler_); |
| handler = std::move(shutdown_handler_); |
| } |
| |
| handler(ZX_OK); |
| // |this| can be deleted at this point! |
| } |
| |
| zx_status_t ManagedVfs::RegisterConnection(std::unique_ptr<internal::Connection> connection, |
| zx::channel channel) { |
| std::lock_guard lock(lock_); |
| if (is_shutting_down_.load()) { |
| return ZX_ERR_CANCELED; |
| } |
| connections_.push_back(std::move(connection)); |
| connections_.back().Bind(std::move(channel), [this](internal::Connection* connection) { |
| std::shared_ptr<fit::deferred_action<fit::callback<void()>>> closer; // Must go before lock. |
| std::lock_guard lock(lock_); |
| |
| auto iter = closing_connections_.find(connection); |
| if (iter != closing_connections_.end()) { |
| closer = iter->second; |
| closing_connections_.erase(iter); |
| } |
| |
| // We drop the result of |erase| on the floor, effectively destroying the connection when all |
| // other references (like async callbacks) have completed. |
| connections_.erase(*connection); |
| MaybeAsyncFinishShutdown(); |
| |
| if (connections_.is_empty()) { |
| OnNoConnections(); |
| } |
| |
| // |closer| will call the callback here if it's the last connection to be closed. |
| }); |
| return ZX_OK; |
| } |
| |
| bool ManagedVfs::IsTerminating() const { return is_shutting_down_.load(); } |
| |
| } // namespace fs |