Do not use `bool` type; use new `bt_bool` instead
[babeltrace.git] / lib / graph / iterator.c
CommitLineData
47e5a032
JG
1/*
2 * iterator.c
3 *
4 * Babeltrace Notification Iterator
5 *
6 * Copyright 2015 Jérémie Galarneau <jeremie.galarneau@efficios.com>
3230ee6b 7 * Copyright 2017 Philippe Proulx <pproulx@efficios.com>
47e5a032
JG
8 *
9 * Permission is hereby granted, free of charge, to any person obtaining a copy
10 * of this software and associated documentation files (the "Software"), to deal
11 * in the Software without restriction, including without limitation the rights
12 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 * copies of the Software, and to permit persons to whom the Software is
14 * furnished to do so, subject to the following conditions:
15 *
16 * The above copyright notice and this permission notice shall be included in
17 * all copies or substantial portions of the Software.
18 *
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25 * SOFTWARE.
26 */
27
3d9990ac 28#include <babeltrace/compiler-internal.h>
b8a06801 29#include <babeltrace/ref.h>
3230ee6b
PP
30#include <babeltrace/ctf-ir/event-internal.h>
31#include <babeltrace/ctf-ir/packet-internal.h>
32#include <babeltrace/ctf-ir/stream-internal.h>
b2e0c907
PP
33#include <babeltrace/graph/component.h>
34#include <babeltrace/graph/component-source-internal.h>
35#include <babeltrace/graph/component-class-internal.h>
fa054faf 36#include <babeltrace/graph/notification.h>
b2e0c907
PP
37#include <babeltrace/graph/notification-iterator.h>
38#include <babeltrace/graph/notification-iterator-internal.h>
e7fa96c3 39#include <babeltrace/graph/notification-internal.h>
3230ee6b
PP
40#include <babeltrace/graph/notification-event.h>
41#include <babeltrace/graph/notification-event-internal.h>
42#include <babeltrace/graph/notification-packet.h>
43#include <babeltrace/graph/notification-packet-internal.h>
44#include <babeltrace/graph/notification-stream.h>
45#include <babeltrace/graph/notification-stream-internal.h>
46#include <babeltrace/graph/port.h>
c55a9f58 47#include <babeltrace/types.h>
fa054faf 48#include <stdint.h>
3230ee6b
PP
49
50struct stream_state {
51 struct bt_ctf_stream *stream; /* owned by this */
52 struct bt_ctf_packet *cur_packet; /* owned by this */
c55a9f58 53 bt_bool is_ended;
3230ee6b
PP
54};
55
56enum action_type {
57 ACTION_TYPE_PUSH_NOTIF,
58 ACTION_TYPE_MAP_PORT_TO_COMP_IN_STREAM,
59 ACTION_TYPE_ADD_STREAM_STATE,
60 ACTION_TYPE_SET_STREAM_STATE_IS_ENDED,
61 ACTION_TYPE_SET_STREAM_STATE_CUR_PACKET,
62};
63
64struct action {
65 enum action_type type;
66 union {
67 /* ACTION_TYPE_PUSH_NOTIF */
68 struct {
69 struct bt_notification *notif; /* owned by this */
70 } push_notif;
71
72 /* ACTION_TYPE_MAP_PORT_TO_COMP_IN_STREAM */
73 struct {
74 struct bt_ctf_stream *stream; /* owned by this */
75 struct bt_component *component; /* owned by this */
76 struct bt_port *port; /* owned by this */
77 } map_port_to_comp_in_stream;
78
79 /* ACTION_TYPE_ADD_STREAM_STATE */
80 struct {
81 struct bt_ctf_stream *stream; /* owned by this */
82 struct stream_state *stream_state; /* owned by this */
83 } add_stream_state;
84
85 /* ACTION_TYPE_SET_STREAM_STATE_IS_ENDED */
86 struct {
87 struct stream_state *stream_state; /* weak */
88 } set_stream_state_is_ended;
89
90 /* ACTION_TYPE_SET_STREAM_STATE_CUR_PACKET */
91 struct {
92 struct stream_state *stream_state; /* weak */
93 struct bt_ctf_packet *packet; /* owned by this */
94 } set_stream_state_cur_packet;
95 } payload;
96};
97
98static
99void stream_destroy_listener(struct bt_ctf_stream *stream, void *data)
100{
101 struct bt_notification_iterator *iterator = data;
102
103 /* Remove associated stream state */
104 g_hash_table_remove(iterator->stream_states, stream);
105}
106
107static
108void destroy_stream_state(struct stream_state *stream_state)
109{
110 if (!stream_state) {
111 return;
112 }
113
114 bt_put(stream_state->cur_packet);
115 bt_put(stream_state->stream);
116 g_free(stream_state);
117}
118
119static
120void destroy_action(struct action *action)
121{
122 assert(action);
123
124 switch (action->type) {
125 case ACTION_TYPE_PUSH_NOTIF:
126 BT_PUT(action->payload.push_notif.notif);
127 break;
128 case ACTION_TYPE_MAP_PORT_TO_COMP_IN_STREAM:
129 BT_PUT(action->payload.map_port_to_comp_in_stream.stream);
130 BT_PUT(action->payload.map_port_to_comp_in_stream.component);
131 BT_PUT(action->payload.map_port_to_comp_in_stream.port);
132 break;
133 case ACTION_TYPE_ADD_STREAM_STATE:
134 BT_PUT(action->payload.add_stream_state.stream);
135 destroy_stream_state(
136 action->payload.add_stream_state.stream_state);
137 action->payload.add_stream_state.stream_state = NULL;
138 break;
139 case ACTION_TYPE_SET_STREAM_STATE_CUR_PACKET:
140 BT_PUT(action->payload.set_stream_state_cur_packet.packet);
141 break;
142 case ACTION_TYPE_SET_STREAM_STATE_IS_ENDED:
143 break;
144 default:
c55a9f58 145 assert(BT_FALSE);
3230ee6b
PP
146 }
147}
148
149static
150void add_action(struct bt_notification_iterator *iterator,
151 struct action *action)
152{
153 g_array_append_val(iterator->actions, *action);
154}
155
156static
157void clear_actions(struct bt_notification_iterator *iterator)
158{
159 size_t i;
160
161 for (i = 0; i < iterator->actions->len; i++) {
162 struct action *action = &g_array_index(iterator->actions,
163 struct action, i);
164
165 destroy_action(action);
166 }
167
168 g_array_set_size(iterator->actions, 0);
169}
170
171static
172void apply_actions(struct bt_notification_iterator *iterator)
173{
174 size_t i;
175
176 for (i = 0; i < iterator->actions->len; i++) {
177 struct action *action = &g_array_index(iterator->actions,
178 struct action, i);
179
180 switch (action->type) {
181 case ACTION_TYPE_PUSH_NOTIF:
182 /* Move notification to queue */
183 g_queue_push_head(iterator->queue,
184 action->payload.push_notif.notif);
185 bt_notification_freeze(
186 action->payload.push_notif.notif);
187 action->payload.push_notif.notif = NULL;
188 break;
189 case ACTION_TYPE_MAP_PORT_TO_COMP_IN_STREAM:
190 bt_ctf_stream_map_component_to_port(
191 action->payload.map_port_to_comp_in_stream.stream,
192 action->payload.map_port_to_comp_in_stream.component,
193 action->payload.map_port_to_comp_in_stream.port);
194 break;
195 case ACTION_TYPE_ADD_STREAM_STATE:
196 /* Move stream state to hash table */
197 g_hash_table_insert(iterator->stream_states,
198 action->payload.add_stream_state.stream,
199 action->payload.add_stream_state.stream_state);
200
201 action->payload.add_stream_state.stream_state = NULL;
202 break;
203 case ACTION_TYPE_SET_STREAM_STATE_IS_ENDED:
204 /*
205 * We know that this stream is ended. We need to
206 * remember this as long as the stream exists to
207 * enforce that the same stream does not end
208 * twice.
209 *
210 * Here we add a destroy listener to the stream
211 * which we put after (becomes weak as the hash
212 * table key). If we were the last object to own
213 * this stream, the destroy listener is called
214 * when we call bt_put() which removes this
215 * stream state completely. This is important
216 * because the memory used by this stream object
217 * could be reused for another stream, and they
218 * must have different states.
219 */
220 bt_ctf_stream_add_destroy_listener(
221 action->payload.set_stream_state_is_ended.stream_state->stream,
222 stream_destroy_listener, iterator);
c55a9f58 223 action->payload.set_stream_state_is_ended.stream_state->is_ended = BT_TRUE;
3230ee6b
PP
224 BT_PUT(action->payload.set_stream_state_is_ended.stream_state->stream);
225 break;
226 case ACTION_TYPE_SET_STREAM_STATE_CUR_PACKET:
227 /* Move packet to stream state's current packet */
228 BT_MOVE(action->payload.set_stream_state_cur_packet.stream_state->cur_packet,
229 action->payload.set_stream_state_cur_packet.packet);
230 break;
231 default:
c55a9f58 232 assert(BT_FALSE);
3230ee6b
PP
233 }
234 }
235
236 clear_actions(iterator);
237}
238
239static
240struct stream_state *create_stream_state(struct bt_ctf_stream *stream)
241{
242 struct stream_state *stream_state = g_new0(struct stream_state, 1);
243
244 if (!stream_state) {
245 goto end;
246 }
247
248 /*
249 * We keep a reference to the stream until we know it's ended
250 * because we need to be able to create an automatic "stream
251 * end" notification when the user's "next" method returns
252 * BT_NOTIFICATION_ITERATOR_STATUS_END.
253 *
254 * We put this reference when the stream is marked as ended.
255 */
256 stream_state->stream = bt_get(stream);
257
258end:
259 return stream_state;
260}
47e5a032
JG
261
262static
b8a06801 263void bt_notification_iterator_destroy(struct bt_object *obj)
47e5a032 264{
8738a040 265 struct bt_notification_iterator *iterator;
d3eb6e8f 266 struct bt_component_class *comp_class;
8738a040 267
b8a06801
JG
268 assert(obj);
269 iterator = container_of(obj, struct bt_notification_iterator,
270 base);
3230ee6b
PP
271 assert(iterator->upstream_component);
272 comp_class = iterator->upstream_component->class;
d3eb6e8f
PP
273
274 /* Call user-defined destroy method */
275 switch (comp_class->type) {
276 case BT_COMPONENT_CLASS_TYPE_SOURCE:
277 {
278 struct bt_component_class_source *source_class;
279
280 source_class = container_of(comp_class, struct bt_component_class_source, parent);
281
64cadc66
PP
282 if (source_class->methods.iterator.finalize) {
283 source_class->methods.iterator.finalize(
890882ef 284 bt_private_notification_iterator_from_notification_iterator(iterator));
d3eb6e8f
PP
285 }
286 break;
512ccb99 287 }
d3eb6e8f
PP
288 case BT_COMPONENT_CLASS_TYPE_FILTER:
289 {
290 struct bt_component_class_filter *filter_class;
291
292 filter_class = container_of(comp_class, struct bt_component_class_filter, parent);
293
64cadc66
PP
294 if (filter_class->methods.iterator.finalize) {
295 filter_class->methods.iterator.finalize(
890882ef 296 bt_private_notification_iterator_from_notification_iterator(iterator));
d3eb6e8f
PP
297 }
298 break;
299 }
300 default:
301 /* Unreachable */
302 assert(0);
303 }
304
3230ee6b
PP
305 if (iterator->queue) {
306 struct bt_notification *notif;
307
308 while ((notif = g_queue_pop_tail(iterator->queue))) {
309 bt_put(notif);
310 }
311
312 g_queue_free(iterator->queue);
313 }
314
315 if (iterator->stream_states) {
316 /*
317 * Remove our destroy listener from each stream which
318 * has a state in this iterator. Otherwise the destroy
319 * listener would be called with an invalid/other
320 * notification iterator object.
321 */
322 GHashTableIter ht_iter;
323 gpointer stream_gptr, stream_state_gptr;
324
325 g_hash_table_iter_init(&ht_iter, iterator->stream_states);
326
327 while (g_hash_table_iter_next(&ht_iter, &stream_gptr, &stream_state_gptr)) {
328 assert(stream_gptr);
329 bt_ctf_stream_remove_destroy_listener(
330 (void *) stream_gptr, stream_destroy_listener,
331 iterator);
332 }
333
334 g_hash_table_destroy(iterator->stream_states);
335 }
336
337 if (iterator->actions) {
338 g_array_free(iterator->actions, TRUE);
339 }
340
341 bt_put(iterator->current_notification);
342 bt_put(iterator->upstream_component);
343 bt_put(iterator->upstream_port);
8738a040 344 g_free(iterator);
47e5a032
JG
345}
346
fa054faf
PP
347static
348int create_subscription_mask_from_notification_types(
349 struct bt_notification_iterator *iterator,
350 const enum bt_notification_type *notif_types)
351{
352 const enum bt_notification_type *notif_type;
353 int ret = 0;
354
355 assert(notif_types);
356 iterator->subscription_mask = 0;
357
358 for (notif_type = notif_types;
359 *notif_type != BT_NOTIFICATION_TYPE_SENTINEL;
360 notif_type++) {
361 switch (*notif_type) {
362 case BT_NOTIFICATION_TYPE_ALL:
363 iterator->subscription_mask |=
364 BT_NOTIFICATION_ITERATOR_NOTIF_TYPE_EVENT |
365 BT_NOTIFICATION_ITERATOR_NOTIF_TYPE_INACTIVITY |
366 BT_NOTIFICATION_ITERATOR_NOTIF_TYPE_STREAM_BEGIN |
367 BT_NOTIFICATION_ITERATOR_NOTIF_TYPE_STREAM_END |
368 BT_NOTIFICATION_ITERATOR_NOTIF_TYPE_PACKET_BEGIN |
369 BT_NOTIFICATION_ITERATOR_NOTIF_TYPE_PACKET_END;
370 break;
371 case BT_NOTIFICATION_TYPE_EVENT:
372 iterator->subscription_mask |= BT_NOTIFICATION_ITERATOR_NOTIF_TYPE_EVENT;
373 break;
374 case BT_NOTIFICATION_TYPE_INACTIVITY:
375 iterator->subscription_mask |= BT_NOTIFICATION_ITERATOR_NOTIF_TYPE_INACTIVITY;
376 break;
377 case BT_NOTIFICATION_TYPE_STREAM_BEGIN:
378 iterator->subscription_mask |= BT_NOTIFICATION_ITERATOR_NOTIF_TYPE_STREAM_BEGIN;
379 break;
380 case BT_NOTIFICATION_TYPE_STREAM_END:
381 iterator->subscription_mask |= BT_NOTIFICATION_ITERATOR_NOTIF_TYPE_STREAM_END;
382 break;
383 case BT_NOTIFICATION_TYPE_PACKET_BEGIN:
384 iterator->subscription_mask |= BT_NOTIFICATION_ITERATOR_NOTIF_TYPE_PACKET_BEGIN;
385 break;
386 case BT_NOTIFICATION_TYPE_PACKET_END:
387 iterator->subscription_mask |= BT_NOTIFICATION_ITERATOR_NOTIF_TYPE_PACKET_END;
388 break;
389 default:
390 ret = -1;
391 goto end;
392 }
393 }
394
395end:
396 return ret;
397}
398
47e5a032
JG
399BT_HIDDEN
400struct bt_notification_iterator *bt_notification_iterator_create(
3230ee6b 401 struct bt_component *upstream_comp,
fa054faf
PP
402 struct bt_port *upstream_port,
403 const enum bt_notification_type *notification_types)
47e5a032 404{
d3e4dcd8 405 enum bt_component_class_type type;
47e5a032
JG
406 struct bt_notification_iterator *iterator = NULL;
407
3230ee6b
PP
408 assert(upstream_comp);
409 assert(upstream_port);
fa054faf 410 assert(notification_types);
3230ee6b 411 assert(bt_port_is_connected(upstream_port));
16b7b023 412
3230ee6b 413 type = bt_component_get_class_type(upstream_comp);
16b7b023 414 switch (type) {
d3e4dcd8
PP
415 case BT_COMPONENT_CLASS_TYPE_SOURCE:
416 case BT_COMPONENT_CLASS_TYPE_FILTER:
16b7b023
JG
417 break;
418 default:
3230ee6b 419 goto error;
47e5a032
JG
420 }
421
422 iterator = g_new0(struct bt_notification_iterator, 1);
423 if (!iterator) {
3230ee6b 424 goto error;
47e5a032
JG
425 }
426
b8a06801 427 bt_object_init(iterator, bt_notification_iterator_destroy);
3230ee6b 428
fa054faf
PP
429 if (create_subscription_mask_from_notification_types(iterator,
430 notification_types)) {
431 goto error;
432 }
433
3230ee6b
PP
434 iterator->stream_states = g_hash_table_new_full(g_direct_hash,
435 g_direct_equal, NULL, (GDestroyNotify) destroy_stream_state);
436 if (!iterator->stream_states) {
437 goto error;
438 }
439
440 iterator->queue = g_queue_new();
441 if (!iterator->queue) {
442 goto error;
443 }
444
445 iterator->actions = g_array_new(FALSE, FALSE, sizeof(struct action));
446 if (!iterator->actions) {
447 goto error;
448 }
449
450 iterator->upstream_component = bt_get(upstream_comp);
451 iterator->upstream_port = bt_get(upstream_port);
452 goto end;
453
454error:
455 BT_PUT(iterator);
456
47e5a032
JG
457end:
458 return iterator;
459}
460
461BT_HIDDEN
462enum bt_notification_iterator_status bt_notification_iterator_validate(
463 struct bt_notification_iterator *iterator)
464{
8738a040 465 enum bt_notification_iterator_status ret =
ea8d3e58 466 BT_NOTIFICATION_ITERATOR_STATUS_OK;
47e5a032 467
d3eb6e8f 468 if (!iterator) {
fe8ad2b6 469 ret = BT_NOTIFICATION_ITERATOR_STATUS_INVALID;
ea8d3e58
JG
470 goto end;
471 }
ea8d3e58
JG
472end:
473 return ret;
474}
475
890882ef
PP
476void *bt_private_notification_iterator_get_user_data(
477 struct bt_private_notification_iterator *private_iterator)
ea8d3e58 478{
890882ef
PP
479 struct bt_notification_iterator *iterator =
480 bt_notification_iterator_from_private(private_iterator);
481
ea8d3e58
JG
482 return iterator ? iterator->user_data : NULL;
483}
484
485enum bt_notification_iterator_status
890882ef
PP
486bt_private_notification_iterator_set_user_data(
487 struct bt_private_notification_iterator *private_iterator,
488 void *data)
ea8d3e58
JG
489{
490 enum bt_notification_iterator_status ret =
491 BT_NOTIFICATION_ITERATOR_STATUS_OK;
890882ef
PP
492 struct bt_notification_iterator *iterator =
493 bt_notification_iterator_from_private(private_iterator);
ea8d3e58 494
d3eb6e8f 495 if (!iterator) {
fe8ad2b6 496 ret = BT_NOTIFICATION_ITERATOR_STATUS_INVALID;
ea8d3e58
JG
497 goto end;
498 }
499
500 iterator->user_data = data;
8738a040
JG
501end:
502 return ret;
503}
413bc2c4 504
53d45b87
JG
505struct bt_notification *bt_notification_iterator_get_notification(
506 struct bt_notification_iterator *iterator)
507{
41a2b7ae 508 struct bt_notification *notification = NULL;
d3eb6e8f 509
41a2b7ae
PP
510 if (!iterator) {
511 goto end;
d3eb6e8f 512 }
d3eb6e8f 513
41a2b7ae 514 notification = bt_get(iterator->current_notification);
d3eb6e8f 515
41a2b7ae
PP
516end:
517 return notification;
53d45b87
JG
518}
519
fa054faf
PP
520static
521enum bt_notification_iterator_notif_type
522bt_notification_iterator_notif_type_from_notif_type(
523 enum bt_notification_type notif_type)
524{
525 enum bt_notification_iterator_notif_type iter_notif_type;
526
527 switch (notif_type) {
528 case BT_NOTIFICATION_TYPE_EVENT:
529 iter_notif_type = BT_NOTIFICATION_ITERATOR_NOTIF_TYPE_EVENT;
530 break;
531 case BT_NOTIFICATION_TYPE_INACTIVITY:
532 iter_notif_type = BT_NOTIFICATION_ITERATOR_NOTIF_TYPE_INACTIVITY;
533 break;
534 case BT_NOTIFICATION_TYPE_STREAM_BEGIN:
535 iter_notif_type = BT_NOTIFICATION_ITERATOR_NOTIF_TYPE_STREAM_BEGIN;
536 break;
537 case BT_NOTIFICATION_TYPE_STREAM_END:
538 iter_notif_type = BT_NOTIFICATION_ITERATOR_NOTIF_TYPE_STREAM_END;
539 break;
540 case BT_NOTIFICATION_TYPE_PACKET_BEGIN:
541 iter_notif_type = BT_NOTIFICATION_ITERATOR_NOTIF_TYPE_PACKET_BEGIN;
542 break;
543 case BT_NOTIFICATION_TYPE_PACKET_END:
544 iter_notif_type = BT_NOTIFICATION_ITERATOR_NOTIF_TYPE_PACKET_END;
545 break;
546 default:
c55a9f58 547 assert(BT_FALSE);
fa054faf
PP
548 }
549
550 return iter_notif_type;
551}
552
3230ee6b 553static
c55a9f58 554bt_bool validate_notification(struct bt_notification_iterator *iterator,
3230ee6b
PP
555 struct bt_notification *notif,
556 struct bt_ctf_stream *notif_stream,
557 struct bt_ctf_packet *notif_packet)
558{
c55a9f58 559 bt_bool is_valid = BT_TRUE;
3230ee6b
PP
560 struct stream_state *stream_state;
561 struct bt_port *stream_comp_cur_port;
562
563 assert(notif_stream);
564 stream_comp_cur_port =
565 bt_ctf_stream_port_for_component(notif_stream,
566 iterator->upstream_component);
567 if (!stream_comp_cur_port) {
568 /*
569 * This is the first time this notification iterator
570 * bumps into this stream. Add an action to map the
571 * iterator's upstream component to the iterator's
572 * upstream port in this stream.
573 */
574 struct action action = {
575 .type = ACTION_TYPE_MAP_PORT_TO_COMP_IN_STREAM,
576 .payload.map_port_to_comp_in_stream = {
577 .stream = bt_get(notif_stream),
578 .component = bt_get(iterator->upstream_component),
579 .port = bt_get(iterator->upstream_port),
580 },
581 };
582
583 add_action(iterator, &action);
584 } else {
585 if (stream_comp_cur_port != iterator->upstream_port) {
586 /*
587 * It looks like two different ports of the same
588 * component are emitting notifications which
589 * have references to the same stream. This is
590 * bad: the API guarantees that it can never
591 * happen.
592 */
c55a9f58 593 is_valid = BT_FALSE;
3230ee6b
PP
594 goto end;
595 }
596
597 }
598
599 stream_state = g_hash_table_lookup(iterator->stream_states,
600 notif_stream);
601 if (stream_state) {
602 if (stream_state->is_ended) {
603 /*
604 * There's a new notification which has a
605 * reference to a stream which, from this
606 * iterator's point of view, is ended ("end of
607 * stream" notification was returned). This is
608 * bad: the API guarantees that it can never
609 * happen.
610 */
c55a9f58 611 is_valid = BT_FALSE;
3230ee6b
PP
612 goto end;
613 }
614
615 switch (notif->type) {
616 case BT_NOTIFICATION_TYPE_STREAM_BEGIN:
617 /*
618 * We already have a stream state, which means
619 * we already returned a "stream begin"
620 * notification: this is an invalid duplicate.
621 */
c55a9f58 622 is_valid = BT_FALSE;
3230ee6b
PP
623 goto end;
624 case BT_NOTIFICATION_TYPE_PACKET_BEGIN:
625 if (notif_packet == stream_state->cur_packet) {
626 /* Duplicate "packet begin" notification */
c55a9f58 627 is_valid = BT_FALSE;
3230ee6b
PP
628 goto end;
629 }
630 break;
631 default:
632 break;
633 }
634 }
635
636end:
637 return is_valid;
638}
639
fa054faf 640static
c55a9f58 641bt_bool is_subscribed_to_notification_type(struct bt_notification_iterator *iterator,
fa054faf
PP
642 enum bt_notification_type notif_type)
643{
644 uint32_t iter_notif_type =
645 (uint32_t) bt_notification_iterator_notif_type_from_notif_type(
646 notif_type);
647
c55a9f58 648 return (iter_notif_type & iterator->subscription_mask) ? BT_TRUE : BT_FALSE;
fa054faf
PP
649}
650
3230ee6b
PP
651static
652void add_action_push_notif(struct bt_notification_iterator *iterator,
653 struct bt_notification *notif)
654{
655 struct action action = {
656 .type = ACTION_TYPE_PUSH_NOTIF,
3230ee6b
PP
657 };
658
659 assert(notif);
fa054faf
PP
660
661 if (!is_subscribed_to_notification_type(iterator, notif->type)) {
662 return;
663 }
664
665 action.payload.push_notif.notif = bt_get(notif);
3230ee6b
PP
666 add_action(iterator, &action);
667}
668
669static
670int add_action_push_notif_stream_begin(
671 struct bt_notification_iterator *iterator,
672 struct bt_ctf_stream *stream)
673{
674 int ret = 0;
675 struct bt_notification *stream_begin_notif = NULL;
676
fa054faf
PP
677 if (!is_subscribed_to_notification_type(iterator,
678 BT_NOTIFICATION_TYPE_STREAM_BEGIN)) {
679 goto end;
680 }
681
3230ee6b
PP
682 assert(stream);
683 stream_begin_notif = bt_notification_stream_begin_create(stream);
684 if (!stream_begin_notif) {
685 goto error;
686 }
687
688 add_action_push_notif(iterator, stream_begin_notif);
689 goto end;
690
691error:
692 ret = -1;
693
694end:
695 bt_put(stream_begin_notif);
696 return ret;
697}
698
699static
700int add_action_push_notif_stream_end(
701 struct bt_notification_iterator *iterator,
702 struct bt_ctf_stream *stream)
703{
704 int ret = 0;
705 struct bt_notification *stream_end_notif = NULL;
706
fa054faf
PP
707 if (!is_subscribed_to_notification_type(iterator,
708 BT_NOTIFICATION_TYPE_STREAM_END)) {
709 goto end;
710 }
711
3230ee6b
PP
712 assert(stream);
713 stream_end_notif = bt_notification_stream_end_create(stream);
714 if (!stream_end_notif) {
715 goto error;
716 }
717
718 add_action_push_notif(iterator, stream_end_notif);
719 goto end;
720
721error:
722 ret = -1;
723
724end:
725 bt_put(stream_end_notif);
726 return ret;
727}
728
729static
730int add_action_push_notif_packet_begin(
731 struct bt_notification_iterator *iterator,
732 struct bt_ctf_packet *packet)
733{
734 int ret = 0;
735 struct bt_notification *packet_begin_notif = NULL;
736
fa054faf
PP
737 if (!is_subscribed_to_notification_type(iterator,
738 BT_NOTIFICATION_TYPE_PACKET_BEGIN)) {
739 goto end;
740 }
741
3230ee6b
PP
742 assert(packet);
743 packet_begin_notif = bt_notification_packet_begin_create(packet);
744 if (!packet_begin_notif) {
745 goto error;
746 }
747
748 add_action_push_notif(iterator, packet_begin_notif);
749 goto end;
750
751error:
752 ret = -1;
753
754end:
755 bt_put(packet_begin_notif);
756 return ret;
757}
758
759static
760int add_action_push_notif_packet_end(
761 struct bt_notification_iterator *iterator,
762 struct bt_ctf_packet *packet)
763{
764 int ret = 0;
765 struct bt_notification *packet_end_notif = NULL;
766
fa054faf
PP
767 if (!is_subscribed_to_notification_type(iterator,
768 BT_NOTIFICATION_TYPE_PACKET_END)) {
769 goto end;
770 }
771
3230ee6b
PP
772 assert(packet);
773 packet_end_notif = bt_notification_packet_end_create(packet);
774 if (!packet_end_notif) {
775 goto error;
776 }
777
778 add_action_push_notif(iterator, packet_end_notif);
779 goto end;
780
781error:
782 ret = -1;
783
784end:
785 bt_put(packet_end_notif);
786 return ret;
787}
788
789static
790void add_action_set_stream_state_is_ended(
791 struct bt_notification_iterator *iterator,
792 struct stream_state *stream_state)
793{
794 struct action action = {
795 .type = ACTION_TYPE_SET_STREAM_STATE_IS_ENDED,
796 .payload.set_stream_state_is_ended = {
797 .stream_state = stream_state,
798 },
799 };
800
801 assert(stream_state);
802 add_action(iterator, &action);
803}
804
805static
806void add_action_set_stream_state_cur_packet(
807 struct bt_notification_iterator *iterator,
808 struct stream_state *stream_state,
809 struct bt_ctf_packet *packet)
810{
811 struct action action = {
812 .type = ACTION_TYPE_SET_STREAM_STATE_CUR_PACKET,
813 .payload.set_stream_state_cur_packet = {
814 .stream_state = stream_state,
815 .packet = bt_get(packet),
816 },
817 };
818
819 assert(stream_state);
820 add_action(iterator, &action);
821}
822
823static
824int ensure_stream_state_exists(struct bt_notification_iterator *iterator,
825 struct bt_notification *stream_begin_notif,
826 struct bt_ctf_stream *notif_stream,
827 struct stream_state **stream_state)
828{
829 int ret = 0;
830
831 if (!notif_stream) {
832 /*
833 * The notification does not reference any stream: no
834 * need to get or create a stream state.
835 */
836 goto end;
837 }
838
839 *stream_state = g_hash_table_lookup(iterator->stream_states,
840 notif_stream);
841 if (!*stream_state) {
842 /*
843 * This iterator did not bump into this stream yet:
844 * create a stream state and a "stream begin"
845 * notification.
846 */
847 struct action action = {
848 .type = ACTION_TYPE_ADD_STREAM_STATE,
849 .payload.add_stream_state = {
850 .stream = bt_get(notif_stream),
851 .stream_state = NULL,
852 },
853 };
854
855 *stream_state = create_stream_state(notif_stream);
856 if (!stream_state) {
857 goto error;
858 }
859
860 action.payload.add_stream_state.stream_state =
861 *stream_state;
862 add_action(iterator, &action);
863
864 if (stream_begin_notif) {
865 add_action_push_notif(iterator, stream_begin_notif);
866 } else {
867 ret = add_action_push_notif_stream_begin(iterator,
868 notif_stream);
869 if (ret) {
870 goto error;
871 }
872 }
873 }
874
875 goto end;
876
877error:
878 destroy_stream_state(*stream_state);
879 ret = -1;
880
881end:
882 return ret;
883}
884
885static
886int handle_packet_switch(struct bt_notification_iterator *iterator,
887 struct bt_notification *packet_begin_notif,
888 struct bt_ctf_packet *new_packet,
889 struct stream_state *stream_state)
890{
891 int ret = 0;
892
893 if (stream_state->cur_packet == new_packet) {
894 goto end;
895 }
896
897 if (stream_state->cur_packet) {
898 /* End of the current packet */
899 ret = add_action_push_notif_packet_end(iterator,
900 stream_state->cur_packet);
901 if (ret) {
902 goto error;
903 }
904 }
905
906 /* Beginning of the new packet */
907 if (packet_begin_notif) {
908 add_action_push_notif(iterator, packet_begin_notif);
909 } else if (new_packet) {
910 ret = add_action_push_notif_packet_begin(iterator,
911 new_packet);
912 if (ret) {
913 goto error;
914 }
915 }
916
917 add_action_set_stream_state_cur_packet(iterator, stream_state,
918 new_packet);
919 goto end;
920
921error:
922 ret = -1;
923
924end:
925 return ret;
926}
927
928static
929int handle_notif_stream_begin(
930 struct bt_notification_iterator *iterator,
931 struct bt_notification *notif,
932 struct bt_ctf_stream *notif_stream)
933{
934 int ret = 0;
935 struct stream_state *stream_state;
936
937 assert(notif->type == BT_NOTIFICATION_TYPE_STREAM_BEGIN);
938 assert(notif_stream);
939 ret = ensure_stream_state_exists(iterator, notif, notif_stream,
940 &stream_state);
941 if (ret) {
942 goto error;
943 }
944
945 goto end;
946
947error:
948 ret = -1;
949
950end:
951 return ret;
952}
953
954static
955int handle_notif_stream_end(
956 struct bt_notification_iterator *iterator,
957 struct bt_notification *notif,
958 struct bt_ctf_stream *notif_stream)
959{
960 int ret = 0;
961 struct stream_state *stream_state;
962
963 assert(notif->type == BT_NOTIFICATION_TYPE_STREAM_END);
964 assert(notif_stream);
965 ret = ensure_stream_state_exists(iterator, NULL, notif_stream,
966 &stream_state);
967 if (ret) {
968 goto error;
969 }
970
971 ret = handle_packet_switch(iterator, NULL, NULL, stream_state);
972 if (ret) {
973 goto error;
974 }
975
976 add_action_push_notif(iterator, notif);
977 add_action_set_stream_state_is_ended(iterator, stream_state);
978 goto end;
979
980error:
981 ret = -1;
982
983end:
984 return ret;
985}
986
987static
988int handle_notif_packet_begin(
989 struct bt_notification_iterator *iterator,
990 struct bt_notification *notif,
991 struct bt_ctf_stream *notif_stream,
992 struct bt_ctf_packet *notif_packet)
993{
994 int ret = 0;
995 struct stream_state *stream_state;
996
997 assert(notif->type == BT_NOTIFICATION_TYPE_PACKET_BEGIN);
998 assert(notif_packet);
999 ret = ensure_stream_state_exists(iterator, NULL, notif_stream,
1000 &stream_state);
1001 if (ret) {
1002 goto error;
1003 }
1004
1005 ret = handle_packet_switch(iterator, notif, notif_packet, stream_state);
1006 if (ret) {
1007 goto error;
1008 }
1009
1010 goto end;
1011
1012error:
1013 ret = -1;
1014
1015end:
1016 return ret;
1017}
1018
1019static
1020int handle_notif_packet_end(
1021 struct bt_notification_iterator *iterator,
1022 struct bt_notification *notif,
1023 struct bt_ctf_stream *notif_stream,
1024 struct bt_ctf_packet *notif_packet)
1025{
1026 int ret = 0;
1027 struct stream_state *stream_state;
1028
1029 assert(notif->type == BT_NOTIFICATION_TYPE_PACKET_END);
1030 assert(notif_packet);
1031 ret = ensure_stream_state_exists(iterator, NULL, notif_stream,
1032 &stream_state);
1033 if (ret) {
1034 goto error;
1035 }
1036
1037 ret = handle_packet_switch(iterator, NULL, notif_packet, stream_state);
1038 if (ret) {
1039 goto error;
1040 }
1041
1042 /* End of the current packet */
1043 add_action_push_notif(iterator, notif);
1044 add_action_set_stream_state_cur_packet(iterator, stream_state, NULL);
1045 goto end;
1046
1047error:
1048 ret = -1;
1049
1050end:
1051 return ret;
1052}
1053
1054static
1055int handle_notif_event(
1056 struct bt_notification_iterator *iterator,
1057 struct bt_notification *notif,
1058 struct bt_ctf_stream *notif_stream,
1059 struct bt_ctf_packet *notif_packet)
1060{
1061 int ret = 0;
1062 struct stream_state *stream_state;
1063
1064 assert(notif->type == BT_NOTIFICATION_TYPE_EVENT);
1065 assert(notif_packet);
1066 ret = ensure_stream_state_exists(iterator, NULL, notif_stream,
1067 &stream_state);
1068 if (ret) {
1069 goto error;
1070 }
1071
1072 ret = handle_packet_switch(iterator, NULL, notif_packet, stream_state);
1073 if (ret) {
1074 goto error;
1075 }
1076
1077 add_action_push_notif(iterator, notif);
1078 goto end;
1079
1080error:
1081 ret = -1;
1082
1083end:
1084 return ret;
1085}
1086
1087static
1088int enqueue_notification_and_automatic(
1089 struct bt_notification_iterator *iterator,
1090 struct bt_notification *notif)
1091{
1092 int ret = 0;
1093 struct bt_ctf_event *notif_event = NULL;
1094 struct bt_ctf_stream *notif_stream = NULL;
1095 struct bt_ctf_packet *notif_packet = NULL;
1096
1097 assert(notif);
1098
fa054faf
PP
1099 // TODO: Skip most of this if the iterator is only subscribed
1100 // to event/inactivity notifications.
1101
3230ee6b
PP
1102 /* Get the stream and packet referred by the notification */
1103 switch (notif->type) {
1104 case BT_NOTIFICATION_TYPE_EVENT:
1105 notif_event = bt_notification_event_borrow_event(notif);
1106 assert(notif_event);
1107 notif_packet = bt_ctf_event_borrow_packet(notif_event);
1108 assert(notif_packet);
1109 break;
1110 case BT_NOTIFICATION_TYPE_STREAM_BEGIN:
1111 notif_stream =
1112 bt_notification_stream_begin_borrow_stream(notif);
1113 assert(notif_stream);
1114 break;
1115 case BT_NOTIFICATION_TYPE_STREAM_END:
1116 notif_stream = bt_notification_stream_end_borrow_stream(notif);
1117 assert(notif_stream);
1118 break;
1119 case BT_NOTIFICATION_TYPE_PACKET_BEGIN:
1120 notif_packet =
1121 bt_notification_packet_begin_borrow_packet(notif);
1122 assert(notif_packet);
1123 break;
1124 case BT_NOTIFICATION_TYPE_PACKET_END:
1125 notif_packet = bt_notification_packet_end_borrow_packet(notif);
1126 assert(notif_packet);
1127 break;
1128 case BT_NOTIFICATION_TYPE_INACTIVITY:
1129 /* Always valid */
1130 break;
1131 default:
1132 /*
1133 * Invalid type of notification. Only the notification
1134 * types above are allowed to be returned by a user
1135 * component.
1136 */
1137 goto error;
1138 }
1139
1140 if (notif_packet) {
1141 notif_stream = bt_ctf_packet_borrow_stream(notif_packet);
1142 assert(notif_stream);
1143 }
1144
1145 if (!notif_stream) {
1146 /*
1147 * The notification has no reference to a stream: it
1148 * cannot cause the creation of automatic notifications.
1149 */
1150 goto end;
1151 }
1152
1153 if (!validate_notification(iterator, notif, notif_stream,
1154 notif_packet)) {
1155 goto error;
1156 }
1157
1158 switch (notif->type) {
1159 case BT_NOTIFICATION_TYPE_EVENT:
1160 ret = handle_notif_event(iterator, notif, notif_stream,
1161 notif_packet);
1162 break;
1163 case BT_NOTIFICATION_TYPE_STREAM_BEGIN:
1164 ret = handle_notif_stream_begin(iterator, notif, notif_stream);
1165 break;
1166 case BT_NOTIFICATION_TYPE_STREAM_END:
1167 ret = handle_notif_stream_end(iterator, notif, notif_stream);
1168 break;
1169 case BT_NOTIFICATION_TYPE_PACKET_BEGIN:
1170 ret = handle_notif_packet_begin(iterator, notif, notif_stream,
1171 notif_packet);
1172 break;
1173 case BT_NOTIFICATION_TYPE_PACKET_END:
1174 ret = handle_notif_packet_end(iterator, notif, notif_stream,
1175 notif_packet);
1176 break;
1177 default:
1178 break;
1179 }
1180
1181 if (ret) {
1182 goto error;
1183 }
1184
1185 apply_actions(iterator);
1186 goto end;
1187
1188error:
1189 ret = -1;
1190
1191end:
1192 return ret;
1193}
1194
1195static
1196int handle_end(struct bt_notification_iterator *iterator)
1197{
1198 GHashTableIter stream_state_iter;
1199 gpointer stream_gptr, stream_state_gptr;
1200 int ret = 0;
1201
1202 /*
1203 * Emit a "stream end" notification for each non-ended stream
1204 * known by this iterator and mark them as ended.
1205 */
1206 g_hash_table_iter_init(&stream_state_iter, iterator->stream_states);
1207
1208 while (g_hash_table_iter_next(&stream_state_iter, &stream_gptr,
1209 &stream_state_gptr)) {
1210 struct stream_state *stream_state = stream_state_gptr;
1211
1212 assert(stream_state_gptr);
1213
1214 if (stream_state->is_ended) {
1215 continue;
1216 }
1217
1218 ret = handle_packet_switch(iterator, NULL, NULL, stream_state);
1219 if (ret) {
1220 goto error;
1221 }
1222
1223 ret = add_action_push_notif_stream_end(iterator, stream_gptr);
1224 if (ret) {
1225 goto error;
1226 }
1227
1228 add_action_set_stream_state_is_ended(iterator, stream_state);
1229 }
1230
1231 apply_actions(iterator);
1232 goto end;
1233
1234error:
1235 ret = -1;
1236
1237end:
1238 return ret;
1239}
1240
1241static
1242enum bt_notification_iterator_status ensure_queue_has_notifications(
1243 struct bt_notification_iterator *iterator)
53d45b87 1244{
890882ef
PP
1245 struct bt_private_notification_iterator *priv_iterator =
1246 bt_private_notification_iterator_from_notification_iterator(iterator);
d3eb6e8f 1247 bt_component_class_notification_iterator_next_method next_method = NULL;
fe8ad2b6
PP
1248 struct bt_notification_iterator_next_return next_return = {
1249 .status = BT_NOTIFICATION_ITERATOR_STATUS_OK,
1250 .notification = NULL,
1251 };
3230ee6b
PP
1252 enum bt_notification_iterator_status status =
1253 BT_NOTIFICATION_ITERATOR_STATUS_OK;
1254 int ret;
41a2b7ae 1255
3230ee6b
PP
1256 assert(iterator);
1257
1258 if (iterator->queue->length > 0) {
1259 /* We already have enough */
1260 goto end;
1261 }
1262
1263 if (iterator->is_ended) {
1264 status = BT_NOTIFICATION_ITERATOR_STATUS_END;
41a2b7ae
PP
1265 goto end;
1266 }
d3eb6e8f 1267
3230ee6b
PP
1268 assert(iterator->upstream_component);
1269 assert(iterator->upstream_component->class);
d3eb6e8f 1270
3230ee6b
PP
1271 /* Pick the appropriate "next" method */
1272 switch (iterator->upstream_component->class->type) {
d3eb6e8f
PP
1273 case BT_COMPONENT_CLASS_TYPE_SOURCE:
1274 {
1275 struct bt_component_class_source *source_class =
3230ee6b 1276 container_of(iterator->upstream_component->class,
d3eb6e8f
PP
1277 struct bt_component_class_source, parent);
1278
1279 assert(source_class->methods.iterator.next);
1280 next_method = source_class->methods.iterator.next;
1281 break;
1282 }
1283 case BT_COMPONENT_CLASS_TYPE_FILTER:
1284 {
1285 struct bt_component_class_filter *filter_class =
3230ee6b 1286 container_of(iterator->upstream_component->class,
d3eb6e8f
PP
1287 struct bt_component_class_filter, parent);
1288
1289 assert(filter_class->methods.iterator.next);
1290 next_method = filter_class->methods.iterator.next;
1291 break;
1292 }
1293 default:
c55a9f58 1294 assert(BT_FALSE);
d3eb6e8f
PP
1295 break;
1296 }
1297
3230ee6b
PP
1298 /*
1299 * Call the user's "next" method to get the next notification
fa054faf 1300 * and status.
3230ee6b 1301 */
d3eb6e8f 1302 assert(next_method);
3230ee6b 1303
fa054faf
PP
1304 while (iterator->queue->length == 0) {
1305 next_return = next_method(priv_iterator);
1306 if (next_return.status < 0) {
1307 status = next_return.status;
3230ee6b
PP
1308 goto end;
1309 }
1310
fa054faf
PP
1311 switch (next_return.status) {
1312 case BT_NOTIFICATION_ITERATOR_STATUS_END:
1313 ret = handle_end(iterator);
1314 if (ret) {
1315 status = BT_NOTIFICATION_ITERATOR_STATUS_ERROR;
1316 goto end;
1317 }
3230ee6b 1318
fa054faf
PP
1319 if (iterator->queue->length == 0) {
1320 status = BT_NOTIFICATION_ITERATOR_STATUS_END;
1321 }
41a2b7ae 1322
c55a9f58 1323 iterator->is_ended = BT_TRUE;
3230ee6b 1324 goto end;
fa054faf
PP
1325 case BT_NOTIFICATION_ITERATOR_STATUS_AGAIN:
1326 status = BT_NOTIFICATION_ITERATOR_STATUS_AGAIN;
1327 goto end;
1328 case BT_NOTIFICATION_ITERATOR_STATUS_OK:
1329 if (!next_return.notification) {
1330 status = BT_NOTIFICATION_ITERATOR_STATUS_ERROR;
1331 goto end;
1332 }
1333
1334 /*
1335 * We know the notification is valid. Before we
1336 * push it to the head of the queue, push the
1337 * appropriate automatic notifications if any.
1338 */
1339 ret = enqueue_notification_and_automatic(iterator,
1340 next_return.notification);
1341 BT_PUT(next_return.notification);
1342 if (ret) {
1343 status = BT_NOTIFICATION_ITERATOR_STATUS_ERROR;
1344 goto end;
1345 }
1346 break;
1347 default:
1348 /* Unknown non-error status */
c55a9f58 1349 assert(BT_FALSE);
3230ee6b 1350 }
41a2b7ae
PP
1351 }
1352
1353end:
3230ee6b
PP
1354 return status;
1355}
1356
1357enum bt_notification_iterator_status
1358bt_notification_iterator_next(struct bt_notification_iterator *iterator)
1359{
1360 enum bt_notification_iterator_status status;
1361
1362 if (!iterator) {
1363 status = BT_NOTIFICATION_ITERATOR_STATUS_INVALID;
1364 goto end;
1365 }
1366
1367 /*
1368 * Make sure that the iterator's queue contains at least one
1369 * notification.
1370 */
1371 status = ensure_queue_has_notifications(iterator);
1372 if (status != BT_NOTIFICATION_ITERATOR_STATUS_OK) {
1373 goto end;
1374 }
1375
1376 /*
1377 * Move the notification at the tail of the queue to the
1378 * iterator's current notification.
1379 */
1380 assert(iterator->queue->length > 0);
1381 bt_put(iterator->current_notification);
1382 iterator->current_notification = g_queue_pop_tail(iterator->queue);
1383 assert(iterator->current_notification);
1384
1385end:
1386 return status;
53d45b87
JG
1387}
1388
413bc2c4
JG
1389struct bt_component *bt_notification_iterator_get_component(
1390 struct bt_notification_iterator *iterator)
1391{
3230ee6b 1392 return bt_get(iterator->upstream_component);
413bc2c4
JG
1393}
1394
91457551
PP
1395struct bt_private_component *
1396bt_private_notification_iterator_get_private_component(
1397 struct bt_private_notification_iterator *private_iterator)
1398{
1399 return bt_private_component_from_component(
1400 bt_notification_iterator_get_component(
1401 bt_notification_iterator_from_private(private_iterator)));
1402}
1403
9531634f
JG
1404enum bt_notification_iterator_status bt_notification_iterator_seek_time(
1405 struct bt_notification_iterator *iterator,
1406 enum bt_notification_iterator_seek_origin seek_origin,
1407 int64_t time)
1408{
b7726e32
MD
1409 enum bt_notification_iterator_status ret =
1410 BT_NOTIFICATION_ITERATOR_STATUS_UNSUPPORTED;
9531634f
JG
1411 return ret;
1412}
This page took 0.091708 seconds and 4 git commands to generate.