1 /* Copyright 2017 The TensorFlow Authors. All Rights Reserved.
2 
3 Licensed under the Apache License, Version 2.0 (the "License");
4 you may not use this file except in compliance with the License.
5 You may obtain a copy of the License at
6 
7     http://www.apache.org/licenses/LICENSE-2.0
8 
9 Unless required by applicable law or agreed to in writing, software
10 distributed under the License is distributed on an "AS IS" BASIS,
11 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 See the License for the specific language governing permissions and
13 limitations under the License.
14 ==============================================================================*/
15 
16 #ifndef TENSORFLOW_STREAM_EXECUTOR_LIB_STATUSOR_INTERNALS_H_
17 #define TENSORFLOW_STREAM_EXECUTOR_LIB_STATUSOR_INTERNALS_H_
18 
19 #include "tensorflow/core/platform/macros.h"
20 #include "tensorflow/stream_executor/lib/status.h"
21 
22 namespace stream_executor {
23 namespace port {
24 namespace internal_statusor {
25 
26 class Helper {
27  public:
28   // Move type-agnostic error handling to the .cc.
29   static void HandleInvalidStatusCtorArg(Status*);
30   TF_ATTRIBUTE_NORETURN static void Crash(const Status& status);
31 };
32 
33 // Construct an instance of T in `p` through placement new, passing Args... to
34 // the constructor.
35 // This abstraction is here mostly for the gcc performance fix.
36 template <typename T, typename... Args>
PlacementNew(void * p,Args &&...args)37 void PlacementNew(void* p, Args&&... args) {
38 #if defined(__GNUC__) && !defined(__clang__)
39   // Teach gcc that 'p' cannot be null, fixing code size issues.
40   if (p == nullptr) __builtin_unreachable();
41 #endif
42   new (p) T(std::forward<Args>(args)...);
43 }
44 
45 // Helper base class to hold the data and all operations.
46 // We move all this to a base class to allow mixing with the appropriate
47 // TraitsBase specialization.
48 template <typename T>
49 class StatusOrData {
50   template <typename U>
51   friend class StatusOrData;
52 
53  public:
54   StatusOrData() = delete;
55 
StatusOrData(const StatusOrData & other)56   StatusOrData(const StatusOrData& other) {
57     if (other.ok()) {
58       MakeValue(other.data_);
59       MakeStatus();
60     } else {
61       MakeStatus(other.status_);
62     }
63   }
64 
StatusOrData(StatusOrData && other)65   StatusOrData(StatusOrData&& other) noexcept {
66     if (other.ok()) {
67       MakeValue(std::move(other.data_));
68       MakeStatus();
69     } else {
70       MakeStatus(std::move(other.status_));
71     }
72   }
73 
74   template <typename U>
StatusOrData(const StatusOrData<U> & other)75   StatusOrData(const StatusOrData<U>& other) {
76     if (other.ok()) {
77       MakeValue(other.data_);
78       MakeStatus();
79     } else {
80       MakeStatus(other.status_);
81     }
82   }
83 
84   template <typename U>
StatusOrData(StatusOrData<U> && other)85   StatusOrData(StatusOrData<U>&& other) {
86     if (other.ok()) {
87       MakeValue(std::move(other.data_));
88       MakeStatus();
89     } else {
90       MakeStatus(std::move(other.status_));
91     }
92   }
93 
StatusOrData(const T & value)94   explicit StatusOrData(const T& value) : data_(value) { MakeStatus(); }
StatusOrData(T && value)95   explicit StatusOrData(T&& value) : data_(std::move(value)) { MakeStatus(); }
96 
StatusOrData(const Status & status)97   explicit StatusOrData(const Status& status) : status_(status) {
98     EnsureNotOk();
99   }
StatusOrData(Status && status)100   explicit StatusOrData(Status&& status) : status_(std::move(status)) {
101     EnsureNotOk();
102   }
103 
104   StatusOrData& operator=(const StatusOrData& other) {
105     if (this == &other) return *this;
106     if (other.ok())
107       Assign(other.data_);
108     else
109       Assign(other.status_);
110     return *this;
111   }
112 
113   StatusOrData& operator=(StatusOrData&& other) {
114     if (this == &other) return *this;
115     if (other.ok())
116       Assign(std::move(other.data_));
117     else
118       Assign(std::move(other.status_));
119     return *this;
120   }
121 
~StatusOrData()122   ~StatusOrData() {
123     if (ok()) {
124       status_.~Status();
125       data_.~T();
126     } else {
127       status_.~Status();
128     }
129   }
130 
Assign(const T & value)131   void Assign(const T& value) {
132     if (ok()) {
133       data_.~T();
134       MakeValue(value);
135     } else {
136       MakeValue(value);
137       status_ = Status::OK();
138     }
139   }
140 
Assign(T && value)141   void Assign(T&& value) {
142     if (ok()) {
143       data_.~T();
144       MakeValue(std::move(value));
145     } else {
146       MakeValue(std::move(value));
147       status_ = Status::OK();
148     }
149   }
150 
Assign(const Status & status)151   void Assign(const Status& status) {
152     Clear();
153     status_ = status;
154     EnsureNotOk();
155   }
156 
Assign(Status && status)157   void Assign(Status&& status) {
158     Clear();
159     status_ = std::move(status);
160     EnsureNotOk();
161   }
162 
ok()163   bool ok() const { return status_.ok(); }
164 
165  protected:
166   // status_ will always be active after the constructor.
167   // We make it a union to be able to initialize exactly how we need without
168   // waste.
169   // Eg. in the copy constructor we use the default constructor of Status in
170   // the ok() path to avoid an extra Ref call.
171   union {
172     Status status_;
173   };
174 
175   // data_ is active iff status_.ok()==true
176   struct Dummy {};
177   union {
178     // When T is const, we need some non-const object we can cast to void* for
179     // the placement new. dummy_ is that object.
180     Dummy dummy_;
181     T data_;
182   };
183 
Clear()184   void Clear() {
185     if (ok()) data_.~T();
186   }
187 
EnsureOk()188   void EnsureOk() const {
189     if (!ok()) Helper::Crash(status_);
190   }
191 
EnsureNotOk()192   void EnsureNotOk() {
193     if (ok()) Helper::HandleInvalidStatusCtorArg(&status_);
194   }
195 
196   // Construct the value (ie. data_) through placement new with the passed
197   // argument.
198   template <typename Arg>
MakeValue(Arg && arg)199   void MakeValue(Arg&& arg) {
200     internal_statusor::PlacementNew<T>(&dummy_, std::forward<Arg>(arg));
201   }
202 
203   // Construct the status (ie. status_) through placement new with the passed
204   // argument.
205   template <typename... Args>
MakeStatus(Args &&...args)206   void MakeStatus(Args&&... args) {
207     internal_statusor::PlacementNew<Status>(&status_,
208                                             std::forward<Args>(args)...);
209   }
210 };
211 
212 // Helper base class to allow implicitly deleted constructors and assignment
213 // operations in StatusOr.
214 // TraitsBase will explicitly delete what it can't support and StatusOr will
215 // inherit that behavior implicitly.
216 template <bool Copy, bool Move>
217 struct TraitsBase {
218   TraitsBase() = default;
219   TraitsBase(const TraitsBase&) = default;
220   TraitsBase(TraitsBase&&) = default;
221   TraitsBase& operator=(const TraitsBase&) = default;
222   TraitsBase& operator=(TraitsBase&&) = default;
223 };
224 
225 template <>
226 struct TraitsBase<false, true> {
227   TraitsBase() = default;
228   TraitsBase(const TraitsBase&) = delete;
229   TraitsBase(TraitsBase&&) = default;
230   TraitsBase& operator=(const TraitsBase&) = delete;
231   TraitsBase& operator=(TraitsBase&&) = default;
232 };
233 
234 template <>
235 struct TraitsBase<false, false> {
236   TraitsBase() = default;
237   TraitsBase(const TraitsBase&) = delete;
238   TraitsBase(TraitsBase&&) = delete;
239   TraitsBase& operator=(const TraitsBase&) = delete;
240   TraitsBase& operator=(TraitsBase&&) = delete;
241 };
242 
243 }  // namespace internal_statusor
244 }  // namespace port
245 }  // namespace stream_executor
246 
247 #endif  // TENSORFLOW_STREAM_EXECUTOR_LIB_STATUSOR_INTERNALS_H_
248