Commit | Line | Data |
---|---|---|
212313d9 SM |
1 | /* |
2 | * Copyright (c) 2024 EfficiOS, Inc. | |
3 | * | |
4 | * SPDX-License-Identifier: MIT | |
5 | */ | |
6 | ||
7 | #ifndef BABELTRACE_CPP_COMMON_BT2_COMPONENT_CLASS_DEV_HPP | |
8 | #define BABELTRACE_CPP_COMMON_BT2_COMPONENT_CLASS_DEV_HPP | |
9 | ||
10 | #include <cstdint> | |
11 | ||
12 | #include <glib.h> | |
13 | ||
14 | #include "cpp-common/bt2c/c-string-view.hpp" | |
15 | #include "cpp-common/bt2c/logging.hpp" | |
16 | #include "cpp-common/vendor/fmt/core.h" | |
17 | ||
18 | #include "exc.hpp" | |
19 | #include "private-query-executor.hpp" | |
20 | #include "self-component-port.hpp" | |
21 | ||
22 | namespace bt2 { | |
23 | ||
24 | template <typename UserMessageIteratorT, typename UserComponentT> | |
25 | class UserMessageIterator; | |
26 | ||
27 | /* | |
28 | * Base class of any user component. | |
29 | * | |
30 | * See the specific `bt2::UserSourceComponent`, | |
31 | * `bt2::UserFilterComponent`, and `bt2::UserSinkComponent`. | |
32 | */ | |
33 | template <typename SelfCompT, typename InitDataT, typename QueryDataT> | |
34 | class UserComponent | |
35 | { | |
36 | /* Give a related message iterator access to this logger */ | |
37 | template <typename, typename> | |
38 | friend class UserMessageIterator; | |
39 | ||
40 | public: | |
41 | using InitData = InitDataT; | |
42 | using QueryData = QueryDataT; | |
43 | ||
44 | protected: | |
45 | explicit UserComponent(const SelfCompT selfComp, const std::string& logTag) : | |
46 | _mLogger {selfComp, fmt::format("{}/[{}]", logTag, selfComp.name())}, _mSelfComp {selfComp} | |
47 | { | |
48 | } | |
49 | ||
50 | protected: | |
51 | bt2c::CStringView _name() const noexcept | |
52 | { | |
53 | return _mSelfComp.name(); | |
54 | } | |
55 | ||
56 | LoggingLevel _loggingLevel() const noexcept | |
57 | { | |
58 | return _mSelfComp.loggingLevel(); | |
59 | } | |
60 | ||
61 | std::uint64_t _graphMipVersion() const noexcept | |
62 | { | |
63 | return _mSelfComp.graphMipVersion(); | |
64 | } | |
65 | ||
66 | SelfCompT _selfComp() noexcept | |
67 | { | |
68 | return _mSelfComp; | |
69 | } | |
70 | ||
71 | bt2c::Logger _mLogger; | |
72 | ||
73 | private: | |
74 | SelfCompT _mSelfComp; | |
75 | }; | |
76 | ||
77 | /* | |
78 | * Base class of a user source component `UserComponentT` (CRTP). | |
79 | * | |
80 | * UserComponentT::UserComponentT() must accept, in this order: | |
81 | * | |
82 | * 1. A `bt2::SelfSourceComponent` parameter, which it needs to forward | |
83 | * to bt2::UserSourceComponent::UserSourceComponent(). | |
84 | * | |
85 | * 2. A `bt2::ConstValue` parameter (the initialization parameters). | |
86 | * | |
87 | * 3. An `InitDataT *` parameter (the initialization method data). | |
88 | * | |
89 | * `UserMessageIteratorT`, the message iterator class to use, must inherit | |
90 | * `UserMessageIterator`. | |
91 | * | |
92 | * UserComponentT::_query() receives a query method data pointer of type | |
93 | * `QueryDataT *` as its last parameter. | |
94 | */ | |
95 | template <typename UserComponentT, typename UserMessageIteratorT, typename InitDataT = void, | |
96 | typename QueryDataT = void> | |
97 | class UserSourceComponent : public UserComponent<SelfSourceComponent, InitDataT, QueryDataT> | |
98 | { | |
99 | static_assert(std::is_base_of<UserMessageIterator<UserMessageIteratorT, UserComponentT>, | |
100 | UserMessageIteratorT>::value, | |
101 | "`UserMessageIteratorT` inherits `UserMessageIterator`"); | |
102 | ||
103 | public: | |
104 | using MessageIterator = UserMessageIteratorT; | |
105 | ||
106 | protected: | |
107 | using _OutputPorts = SelfSourceComponent::OutputPorts; | |
108 | ||
109 | explicit UserSourceComponent(const SelfSourceComponent selfComp, const std::string& logTag) : | |
110 | UserComponent<SelfSourceComponent, InitDataT, QueryDataT> {selfComp, logTag} | |
111 | { | |
112 | } | |
113 | ||
114 | public: | |
115 | static Value::Shared query(const SelfComponentClass selfCompCls, | |
116 | const PrivateQueryExecutor privQueryExec, | |
117 | const bt2c::CStringView obj, const ConstValue params, | |
118 | QueryDataT * const data) | |
119 | { | |
120 | return UserComponentT::_query(selfCompCls, privQueryExec, obj, params, data); | |
121 | } | |
122 | ||
123 | static void getSupportedMipVersions(const SelfComponentClass selfCompCls, | |
124 | const ConstValue params, const LoggingLevel loggingLevel, | |
125 | const UnsignedIntegerRangeSet ranges) | |
126 | { | |
127 | UserComponentT::_getSupportedMipVersions(selfCompCls, params, loggingLevel, ranges); | |
128 | } | |
129 | ||
130 | void outputPortConnected(const SelfComponentOutputPort outputPort, | |
131 | const ConstInputPort inputPort) | |
132 | { | |
133 | static_cast<UserComponentT&>(*this)._outputPortConnected(outputPort, inputPort); | |
134 | } | |
135 | ||
136 | protected: | |
137 | /* Overloadable */ | |
138 | static Value::Shared _query(SelfComponentClass, PrivateQueryExecutor, bt2c::CStringView, | |
139 | ConstValue, QueryDataT *) | |
140 | { | |
141 | throw UnknownObject {}; | |
142 | } | |
143 | ||
144 | /* Overloadable */ | |
145 | static void _getSupportedMipVersions(SelfComponentClass, ConstValue, LoggingLevel, | |
146 | const UnsignedIntegerRangeSet ranges) | |
147 | { | |
148 | ranges.addRange(0, 0); | |
149 | } | |
150 | ||
151 | /* Overloadable */ | |
152 | void _outputPortConnected(SelfComponentOutputPort, ConstInputPort) | |
153 | { | |
154 | } | |
155 | ||
156 | template <typename DataT> | |
157 | _OutputPorts::Port _addOutputPort(const bt2c::CStringView name, DataT& data) | |
158 | { | |
159 | return this->_selfComp().addOutputPort(name, data); | |
160 | } | |
161 | ||
162 | _OutputPorts::Port _addOutputPort(const bt2c::CStringView name) | |
163 | { | |
164 | return this->_selfComp().addOutputPort(name); | |
165 | } | |
166 | ||
167 | _OutputPorts _outputPorts() noexcept | |
168 | { | |
169 | return this->_selfComp().outputPorts(); | |
170 | } | |
171 | }; | |
172 | ||
173 | /* | |
174 | * Base class of a user filter component `UserComponentT` (CRTP). | |
175 | * | |
176 | * UserComponentT::UserComponentT() must accept, in this order: | |
177 | * | |
178 | * 1. A `bt2::SelfFilterComponent` parameter, which it needs to forward | |
179 | * to bt2::UserFilterComponent::UserFilterComponent(). | |
180 | * | |
181 | * 2. A `bt2::ConstValue` parameter (the initialization parameters). | |
182 | * | |
183 | * 3. An `InitDataT *` parameter (the initialization method data). | |
184 | * | |
185 | * `UserMessageIteratorT`, the message iterator class to use, must inherit | |
186 | * `UserMessageIterator`. | |
187 | * | |
188 | * UserComponentT::_query() receives a query method data pointer of type | |
189 | * `QueryDataT *` as its last parameter. | |
190 | */ | |
191 | template <typename UserComponentT, typename UserMessageIteratorT, typename InitDataT = void, | |
192 | typename QueryDataT = void> | |
193 | class UserFilterComponent : public UserComponent<SelfFilterComponent, InitDataT, QueryDataT> | |
194 | { | |
195 | static_assert(std::is_base_of<UserMessageIterator<UserMessageIteratorT, UserComponentT>, | |
196 | UserMessageIteratorT>::value, | |
197 | "`UserMessageIteratorT` inherits `UserMessageIterator`"); | |
198 | ||
199 | public: | |
200 | using MessageIterator = UserMessageIteratorT; | |
201 | ||
202 | protected: | |
203 | using _InputPorts = SelfFilterComponent::InputPorts; | |
204 | using _OutputPorts = SelfFilterComponent::OutputPorts; | |
205 | ||
206 | explicit UserFilterComponent(const SelfFilterComponent selfComp, const std::string& logTag) : | |
207 | UserComponent<SelfFilterComponent, InitDataT, QueryDataT> {selfComp, logTag} | |
208 | { | |
209 | } | |
210 | ||
211 | public: | |
212 | static Value::Shared query(const SelfComponentClass selfCompCls, | |
213 | const PrivateQueryExecutor privQueryExec, | |
214 | const bt2c::CStringView obj, const ConstValue params, | |
215 | QueryDataT * const data) | |
216 | { | |
217 | return UserComponentT::_query(selfCompCls, privQueryExec, obj, params, data); | |
218 | } | |
219 | ||
220 | static void getSupportedMipVersions(const SelfComponentClass selfCompCls, | |
221 | const ConstValue params, const LoggingLevel loggingLevel, | |
222 | const UnsignedIntegerRangeSet ranges) | |
223 | { | |
224 | UserComponentT::_getSupportedMipVersions(selfCompCls, params, loggingLevel, ranges); | |
225 | } | |
226 | ||
227 | void inputPortConnected(const SelfComponentInputPort inputPort, | |
228 | const ConstOutputPort outputPort) | |
229 | { | |
230 | static_cast<UserComponentT&>(*this)._inputPortConnected(inputPort, outputPort); | |
231 | } | |
232 | ||
233 | void outputPortConnected(const SelfComponentOutputPort outputPort, | |
234 | const ConstInputPort inputPort) | |
235 | { | |
236 | static_cast<UserComponentT&>(*this)._outputPortConnected(outputPort, inputPort); | |
237 | } | |
238 | ||
239 | protected: | |
240 | /* Overloadable */ | |
241 | static Value::Shared _query(SelfComponentClass, PrivateQueryExecutor, bt2c::CStringView, | |
242 | ConstValue, QueryDataT *) | |
243 | { | |
244 | throw UnknownObject {}; | |
245 | } | |
246 | ||
247 | /* Overloadable */ | |
248 | static void _getSupportedMipVersions(SelfComponentClass, ConstValue, LoggingLevel, | |
249 | const UnsignedIntegerRangeSet ranges) | |
250 | { | |
251 | ranges.addRange(0, 0); | |
252 | } | |
253 | ||
254 | /* Overloadable */ | |
255 | void _inputPortConnected(SelfComponentInputPort, ConstOutputPort) | |
256 | { | |
257 | } | |
258 | ||
259 | /* Overloadable */ | |
260 | void _outputPortConnected(SelfComponentOutputPort, ConstInputPort) | |
261 | { | |
262 | } | |
263 | ||
264 | template <typename DataT> | |
265 | _OutputPorts::Port _addInputPort(const bt2c::CStringView name, DataT& data) | |
266 | { | |
267 | return this->_selfComp().addInputPort(name, data); | |
268 | } | |
269 | ||
270 | _InputPorts::Port _addInputPort(const bt2c::CStringView name) | |
271 | { | |
272 | return this->_selfComp().addInputPort(name); | |
273 | } | |
274 | ||
275 | _InputPorts _inputPorts() noexcept | |
276 | { | |
277 | return this->_selfComp().inputPorts(); | |
278 | } | |
279 | ||
280 | template <typename DataT> | |
281 | _OutputPorts::Port _addOutputPort(const bt2c::CStringView name, DataT& data) | |
282 | { | |
283 | return this->_selfComp().addOutputPort(name, data); | |
284 | } | |
285 | ||
286 | _OutputPorts::Port _addOutputPort(const bt2c::CStringView name) | |
287 | { | |
288 | return this->_selfComp().addOutputPort(name); | |
289 | } | |
290 | ||
291 | _OutputPorts _outputPorts() noexcept | |
292 | { | |
293 | return this->_selfComp().outputPorts(); | |
294 | } | |
295 | }; | |
296 | ||
297 | /* | |
298 | * Base class of a user sink component `UserComponentT` (CRTP). | |
299 | * | |
300 | * UserComponentT::UserComponentT() must accept, in this order: | |
301 | * | |
302 | * 1. A `bt2::SelfSinkComponent` parameter, which it needs to forward | |
303 | * to bt2::UserSinkComponent::UserSinkComponent(). | |
304 | * | |
305 | * 2. A `bt2::ConstValue` parameter (the initialization parameters). | |
306 | * | |
307 | * 3. An `InitDataT *` parameter (the initialization method data). | |
308 | * | |
309 | * `UserComponentT` must implement: | |
310 | * | |
311 | * bool _consume(); | |
312 | * | |
313 | * This method returns `true` if the sink component still needs to | |
314 | * consume, or `false` if it's finished. | |
315 | * | |
316 | * UserComponentT::_query() receives a query method data pointer of type | |
317 | * `QueryDataT *` as its last parameter. | |
318 | ||
319 | */ | |
320 | template <typename UserComponentT, typename InitDataT = void, typename QueryDataT = void> | |
321 | class UserSinkComponent : public UserComponent<SelfSinkComponent, InitDataT, QueryDataT> | |
322 | { | |
323 | protected: | |
324 | using _InputPorts = SelfSinkComponent::InputPorts; | |
325 | ||
326 | explicit UserSinkComponent(const SelfSinkComponent selfComp, const std::string& logTag) : | |
327 | UserComponent<SelfSinkComponent, InitDataT, QueryDataT> {selfComp, logTag} | |
328 | { | |
329 | } | |
330 | ||
331 | public: | |
332 | static Value::Shared query(const SelfComponentClass selfCompCls, | |
333 | const PrivateQueryExecutor privQueryExec, | |
334 | const bt2c::CStringView obj, const ConstValue params, | |
335 | QueryDataT * const data) | |
336 | { | |
337 | return UserComponentT::_query(selfCompCls, privQueryExec, obj, params, data); | |
338 | } | |
339 | ||
340 | static void getSupportedMipVersions(const SelfComponentClass selfCompCls, | |
341 | const ConstValue params, const LoggingLevel loggingLevel, | |
342 | const UnsignedIntegerRangeSet ranges) | |
343 | { | |
344 | UserComponentT::_getSupportedMipVersions(selfCompCls, params, loggingLevel, ranges); | |
345 | } | |
346 | ||
347 | void graphIsConfigured() | |
348 | { | |
349 | static_cast<UserComponentT&>(*this)._graphIsConfigured(); | |
350 | } | |
351 | ||
352 | void inputPortConnected(const SelfComponentInputPort inputPort, | |
353 | const ConstOutputPort outputPort) | |
354 | { | |
355 | static_cast<UserComponentT&>(*this)._inputPortConnected(inputPort, outputPort); | |
356 | } | |
357 | ||
358 | bool consume() | |
359 | { | |
360 | return static_cast<UserComponentT&>(*this)._consume(); | |
361 | } | |
362 | ||
363 | protected: | |
364 | /* Overloadable */ | |
365 | static Value::Shared _query(SelfComponentClass, PrivateQueryExecutor, bt2c::CStringView, | |
366 | ConstValue, QueryDataT *) | |
367 | { | |
368 | throw UnknownObject {}; | |
369 | } | |
370 | ||
371 | /* Overloadable */ | |
372 | static void _getSupportedMipVersions(SelfComponentClass, ConstValue, LoggingLevel, | |
373 | const UnsignedIntegerRangeSet ranges) | |
374 | { | |
375 | ranges.addRange(0, 0); | |
376 | } | |
377 | ||
378 | /* Overloadable */ | |
379 | void _graphIsConfigured() | |
380 | { | |
381 | } | |
382 | ||
383 | /* Overloadable */ | |
384 | void _inputPortConnected(SelfComponentInputPort, ConstOutputPort) | |
385 | { | |
386 | } | |
387 | ||
388 | MessageIterator::Shared _createMessageIterator(const _InputPorts::Port port) | |
389 | { | |
390 | return this->_selfComp().createMessageIterator(port); | |
391 | } | |
392 | ||
393 | template <typename DataT> | |
394 | _InputPorts::Port _addInputPort(const bt2c::CStringView name, DataT& data) | |
395 | { | |
396 | return this->_selfComp().addInputPort(name, data); | |
397 | } | |
398 | ||
399 | _InputPorts::Port _addInputPort(const bt2c::CStringView name) | |
400 | { | |
401 | return this->_selfComp().addInputPort(name); | |
402 | } | |
403 | ||
404 | _InputPorts _inputPorts() noexcept | |
405 | { | |
406 | return this->_selfComp().inputPorts(); | |
407 | } | |
408 | }; | |
409 | ||
410 | /* | |
411 | * Base class of a user message iterator `UserMessageIteratorT` (CRTP) | |
412 | * of which the parent user component class is `UserComponentT`. | |
413 | * | |
414 | * `UserMessageIteratorT::UserMessageIteratorT()` must accept a | |
415 | * `bt2::SelfMessageIterator` parameter, which it needs to forward to | |
416 | * bt2::UserMessageIterator::UserMessageIterator(). | |
417 | * | |
418 | * The public next() method below (called by the bridge) implements the | |
419 | * very common pattern of appending messages into the output array, and, | |
420 | * meanwhile: | |
421 | * | |
422 | * If it catches a `bt2::TryAgain` exception: | |
423 | * If the message array isn't empty, transform this into a success | |
424 | * (don't throw). | |
425 | * | |
426 | * Otherwise rethrow. | |
427 | * | |
428 | * If it catches an error: | |
429 | * If the message array isn't empty, transform this into a success | |
430 | * (don't throw), but save the error of the current thread and the | |
431 | * type of error to throw the next time the user calls next(). | |
432 | * | |
433 | * Otherwise rethrow. | |
434 | * | |
435 | * `UserMessageIteratorT` must implement: | |
436 | * | |
437 | * void _next(bt2::ConstMessageArray& messages); | |
438 | * | |
439 | * This method fills `messages` with at most `messages.capacity()` | |
440 | * messages and may throw `bt2::TryAgain` or a valid error whenever. | |
441 | * Leaving an empty `messages` means the end of iteration. | |
442 | */ | |
443 | template <typename UserMessageIteratorT, typename UserComponentT> | |
444 | class UserMessageIterator | |
445 | { | |
446 | private: | |
447 | /* Type of `_mExcToThrowType` */ | |
448 | enum class _ExcToThrowType | |
449 | { | |
450 | NONE, | |
451 | ERROR, | |
452 | MEM_ERROR, | |
453 | }; | |
454 | ||
455 | protected: | |
456 | explicit UserMessageIterator(const SelfMessageIterator selfMsgIter, | |
457 | const std::string& logTagSuffix) : | |
458 | _mSelfMsgIter {selfMsgIter}, | |
459 | _mLogger {selfMsgIter, | |
460 | fmt::format("{}/{}", this->_component()._mLogger.tag(), logTagSuffix)} | |
461 | { | |
462 | } | |
463 | ||
464 | public: | |
465 | ~UserMessageIterator() | |
466 | { | |
467 | this->_resetError(); | |
468 | } | |
469 | ||
470 | void next(bt2::ConstMessageArray& messages) | |
471 | { | |
472 | /* Any saved error? Now is the time to throw */ | |
473 | if (G_UNLIKELY(_mExcToThrowType != _ExcToThrowType::NONE)) { | |
474 | /* Move `_mSavedLibError`, if any, as current thread error */ | |
475 | if (_mSavedLibError) { | |
476 | BT_CURRENT_THREAD_MOVE_ERROR_AND_RESET(_mSavedLibError); | |
477 | } | |
478 | ||
479 | /* Throw the corresponding exception */ | |
480 | if (_mExcToThrowType == _ExcToThrowType::ERROR) { | |
481 | throw bt2::Error {}; | |
482 | } else { | |
483 | BT_ASSERT(_mExcToThrowType == _ExcToThrowType::MEM_ERROR); | |
484 | throw bt2::MemoryError {}; | |
485 | } | |
486 | } | |
487 | ||
488 | /* | |
489 | * When catching some exception below, if our message array | |
490 | * isn't empty, then return immediately before throwing to | |
491 | * provide those messages to downstream. | |
492 | * | |
493 | * When catching an error, also save the current thread error, | |
494 | * if any, so that we can restore it later (see the beginning of | |
495 | * this method). | |
496 | */ | |
497 | BT_ASSERT_DBG(_mExcToThrowType == _ExcToThrowType::NONE); | |
498 | ||
499 | try { | |
500 | this->_userObj()._next(messages); | |
501 | ||
502 | /* We're done: everything below is exception handling */ | |
503 | return; | |
504 | } catch (const bt2::TryAgain&) { | |
505 | if (messages.isEmpty()) { | |
506 | throw; | |
507 | } | |
508 | } catch (const std::bad_alloc&) { | |
509 | if (messages.isEmpty()) { | |
510 | throw; | |
511 | } | |
512 | ||
513 | _mExcToThrowType = _ExcToThrowType::MEM_ERROR; | |
514 | } catch (const bt2::Error&) { | |
515 | if (messages.isEmpty()) { | |
516 | throw; | |
517 | } | |
518 | ||
519 | _mExcToThrowType = _ExcToThrowType::ERROR; | |
520 | } | |
521 | ||
522 | if (_mExcToThrowType != _ExcToThrowType::NONE) { | |
523 | BT_CPPLOGE( | |
524 | "An error occurred, but there are {} messages to return: delaying the error reporting.", | |
525 | messages.length()); | |
526 | BT_ASSERT(!_mSavedLibError); | |
527 | _mSavedLibError = bt_current_thread_take_error(); | |
528 | } | |
529 | } | |
530 | ||
531 | bool canSeekBeginning() | |
532 | { | |
533 | this->_resetError(); | |
534 | return this->_userObj()._canSeekBeginning(); | |
535 | } | |
536 | ||
537 | void seekBeginning() | |
538 | { | |
539 | this->_resetError(); | |
540 | return this->_userObj()._seekBeginning(); | |
541 | } | |
542 | ||
543 | bool canSeekNsFromOrigin(const std::int64_t nsFromOrigin) | |
544 | { | |
545 | this->_resetError(); | |
546 | return this->_userObj()._canSeekNsFromOrigin(nsFromOrigin); | |
547 | } | |
548 | ||
549 | void seekNsFromOrigin(const std::int64_t nsFromOrigin) | |
550 | { | |
551 | this->_resetError(); | |
552 | this->_userObj()._seekNsFromOrigin(nsFromOrigin); | |
553 | } | |
554 | ||
555 | protected: | |
556 | /* Overloadable */ | |
557 | bool _canSeekBeginning() noexcept | |
558 | { | |
559 | return false; | |
560 | } | |
561 | ||
562 | /* Overloadable */ | |
563 | void _seekBeginning() noexcept | |
564 | { | |
565 | } | |
566 | ||
567 | /* Overloadable */ | |
568 | bool _canSeekNsFromOrigin(std::int64_t) noexcept | |
569 | { | |
570 | return false; | |
571 | } | |
572 | ||
573 | /* Overloadable */ | |
574 | void _seekNsFromOrigin(std::int64_t) noexcept | |
575 | { | |
576 | } | |
577 | ||
578 | MessageIterator::Shared _createMessageIterator(const SelfComponentInputPort port) | |
579 | { | |
580 | return _mSelfMsgIter.createMessageIterator(port); | |
581 | } | |
582 | ||
583 | UserComponentT& _component() noexcept | |
584 | { | |
585 | return _mSelfMsgIter.component().template data<UserComponentT>(); | |
586 | } | |
587 | ||
588 | SelfComponentOutputPort _port() noexcept | |
589 | { | |
590 | return _mSelfMsgIter.port(); | |
591 | } | |
592 | ||
593 | bool _isInterrupted() const noexcept | |
594 | { | |
595 | return _mSelfMsgIter.isInterrupted(); | |
596 | } | |
597 | ||
598 | private: | |
599 | UserMessageIteratorT& _userObj() noexcept | |
600 | { | |
601 | return static_cast<UserMessageIteratorT&>(*this); | |
602 | } | |
603 | ||
604 | void _resetError() noexcept | |
605 | { | |
606 | _mExcToThrowType = _ExcToThrowType::NONE; | |
607 | ||
608 | if (_mSavedLibError) { | |
609 | bt_error_release(_mSavedLibError); | |
610 | } | |
611 | } | |
612 | ||
613 | SelfMessageIterator _mSelfMsgIter; | |
614 | ||
615 | /* | |
616 | * next() may accumulate messages, and then catch an error before | |
617 | * returning. In that case, it saves the error of the current thread | |
618 | * here so that it can return its accumulated messages and throw the | |
619 | * next time. | |
620 | * | |
621 | * It also saves the type of the exception to throw the next time. | |
622 | */ | |
623 | _ExcToThrowType _mExcToThrowType = _ExcToThrowType::NONE; | |
624 | const bt_error *_mSavedLibError = nullptr; | |
625 | ||
626 | protected: | |
627 | bt2c::Logger _mLogger; | |
628 | }; | |
629 | ||
630 | } /* namespace bt2 */ | |
631 | ||
632 | #endif /* BABELTRACE_CPP_COMMON_BT2_COMPONENT_CLASS_DEV_HPP */ |