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