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
96 template <typename... Args>
StatusOrData(absl::in_place_t,Args &&...args)97 explicit StatusOrData(absl::in_place_t, Args&&... args)
98 : data_(std::forward<Args>(args)...) {
99 MakeStatus();
100 }
101
StatusOrData(const Status & status)102 explicit StatusOrData(const Status& status) : status_(status) {
103 EnsureNotOk();
104 }
StatusOrData(Status && status)105 explicit StatusOrData(Status&& status) : status_(std::move(status)) {
106 EnsureNotOk();
107 }
108
109 StatusOrData& operator=(const StatusOrData& other) {
110 if (this == &other) return *this;
111 if (other.ok())
112 Assign(other.data_);
113 else
114 Assign(other.status_);
115 return *this;
116 }
117
118 StatusOrData& operator=(StatusOrData&& other) {
119 if (this == &other) return *this;
120 if (other.ok())
121 Assign(std::move(other.data_));
122 else
123 Assign(std::move(other.status_));
124 return *this;
125 }
126
~StatusOrData()127 ~StatusOrData() {
128 if (ok()) {
129 status_.~Status();
130 data_.~T();
131 } else {
132 status_.~Status();
133 }
134 }
135
Assign(const T & value)136 void Assign(const T& value) {
137 if (ok()) {
138 data_.~T();
139 MakeValue(value);
140 } else {
141 MakeValue(value);
142 status_ = OkStatus();
143 }
144 }
145
Assign(T && value)146 void Assign(T&& value) {
147 if (ok()) {
148 data_.~T();
149 MakeValue(std::move(value));
150 } else {
151 MakeValue(std::move(value));
152 status_ = OkStatus();
153 }
154 }
155
Assign(const Status & status)156 void Assign(const Status& status) {
157 Clear();
158 status_ = status;
159 EnsureNotOk();
160 }
161
Assign(Status && status)162 void Assign(Status&& status) {
163 Clear();
164 // Note that we copy instead of moving the status here so that
165 // status.~StatusOrData() can call ok() without invoking UB.
166 status_ = status;
167 EnsureNotOk();
168 }
169
ok()170 bool ok() const { return status_.ok(); }
171
172 protected:
173 // status_ will always be active after the constructor.
174 // We make it a union to be able to initialize exactly how we need without
175 // waste.
176 // Eg. in the copy constructor we use the default constructor of Status in
177 // the ok() path to avoid an extra Ref call.
178 union {
179 Status status_;
180 };
181
182 // data_ is active iff status_.ok()==true
183 struct Dummy {};
184 union {
185 // When T is const, we need some non-const object we can cast to void* for
186 // the placement new. dummy_ is that object.
187 Dummy dummy_;
188 T data_;
189 };
190
Clear()191 void Clear() {
192 if (ok()) data_.~T();
193 }
194
EnsureOk()195 void EnsureOk() const {
196 if (!ok()) Helper::Crash(status_);
197 }
198
EnsureNotOk()199 void EnsureNotOk() {
200 if (ok()) Helper::HandleInvalidStatusCtorArg(&status_);
201 }
202
203 // Construct the value (ie. data_) through placement new with the passed
204 // argument.
205 template <typename Arg>
MakeValue(Arg && arg)206 void MakeValue(Arg&& arg) {
207 internal_statusor::PlacementNew<T>(&dummy_, std::forward<Arg>(arg));
208 }
209
210 // Construct the status (ie. status_) through placement new with the passed
211 // argument.
212 template <typename... Args>
MakeStatus(Args &&...args)213 void MakeStatus(Args&&... args) {
214 internal_statusor::PlacementNew<Status>(&status_,
215 std::forward<Args>(args)...);
216 }
217 };
218
219 // Helper base class to allow implicitly deleted constructors and assignment
220 // operations in StatusOr.
221 // TraitsBase will explicitly delete what it can't support and StatusOr will
222 // inherit that behavior implicitly.
223 template <bool Copy, bool Move>
224 struct TraitsBase {
225 TraitsBase() = default;
226 TraitsBase(const TraitsBase&) = default;
227 TraitsBase(TraitsBase&&) = default;
228 TraitsBase& operator=(const TraitsBase&) = default;
229 TraitsBase& operator=(TraitsBase&&) = default;
230 };
231
232 template <>
233 struct TraitsBase<false, true> {
234 TraitsBase() = default;
235 TraitsBase(const TraitsBase&) = delete;
236 TraitsBase(TraitsBase&&) = default;
237 TraitsBase& operator=(const TraitsBase&) = delete;
238 TraitsBase& operator=(TraitsBase&&) = default;
239 };
240
241 template <>
242 struct TraitsBase<false, false> {
243 TraitsBase() = default;
244 TraitsBase(const TraitsBase&) = delete;
245 TraitsBase(TraitsBase&&) = delete;
246 TraitsBase& operator=(const TraitsBase&) = delete;
247 TraitsBase& operator=(TraitsBase&&) = delete;
248 };
249
250 } // namespace internal_statusor
251 } // namespace tensorflow
252
253 #endif // TENSORFLOW_CORE_PLATFORM_STATUSOR_INTERNALS_H_
254