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