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