• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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(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(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     // Note that we copy instead of moving the status here so that
160     // status.~StatusOrData() can call ok() without invoking UB.
161     status_ = status;
162     EnsureNotOk();
163   }
164 
ok()165   bool ok() const { return status_.ok(); }
166 
167  protected:
168   // status_ will always be active after the constructor.
169   // We make it a union to be able to initialize exactly how we need without
170   // waste.
171   // Eg. in the copy constructor we use the default constructor of Status in
172   // the ok() path to avoid an extra Ref call.
173   union {
174     Status status_;
175   };
176 
177   // data_ is active iff status_.ok()==true
178   struct Dummy {};
179   union {
180     // When T is const, we need some non-const object we can cast to void* for
181     // the placement new. dummy_ is that object.
182     Dummy dummy_;
183     T data_;
184   };
185 
Clear()186   void Clear() {
187     if (ok()) data_.~T();
188   }
189 
EnsureOk()190   void EnsureOk() const {
191     if (!ok()) Helper::Crash(status_);
192   }
193 
EnsureNotOk()194   void EnsureNotOk() {
195     if (ok()) Helper::HandleInvalidStatusCtorArg(&status_);
196   }
197 
198   // Construct the value (ie. data_) through placement new with the passed
199   // argument.
200   template <typename Arg>
MakeValue(Arg && arg)201   void MakeValue(Arg&& arg) {
202     internal_statusor::PlacementNew<T>(&dummy_, std::forward<Arg>(arg));
203   }
204 
205   // Construct the status (ie. status_) through placement new with the passed
206   // argument.
207   template <typename... Args>
MakeStatus(Args &&...args)208   void MakeStatus(Args&&... args) {
209     internal_statusor::PlacementNew<Status>(&status_,
210                                             std::forward<Args>(args)...);
211   }
212 };
213 
214 // Helper base class to allow implicitly deleted constructors and assignment
215 // operations in StatusOr.
216 // TraitsBase will explicitly delete what it can't support and StatusOr will
217 // inherit that behavior implicitly.
218 template <bool Copy, bool Move>
219 struct TraitsBase {
220   TraitsBase() = default;
221   TraitsBase(const TraitsBase&) = default;
222   TraitsBase(TraitsBase&&) = default;
223   TraitsBase& operator=(const TraitsBase&) = default;
224   TraitsBase& operator=(TraitsBase&&) = default;
225 };
226 
227 template <>
228 struct TraitsBase<false, true> {
229   TraitsBase() = default;
230   TraitsBase(const TraitsBase&) = delete;
231   TraitsBase(TraitsBase&&) = default;
232   TraitsBase& operator=(const TraitsBase&) = delete;
233   TraitsBase& operator=(TraitsBase&&) = default;
234 };
235 
236 template <>
237 struct TraitsBase<false, false> {
238   TraitsBase() = default;
239   TraitsBase(const TraitsBase&) = delete;
240   TraitsBase(TraitsBase&&) = delete;
241   TraitsBase& operator=(const TraitsBase&) = delete;
242   TraitsBase& operator=(TraitsBase&&) = delete;
243 };
244 
245 }  // namespace internal_statusor
246 }  // namespace port
247 }  // namespace stream_executor
248 
249 #endif  // TENSORFLOW_STREAM_EXECUTOR_LIB_STATUSOR_INTERNALS_H_
250