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_TSL_PLATFORM_STATUSOR_INTERNALS_H_ |
17 | #define TENSORFLOW_TSL_PLATFORM_STATUSOR_INTERNALS_H_ |
18 | |
19 | #include "tensorflow/tsl/platform/macros.h" |
20 | #include "tensorflow/tsl/platform/status.h" |
21 | |
22 | namespace tsl { |
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> |
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 | |
55 | StatusOrData(const StatusOrData& other) { |
56 | if (other.ok()) { |
57 | MakeValue(other.data_); |
58 | MakeStatus(); |
59 | } else { |
60 | MakeStatus(other.status_); |
61 | } |
62 | } |
63 | |
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> |
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> |
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 | |
93 | explicit StatusOrData(const T& value) : data_(value) { MakeStatus(); } |
94 | explicit StatusOrData(T&& value) : data_(std::move(value)) { MakeStatus(); } |
95 | |
96 | template <typename... Args> |
97 | explicit StatusOrData(absl::in_place_t, Args&&... args) |
98 | : data_(std::forward<Args>(args)...) { |
99 | MakeStatus(); |
100 | } |
101 | |
102 | explicit StatusOrData(const Status& status) : status_(status) { |
103 | EnsureNotOk(); |
104 | } |
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 | |
127 | ~StatusOrData() { |
128 | if (ok()) { |
129 | status_.~Status(); |
130 | data_.~T(); |
131 | } else { |
132 | status_.~Status(); |
133 | } |
134 | } |
135 | |
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 | |
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 | |
156 | void Assign(const Status& status) { |
157 | Clear(); |
158 | status_ = status; |
159 | EnsureNotOk(); |
160 | } |
161 | |
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 | |
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 | |
191 | void Clear() { |
192 | if (ok()) data_.~T(); |
193 | } |
194 | |
195 | void EnsureOk() const { |
196 | if (!ok()) Helper::Crash(status_); |
197 | } |
198 | |
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> |
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> |
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 tsl |
252 | |
253 | #endif // TENSORFLOW_TSL_PLATFORM_STATUSOR_INTERNALS_H_ |
254 | |