2 * Copyright (c) 2024 EfficiOS, Inc.
4 * SPDX-License-Identifier: MIT
7 #ifndef BABELTRACE_CPP_COMMON_BT2_COMPONENT_CLASS_DEV_HPP
8 #define BABELTRACE_CPP_COMMON_BT2_COMPONENT_CLASS_DEV_HPP
14 #include "cpp-common/bt2c/c-string-view.hpp"
15 #include "cpp-common/bt2c/logging.hpp"
16 #include "cpp-common/vendor/fmt/core.h"
19 #include "private-query-executor.hpp"
20 #include "self-component-port.hpp"
24 template <typename UserMessageIteratorT, typename UserComponentT>
25 class UserMessageIterator;
28 * Base class of any user component.
30 * See the specific `bt2::UserSourceComponent`,
31 * `bt2::UserFilterComponent`, and `bt2::UserSinkComponent`.
33 template <typename SelfCompT, typename InitDataT, typename QueryDataT>
36 /* Give a related message iterator access to this logger */
37 template <typename, typename>
38 friend class UserMessageIterator;
41 using InitData = InitDataT;
42 using QueryData = QueryDataT;
45 explicit UserComponent(const SelfCompT selfComp, const std::string& logTag) :
46 _mLogger {selfComp, fmt::format("{}/[{}]", logTag, selfComp.name())}, _mSelfComp {selfComp}
51 bt2c::CStringView _name() const noexcept
53 return _mSelfComp.name();
56 LoggingLevel _loggingLevel() const noexcept
58 return _mSelfComp.loggingLevel();
61 std::uint64_t _graphMipVersion() const noexcept
63 return _mSelfComp.graphMipVersion();
66 SelfCompT _selfComp() noexcept
71 bt2c::Logger _mLogger;
78 * Base class of a user source component `UserComponentT` (CRTP).
80 * UserComponentT::UserComponentT() must accept, in this order:
82 * 1. A `bt2::SelfSourceComponent` parameter, which it needs to forward
83 * to bt2::UserSourceComponent::UserSourceComponent().
85 * 2. A `bt2::ConstValue` parameter (the initialization parameters).
87 * 3. An `InitDataT *` parameter (the initialization method data).
89 * `UserMessageIteratorT`, the message iterator class to use, must inherit
90 * `UserMessageIterator`.
92 * UserComponentT::_query() receives a query method data pointer of type
93 * `QueryDataT *` as its last parameter.
95 template <typename UserComponentT, typename UserMessageIteratorT, typename InitDataT = void,
96 typename QueryDataT = void>
97 class UserSourceComponent : public UserComponent<SelfSourceComponent, InitDataT, QueryDataT>
99 static_assert(std::is_base_of<UserMessageIterator<UserMessageIteratorT, UserComponentT>,
100 UserMessageIteratorT>::value,
101 "`UserMessageIteratorT` inherits `UserMessageIterator`");
104 using MessageIterator = UserMessageIteratorT;
107 using _OutputPorts = SelfSourceComponent::OutputPorts;
109 explicit UserSourceComponent(const SelfSourceComponent selfComp, const std::string& logTag) :
110 UserComponent<SelfSourceComponent, InitDataT, QueryDataT> {selfComp, logTag}
115 static Value::Shared query(const SelfComponentClass selfCompCls,
116 const PrivateQueryExecutor privQueryExec,
117 const bt2c::CStringView obj, const ConstValue params,
118 QueryDataT * const data)
120 return UserComponentT::_query(selfCompCls, privQueryExec, obj, params, data);
123 static void getSupportedMipVersions(const SelfComponentClass selfCompCls,
124 const ConstValue params, const LoggingLevel loggingLevel,
125 const UnsignedIntegerRangeSet ranges)
127 UserComponentT::_getSupportedMipVersions(selfCompCls, params, loggingLevel, ranges);
130 void outputPortConnected(const SelfComponentOutputPort outputPort,
131 const ConstInputPort inputPort)
133 static_cast<UserComponentT&>(*this)._outputPortConnected(outputPort, inputPort);
138 static Value::Shared _query(SelfComponentClass, PrivateQueryExecutor, bt2c::CStringView,
139 ConstValue, QueryDataT *)
141 throw UnknownObject {};
145 static void _getSupportedMipVersions(SelfComponentClass, ConstValue, LoggingLevel,
146 const UnsignedIntegerRangeSet ranges)
148 ranges.addRange(0, 0);
152 void _outputPortConnected(SelfComponentOutputPort, ConstInputPort)
156 template <typename DataT>
157 _OutputPorts::Port _addOutputPort(const bt2c::CStringView name, DataT& data)
159 return this->_selfComp().addOutputPort(name, data);
162 _OutputPorts::Port _addOutputPort(const bt2c::CStringView name)
164 return this->_selfComp().addOutputPort(name);
167 _OutputPorts _outputPorts() noexcept
169 return this->_selfComp().outputPorts();
174 * Base class of a user filter component `UserComponentT` (CRTP).
176 * UserComponentT::UserComponentT() must accept, in this order:
178 * 1. A `bt2::SelfFilterComponent` parameter, which it needs to forward
179 * to bt2::UserFilterComponent::UserFilterComponent().
181 * 2. A `bt2::ConstValue` parameter (the initialization parameters).
183 * 3. An `InitDataT *` parameter (the initialization method data).
185 * `UserMessageIteratorT`, the message iterator class to use, must inherit
186 * `UserMessageIterator`.
188 * UserComponentT::_query() receives a query method data pointer of type
189 * `QueryDataT *` as its last parameter.
191 template <typename UserComponentT, typename UserMessageIteratorT, typename InitDataT = void,
192 typename QueryDataT = void>
193 class UserFilterComponent : public UserComponent<SelfFilterComponent, InitDataT, QueryDataT>
195 static_assert(std::is_base_of<UserMessageIterator<UserMessageIteratorT, UserComponentT>,
196 UserMessageIteratorT>::value,
197 "`UserMessageIteratorT` inherits `UserMessageIterator`");
200 using MessageIterator = UserMessageIteratorT;
203 using _InputPorts = SelfFilterComponent::InputPorts;
204 using _OutputPorts = SelfFilterComponent::OutputPorts;
206 explicit UserFilterComponent(const SelfFilterComponent selfComp, const std::string& logTag) :
207 UserComponent<SelfFilterComponent, InitDataT, QueryDataT> {selfComp, logTag}
212 static Value::Shared query(const SelfComponentClass selfCompCls,
213 const PrivateQueryExecutor privQueryExec,
214 const bt2c::CStringView obj, const ConstValue params,
215 QueryDataT * const data)
217 return UserComponentT::_query(selfCompCls, privQueryExec, obj, params, data);
220 static void getSupportedMipVersions(const SelfComponentClass selfCompCls,
221 const ConstValue params, const LoggingLevel loggingLevel,
222 const UnsignedIntegerRangeSet ranges)
224 UserComponentT::_getSupportedMipVersions(selfCompCls, params, loggingLevel, ranges);
227 void inputPortConnected(const SelfComponentInputPort inputPort,
228 const ConstOutputPort outputPort)
230 static_cast<UserComponentT&>(*this)._inputPortConnected(inputPort, outputPort);
233 void outputPortConnected(const SelfComponentOutputPort outputPort,
234 const ConstInputPort inputPort)
236 static_cast<UserComponentT&>(*this)._outputPortConnected(outputPort, inputPort);
241 static Value::Shared _query(SelfComponentClass, PrivateQueryExecutor, bt2c::CStringView,
242 ConstValue, QueryDataT *)
244 throw UnknownObject {};
248 static void _getSupportedMipVersions(SelfComponentClass, ConstValue, LoggingLevel,
249 const UnsignedIntegerRangeSet ranges)
251 ranges.addRange(0, 0);
255 void _inputPortConnected(SelfComponentInputPort, ConstOutputPort)
260 void _outputPortConnected(SelfComponentOutputPort, ConstInputPort)
264 template <typename DataT>
265 _OutputPorts::Port _addInputPort(const bt2c::CStringView name, DataT& data)
267 return this->_selfComp().addInputPort(name, data);
270 _InputPorts::Port _addInputPort(const bt2c::CStringView name)
272 return this->_selfComp().addInputPort(name);
275 _InputPorts _inputPorts() noexcept
277 return this->_selfComp().inputPorts();
280 template <typename DataT>
281 _OutputPorts::Port _addOutputPort(const bt2c::CStringView name, DataT& data)
283 return this->_selfComp().addOutputPort(name, data);
286 _OutputPorts::Port _addOutputPort(const bt2c::CStringView name)
288 return this->_selfComp().addOutputPort(name);
291 _OutputPorts _outputPorts() noexcept
293 return this->_selfComp().outputPorts();
298 * Base class of a user sink component `UserComponentT` (CRTP).
300 * UserComponentT::UserComponentT() must accept, in this order:
302 * 1. A `bt2::SelfSinkComponent` parameter, which it needs to forward
303 * to bt2::UserSinkComponent::UserSinkComponent().
305 * 2. A `bt2::ConstValue` parameter (the initialization parameters).
307 * 3. An `InitDataT *` parameter (the initialization method data).
309 * `UserComponentT` must implement:
313 * This method returns `true` if the sink component still needs to
314 * consume, or `false` if it's finished.
316 * UserComponentT::_query() receives a query method data pointer of type
317 * `QueryDataT *` as its last parameter.
320 template <typename UserComponentT, typename InitDataT = void, typename QueryDataT = void>
321 class UserSinkComponent : public UserComponent<SelfSinkComponent, InitDataT, QueryDataT>
324 using _InputPorts = SelfSinkComponent::InputPorts;
326 explicit UserSinkComponent(const SelfSinkComponent selfComp, const std::string& logTag) :
327 UserComponent<SelfSinkComponent, InitDataT, QueryDataT> {selfComp, logTag}
332 static Value::Shared query(const SelfComponentClass selfCompCls,
333 const PrivateQueryExecutor privQueryExec,
334 const bt2c::CStringView obj, const ConstValue params,
335 QueryDataT * const data)
337 return UserComponentT::_query(selfCompCls, privQueryExec, obj, params, data);
340 static void getSupportedMipVersions(const SelfComponentClass selfCompCls,
341 const ConstValue params, const LoggingLevel loggingLevel,
342 const UnsignedIntegerRangeSet ranges)
344 UserComponentT::_getSupportedMipVersions(selfCompCls, params, loggingLevel, ranges);
347 void graphIsConfigured()
349 static_cast<UserComponentT&>(*this)._graphIsConfigured();
352 void inputPortConnected(const SelfComponentInputPort inputPort,
353 const ConstOutputPort outputPort)
355 static_cast<UserComponentT&>(*this)._inputPortConnected(inputPort, outputPort);
360 return static_cast<UserComponentT&>(*this)._consume();
365 static Value::Shared _query(SelfComponentClass, PrivateQueryExecutor, bt2c::CStringView,
366 ConstValue, QueryDataT *)
368 throw UnknownObject {};
372 static void _getSupportedMipVersions(SelfComponentClass, ConstValue, LoggingLevel,
373 const UnsignedIntegerRangeSet ranges)
375 ranges.addRange(0, 0);
379 void _graphIsConfigured()
384 void _inputPortConnected(SelfComponentInputPort, ConstOutputPort)
388 MessageIterator::Shared _createMessageIterator(const _InputPorts::Port port)
390 return this->_selfComp().createMessageIterator(port);
393 template <typename DataT>
394 _InputPorts::Port _addInputPort(const bt2c::CStringView name, DataT& data)
396 return this->_selfComp().addInputPort(name, data);
399 _InputPorts::Port _addInputPort(const bt2c::CStringView name)
401 return this->_selfComp().addInputPort(name);
404 _InputPorts _inputPorts() noexcept
406 return this->_selfComp().inputPorts();
411 * Base class of a user message iterator `UserMessageIteratorT` (CRTP)
412 * of which the parent user component class is `UserComponentT`.
414 * `UserMessageIteratorT::UserMessageIteratorT()` must accept a
415 * `bt2::SelfMessageIterator` parameter, which it needs to forward to
416 * bt2::UserMessageIterator::UserMessageIterator().
418 * The public next() method below (called by the bridge) implements the
419 * very common pattern of appending messages into the output array, and,
422 * If it catches a `bt2::TryAgain` exception:
423 * If the message array isn't empty, transform this into a success
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().
435 * `UserMessageIteratorT` must implement:
437 * void _next(bt2::ConstMessageArray& messages);
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.
443 template <typename UserMessageIteratorT, typename UserComponentT>
444 class UserMessageIterator
447 /* Type of `_mExcToThrowType` */
448 enum class _ExcToThrowType
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)}
465 ~UserMessageIterator()
470 void next(bt2::ConstMessageArray& messages)
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);
479 /* Throw the corresponding exception */
480 if (_mExcToThrowType == _ExcToThrowType::ERROR) {
483 BT_ASSERT(_mExcToThrowType == _ExcToThrowType::MEM_ERROR);
484 throw bt2::MemoryError {};
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.
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
497 BT_ASSERT_DBG(_mExcToThrowType == _ExcToThrowType::NONE);
500 this->_userObj()._next(messages);
502 /* We're done: everything below is exception handling */
504 } catch (const bt2::TryAgain&) {
505 if (messages.isEmpty()) {
508 } catch (const std::bad_alloc&) {
509 if (messages.isEmpty()) {
513 _mExcToThrowType = _ExcToThrowType::MEM_ERROR;
514 } catch (const bt2::Error&) {
515 if (messages.isEmpty()) {
519 _mExcToThrowType = _ExcToThrowType::ERROR;
522 if (_mExcToThrowType != _ExcToThrowType::NONE) {
524 "An error occurred, but there are {} messages to return: delaying the error reporting.",
526 BT_ASSERT(!_mSavedLibError);
527 _mSavedLibError = bt_current_thread_take_error();
531 bool canSeekBeginning()
534 return this->_userObj()._canSeekBeginning();
540 return this->_userObj()._seekBeginning();
543 bool canSeekNsFromOrigin(const std::int64_t nsFromOrigin)
546 return this->_userObj()._canSeekNsFromOrigin(nsFromOrigin);
549 void seekNsFromOrigin(const std::int64_t nsFromOrigin)
552 this->_userObj()._seekNsFromOrigin(nsFromOrigin);
557 bool _canSeekBeginning() noexcept
563 void _seekBeginning() noexcept
568 bool _canSeekNsFromOrigin(std::int64_t) noexcept
574 void _seekNsFromOrigin(std::int64_t) noexcept
578 MessageIterator::Shared _createMessageIterator(const SelfComponentInputPort port)
580 return _mSelfMsgIter.createMessageIterator(port);
583 UserComponentT& _component() noexcept
585 return _mSelfMsgIter.component().template data<UserComponentT>();
588 SelfComponentOutputPort _port() noexcept
590 return _mSelfMsgIter.port();
593 bool _isInterrupted() const noexcept
595 return _mSelfMsgIter.isInterrupted();
599 UserMessageIteratorT& _userObj() noexcept
601 return static_cast<UserMessageIteratorT&>(*this);
604 void _resetError() noexcept
606 _mExcToThrowType = _ExcToThrowType::NONE;
608 if (_mSavedLibError) {
609 bt_error_release(_mSavedLibError);
613 SelfMessageIterator _mSelfMsgIter;
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
621 * It also saves the type of the exception to throw the next time.
623 _ExcToThrowType _mExcToThrowType = _ExcToThrowType::NONE;
624 const bt_error *_mSavedLibError = nullptr;
627 bt2c::Logger _mLogger;
630 } /* namespace bt2 */
632 #endif /* BABELTRACE_CPP_COMMON_BT2_COMPONENT_CLASS_DEV_HPP */