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(std::move(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(std::move(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 status_ = std::move(status);
160 EnsureNotOk();
161 }
162
ok()163 bool ok() const { return status_.ok(); }
164
165 protected:
166 // status_ will always be active after the constructor.
167 // We make it a union to be able to initialize exactly how we need without
168 // waste.
169 // Eg. in the copy constructor we use the default constructor of Status in
170 // the ok() path to avoid an extra Ref call.
171 union {
172 Status status_;
173 };
174
175 // data_ is active iff status_.ok()==true
176 struct Dummy {};
177 union {
178 // When T is const, we need some non-const object we can cast to void* for
179 // the placement new. dummy_ is that object.
180 Dummy dummy_;
181 T data_;
182 };
183
Clear()184 void Clear() {
185 if (ok()) data_.~T();
186 }
187
EnsureOk()188 void EnsureOk() const {
189 if (!ok()) Helper::Crash(status_);
190 }
191
EnsureNotOk()192 void EnsureNotOk() {
193 if (ok()) Helper::HandleInvalidStatusCtorArg(&status_);
194 }
195
196 // Construct the value (ie. data_) through placement new with the passed
197 // argument.
198 template <typename Arg>
MakeValue(Arg && arg)199 void MakeValue(Arg&& arg) {
200 internal_statusor::PlacementNew<T>(&dummy_, std::forward<Arg>(arg));
201 }
202
203 // Construct the status (ie. status_) through placement new with the passed
204 // argument.
205 template <typename... Args>
MakeStatus(Args &&...args)206 void MakeStatus(Args&&... args) {
207 internal_statusor::PlacementNew<Status>(&status_,
208 std::forward<Args>(args)...);
209 }
210 };
211
212 // Helper base class to allow implicitly deleted constructors and assignment
213 // operations in StatusOr.
214 // TraitsBase will explicitly delete what it can't support and StatusOr will
215 // inherit that behavior implicitly.
216 template <bool Copy, bool Move>
217 struct TraitsBase {
218 TraitsBase() = default;
219 TraitsBase(const TraitsBase&) = default;
220 TraitsBase(TraitsBase&&) = default;
221 TraitsBase& operator=(const TraitsBase&) = default;
222 TraitsBase& operator=(TraitsBase&&) = default;
223 };
224
225 template <>
226 struct TraitsBase<false, true> {
227 TraitsBase() = default;
228 TraitsBase(const TraitsBase&) = delete;
229 TraitsBase(TraitsBase&&) = default;
230 TraitsBase& operator=(const TraitsBase&) = delete;
231 TraitsBase& operator=(TraitsBase&&) = default;
232 };
233
234 template <>
235 struct TraitsBase<false, false> {
236 TraitsBase() = default;
237 TraitsBase(const TraitsBase&) = delete;
238 TraitsBase(TraitsBase&&) = delete;
239 TraitsBase& operator=(const TraitsBase&) = delete;
240 TraitsBase& operator=(TraitsBase&&) = delete;
241 };
242
243 } // namespace internal_statusor
244 } // namespace port
245 } // namespace stream_executor
246
247 #endif // TENSORFLOW_STREAM_EXECUTOR_LIB_STATUSOR_INTERNALS_H_
248