cpp-common/bt2: move user component class bases to `component-class-dev.hpp`
[babeltrace.git] / src / cpp-common / bt2 / component-class-dev.hpp
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 */
This page took 0.045129 seconds and 4 git commands to generate.