| #pragma once |
| |
| #include <utility> |
| |
| namespace c10 { |
| |
| // See example implementation in TensorBase.h and TensorBody.h. |
| // Synopsis: |
| // |
| // repr_type -- type to use to store an owned T in ExclusivelyOwned. |
| // |
| // pointer_type -- pointer-esque type to return from |
| // ExclusivelyOwned's get() and operator*() methods. |
| // |
| // const_pointer_type -- similar to pointer_type, used for the const methods. |
| // |
| // static repr_type nullRepr() -- return a null instance of repr_type. |
| // |
| // template <class... Args> |
| // static repr_type createInPlace(Args&&... args) -- used by the in-place |
| // ExclusivelyOwned constructor. |
| // |
| // static repr_type moveToRepr(T&& x) -- move the given x into an |
| // instance of repr_type. used by the ExclusivelyOwned(T&&) |
| // constructor. |
| // |
| // static void destroyOwned(repr_type x) -- free memory for a |
| // known-exclusively-owned instance of x. Replaces calling repr_type's |
| // destructor. Being able to implement this more efficiently than |
| // repr_type's destructor is the main reason to use ExclusivelyOwned |
| // for a type. |
| // |
| // static T take(repr_type&) -- move out of the given repr_type into an owned T. |
| // |
| // static pointer_type getImpl(const repr_type&) -- return a pointer |
| // to the given repr_type. May take repr_type by value if that is more |
| // efficient. |
| template <typename T> |
| struct ExclusivelyOwnedTraits; |
| |
| /// ExclusivelyOwned is a smart-pointer-like wrapper around an |
| /// exclusively-owned instance of some type T that normally has |
| /// mandatory reference counting (currently just Tensor). If you have |
| /// an isolated piece of code that knows that it has sole ownership of |
| /// an object of one of these types (i.e., because you created it |
| /// directly or using a factory function) and that object will not |
| /// escape from that isolated piece of code, then moving the object |
| /// into an ExclusivelyOwned will avoid an atomic reference count |
| /// decrement at destruction time. |
| /// |
| /// If you directly create the Tensor in the first |
| /// place, you can use the in_place constructor of ExclusivelyOwned to |
| /// additionally avoid doing any stores to initialize the refcount & |
| /// weakcount. |
| template <typename T> |
| class ExclusivelyOwned { |
| using EOT = ExclusivelyOwnedTraits<T>; |
| typename ExclusivelyOwnedTraits<T>::repr_type repr_; |
| |
| public: |
| ExclusivelyOwned() : repr_(EOT::nullRepr()) {} |
| |
| explicit ExclusivelyOwned(T&& t) : repr_(EOT::moveToRepr(std::move(t))) {} |
| |
| template <class... Args> |
| explicit ExclusivelyOwned(std::in_place_t, Args&&... args) |
| : repr_(EOT::createInPlace(std::forward<Args>(args)...)) {} |
| |
| ExclusivelyOwned(const ExclusivelyOwned&) = delete; |
| |
| ExclusivelyOwned(ExclusivelyOwned&& rhs) noexcept |
| : repr_(std::move(rhs.repr_)) { |
| rhs.repr_ = EOT::nullRepr(); |
| } |
| |
| ExclusivelyOwned& operator=(const ExclusivelyOwned&) = delete; |
| |
| ExclusivelyOwned& operator=(ExclusivelyOwned&& rhs) noexcept { |
| EOT::destroyOwned(repr_); |
| repr_ = std::move(rhs.repr_); |
| rhs.repr_ = EOT::nullRepr(); |
| return *this; |
| } |
| |
| ExclusivelyOwned& operator=(T&& rhs) noexcept { |
| EOT::destroyOwned(repr_); |
| repr_ = EOT::moveToRepr(std::move(rhs)); |
| return *this; |
| } |
| |
| ~ExclusivelyOwned() { |
| EOT::destroyOwned(repr_); |
| // Don't bother to call the destructor of repr_, since we already |
| // did specialized destruction for the exclusively-owned case in |
| // destroyOwned! |
| } |
| |
| // We don't provide this because it would require us to be able to |
| // differentiate an owned-but-empty T from a lack of T. This is |
| // particularly problematic for Tensor, which wants to use an |
| // undefined Tensor as its null state. |
| explicit operator bool() const noexcept = delete; |
| |
| operator T() && { |
| return take(); |
| } |
| |
| // NOTE: the equivalent operation on MaybeOwned is a moving |
| // operator*. For ExclusivelyOwned, take() and operator*() may well |
| // have different return types, so they are different functions. |
| T take() && { |
| return EOT::take(repr_); |
| } |
| |
| typename EOT::const_pointer_type operator->() const { |
| return get(); |
| } |
| |
| typename EOT::const_pointer_type get() const { |
| return EOT::getImpl(repr_); |
| } |
| |
| typename EOT::pointer_type operator->() { |
| return get(); |
| } |
| |
| typename EOT::pointer_type get() { |
| return EOT::getImpl(repr_); |
| } |
| |
| std::remove_pointer_t<typename EOT::const_pointer_type>& operator*() const { |
| return *get(); |
| } |
| |
| std::remove_pointer_t<typename EOT::pointer_type>& operator*() { |
| return *get(); |
| } |
| }; |
| |
| } // namespace c10 |