MaybeOwned.h 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. #pragma once
  2. #include <c10/macros/Macros.h>
  3. #include <c10/util/Exception.h>
  4. #include <c10/util/in_place.h>
  5. #include <type_traits>
  6. namespace c10 {
  7. /// MaybeOwnedTraits<T> describes how to borrow from T. Here is how we
  8. /// can implement borrowing from an arbitrary type T using a raw
  9. /// pointer to const:
  10. template <typename T>
  11. struct MaybeOwnedTraitsGenericImpl {
  12. using owned_type = T;
  13. using borrow_type = const T*;
  14. static borrow_type createBorrow(const owned_type& from) {
  15. return &from;
  16. }
  17. static void assignBorrow(borrow_type& lhs, borrow_type rhs) {
  18. lhs = rhs;
  19. }
  20. static void destroyBorrow(borrow_type& /*toDestroy*/) {}
  21. static const owned_type& referenceFromBorrow(const borrow_type& borrow) {
  22. return *borrow;
  23. }
  24. static const owned_type* pointerFromBorrow(const borrow_type& borrow) {
  25. return borrow;
  26. }
  27. static bool debugBorrowIsValid(const borrow_type& borrow) {
  28. return borrow != nullptr;
  29. }
  30. };
  31. /// It is possible to eliminate the extra layer of indirection for
  32. /// borrows for some types that we control. For examples, see
  33. /// intrusive_ptr.h and TensorBody.h.
  34. template <typename T>
  35. struct MaybeOwnedTraits;
  36. // Explicitly enable MaybeOwned<shared_ptr<T>>, rather than allowing
  37. // MaybeOwned to be used for any type right away.
  38. template <typename T>
  39. struct MaybeOwnedTraits<std::shared_ptr<T>>
  40. : public MaybeOwnedTraitsGenericImpl<std::shared_ptr<T>> {};
  41. /// A smart pointer around either a borrowed or owned T. When
  42. /// constructed with borrowed(), the caller MUST ensure that the
  43. /// borrowed-from argument outlives this MaybeOwned<T>. Compare to
  44. /// Rust's std::borrow::Cow
  45. /// (https://doc.rust-lang.org/std/borrow/enum.Cow.html), but note
  46. /// that it is probably not suitable for general use because C++ has
  47. /// no borrow checking. Included here to support
  48. /// Tensor::expect_contiguous.
  49. template <typename T>
  50. class MaybeOwned final {
  51. using borrow_type = typename MaybeOwnedTraits<T>::borrow_type;
  52. using owned_type = typename MaybeOwnedTraits<T>::owned_type;
  53. bool isBorrowed_;
  54. union {
  55. borrow_type borrow_;
  56. owned_type own_;
  57. };
  58. /// Don't use this; use borrowed() instead.
  59. explicit MaybeOwned(const owned_type& t)
  60. : isBorrowed_(true), borrow_(MaybeOwnedTraits<T>::createBorrow(t)) {}
  61. /// Don't use this; use owned() instead.
  62. explicit MaybeOwned(T&& t) noexcept(
  63. std::is_nothrow_move_constructible<T>::value)
  64. : isBorrowed_(false), own_(std::move(t)) {}
  65. /// Don't use this; use owned() instead.
  66. template <class... Args>
  67. explicit MaybeOwned(in_place_t, Args&&... args)
  68. : isBorrowed_(false), own_(std::forward<Args>(args)...) {}
  69. public:
  70. explicit MaybeOwned() : isBorrowed_(true), borrow_() {}
  71. // Copying a borrow yields another borrow of the original, as with a
  72. // T*. Copying an owned T yields another owned T for safety: no
  73. // chains of borrowing by default! (Note you could get that behavior
  74. // with MaybeOwned<T>::borrowed(*rhs) if you wanted it.)
  75. MaybeOwned(const MaybeOwned& rhs) : isBorrowed_(rhs.isBorrowed_) {
  76. if (C10_LIKELY(rhs.isBorrowed_)) {
  77. MaybeOwnedTraits<T>::assignBorrow(borrow_, rhs.borrow_);
  78. } else {
  79. new (&own_) T(rhs.own_);
  80. }
  81. }
  82. MaybeOwned& operator=(const MaybeOwned& rhs) {
  83. if (this == &rhs) {
  84. return *this;
  85. }
  86. if (C10_UNLIKELY(!isBorrowed_)) {
  87. if (rhs.isBorrowed_) {
  88. own_.~T();
  89. MaybeOwnedTraits<T>::assignBorrow(borrow_, rhs.borrow_);
  90. isBorrowed_ = true;
  91. } else {
  92. own_ = rhs.own_;
  93. }
  94. } else {
  95. if (C10_LIKELY(rhs.isBorrowed_)) {
  96. MaybeOwnedTraits<T>::assignBorrow(borrow_, rhs.borrow_);
  97. } else {
  98. MaybeOwnedTraits<T>::destroyBorrow(borrow_);
  99. new (&own_) T(rhs.own_);
  100. isBorrowed_ = false;
  101. }
  102. }
  103. TORCH_INTERNAL_ASSERT_DEBUG_ONLY(isBorrowed_ == rhs.isBorrowed_);
  104. return *this;
  105. }
  106. MaybeOwned(MaybeOwned&& rhs) noexcept(
  107. std::is_nothrow_move_constructible<T>::value)
  108. : isBorrowed_(rhs.isBorrowed_) {
  109. if (C10_LIKELY(rhs.isBorrowed_)) {
  110. MaybeOwnedTraits<T>::assignBorrow(borrow_, rhs.borrow_);
  111. } else {
  112. new (&own_) T(std::move(rhs.own_));
  113. }
  114. }
  115. MaybeOwned& operator=(MaybeOwned&& rhs) noexcept(
  116. std::is_nothrow_move_assignable<T>::value) {
  117. if (this == &rhs) {
  118. return *this;
  119. }
  120. if (C10_UNLIKELY(!isBorrowed_)) {
  121. if (rhs.isBorrowed_) {
  122. own_.~T();
  123. MaybeOwnedTraits<T>::assignBorrow(borrow_, rhs.borrow_);
  124. isBorrowed_ = true;
  125. } else {
  126. own_ = std::move(rhs.own_);
  127. }
  128. } else {
  129. if (C10_LIKELY(rhs.isBorrowed_)) {
  130. MaybeOwnedTraits<T>::assignBorrow(borrow_, rhs.borrow_);
  131. } else {
  132. MaybeOwnedTraits<T>::destroyBorrow(borrow_);
  133. new (&own_) T(std::move(rhs.own_));
  134. isBorrowed_ = false;
  135. }
  136. }
  137. TORCH_INTERNAL_ASSERT_DEBUG_ONLY(isBorrowed_ == rhs.isBorrowed_);
  138. return *this;
  139. }
  140. static MaybeOwned borrowed(const T& t) {
  141. return MaybeOwned(t);
  142. }
  143. static MaybeOwned owned(T&& t) noexcept(
  144. std::is_nothrow_move_constructible<T>::value) {
  145. return MaybeOwned(std::move(t));
  146. }
  147. template <class... Args>
  148. static MaybeOwned owned(in_place_t, Args&&... args) {
  149. return MaybeOwned(in_place, std::forward<Args>(args)...);
  150. }
  151. ~MaybeOwned() {
  152. if (C10_UNLIKELY(!isBorrowed_)) {
  153. own_.~T();
  154. } else {
  155. MaybeOwnedTraits<T>::destroyBorrow(borrow_);
  156. }
  157. }
  158. // This is an implementation detail! You should know what you're doing
  159. // if you are testing this. If you just want to guarantee ownership move
  160. // this into a T
  161. bool unsafeIsBorrowed() const {
  162. return isBorrowed_;
  163. }
  164. const T& operator*() const& {
  165. if (isBorrowed_) {
  166. TORCH_INTERNAL_ASSERT_DEBUG_ONLY(
  167. MaybeOwnedTraits<T>::debugBorrowIsValid(borrow_));
  168. }
  169. return C10_LIKELY(isBorrowed_)
  170. ? MaybeOwnedTraits<T>::referenceFromBorrow(borrow_)
  171. : own_;
  172. }
  173. const T* operator->() const {
  174. if (isBorrowed_) {
  175. TORCH_INTERNAL_ASSERT_DEBUG_ONLY(
  176. MaybeOwnedTraits<T>::debugBorrowIsValid(borrow_));
  177. }
  178. return C10_LIKELY(isBorrowed_)
  179. ? MaybeOwnedTraits<T>::pointerFromBorrow(borrow_)
  180. : &own_;
  181. }
  182. // If borrowed, copy the underlying T. If owned, move from
  183. // it. borrowed/owned state remains the same, and either we
  184. // reference the same borrow as before or we are an owned moved-from
  185. // T.
  186. T operator*() && {
  187. if (isBorrowed_) {
  188. TORCH_INTERNAL_ASSERT_DEBUG_ONLY(
  189. MaybeOwnedTraits<T>::debugBorrowIsValid(borrow_));
  190. return MaybeOwnedTraits<T>::referenceFromBorrow(borrow_);
  191. } else {
  192. return std::move(own_);
  193. }
  194. }
  195. };
  196. } // namespace c10