blob: 3850758c7b8a9cec66cb96708ad7f5c535d7d68c [file] [log] [blame]
// Copyright 2020 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.
#ifndef LIB_FIDL_LLCPP_INCLUDE_LIB_FIDL_LLCPP_EXTRACT_RESOURCE_ON_DESTRUCTION_H_
#define LIB_FIDL_LLCPP_INCLUDE_LIB_FIDL_LLCPP_EXTRACT_RESOURCE_ON_DESTRUCTION_H_
#include <lib/sync/completion.h>
#include <lib/zx/channel.h>
#include <zircon/assert.h>
#include <memory>
#include <optional>
namespace fidl {
namespace internal {
// Wraps some value that can be optionally moved out of the containing object
// during destruction. See |DestroyAndExtract| for more rationale.
// |Resource| is the type of the value, which is generally some resource
// (e.g. transport).
template <typename Resource>
class ExtractedOnDestruction {
public:
explicit ExtractedOnDestruction(Resource resource) : resource_(std::move(resource)) {}
~ExtractedOnDestruction() {
if (receiver_on_destruction_) {
*receiver_on_destruction_ = std::optional(std::move(resource_));
}
if (on_destruction_completion_) {
sync_completion_signal(on_destruction_completion_);
}
}
Resource& get() { return resource_; }
const Resource& get() const { return resource_; }
private:
template <typename Container, typename ResourceType, typename Callback>
friend void DestroyAndExtract(std::shared_ptr<Container>&& object,
ExtractedOnDestruction<ResourceType> Container::*member_path,
Callback callback);
Resource resource_;
std::optional<Resource>* receiver_on_destruction_ = nullptr;
sync_completion_t* on_destruction_completion_ = nullptr;
};
// Blocks until there are no other live references to the pointee of |object|,
// then extracts the field within it indexed by |member_path| during
// destruction, and finally returns it by passing it to |callback|.
//
// In a multi-threaded system, teardown can be safely arranged through the use
// of |std::shared_ptr<T>|: the last strong reference owner is responsible
// for destroying the object, regardless of which thread. However, we would
// often like to observe the destruction of this object, and extract important
// resource within it, on some specific thread. For example, a server binding
// object may be destroyed on any thread, but the "on-unbound" handler should
// always run from the dispatcher thread, and need to extract the transport within
// the server binding as it is being destructed.
//
// That extraction can be safely implemented by declaring a local resource
// variable in the observing thread, and storing its address inside the object
// to be destructed, such that the destructor has the opportunity to move out
// that resource into the local variable on the observing thread.
//
// This function implements this general behavior of the observing thread.
// |Container| is the type of the object that will be destructed, while
// |member_path| is a pointer-to-member from a |Container| type to a
// |ExtractedOnDestruction<Resource>| field. The caller should ensure that there
// are no other long-living strong references to |object|, then move its own
// strong reference into this function, which will trigger the destruction.
template <typename Container, typename Resource, typename Callback>
void DestroyAndExtract(std::shared_ptr<Container>&& object,
ExtractedOnDestruction<Resource> Container::*member_path,
Callback callback) {
ExtractedOnDestruction<Resource>* member = &(object.get()->*member_path);
sync_completion_t on_destruction;
member->on_destruction_completion_ = &on_destruction;
// Using an optional accommodates |Resource| types which do not
// have a default constructor.
std::optional<Resource> result;
member->receiver_on_destruction_ = &result;
// Trigger the destruction of |object|.
object.reset();
zx_status_t status = sync_completion_wait(&on_destruction, ZX_TIME_INFINITE);
ZX_ASSERT_MSG(status == ZX_OK, "Error waiting for object destruction.\n");
callback(std::move(result.value()));
}
} // namespace internal
} // namespace fidl
#endif // LIB_FIDL_LLCPP_INCLUDE_LIB_FIDL_LLCPP_EXTRACT_RESOURCE_ON_DESTRUCTION_H_