BT_GRAPH_STATUS_CANCELED is not an error, thus use a positive value
[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 */
7cdc2bab 1130 goto handle_notif;
3230ee6b
PP
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
7cdc2bab 1158handle_notif:
3230ee6b
PP
1159 switch (notif->type) {
1160 case BT_NOTIFICATION_TYPE_EVENT:
1161 ret = handle_notif_event(iterator, notif, notif_stream,
1162 notif_packet);
1163 break;
1164 case BT_NOTIFICATION_TYPE_STREAM_BEGIN:
1165 ret = handle_notif_stream_begin(iterator, notif, notif_stream);
1166 break;
1167 case BT_NOTIFICATION_TYPE_STREAM_END:
1168 ret = handle_notif_stream_end(iterator, notif, notif_stream);
1169 break;
1170 case BT_NOTIFICATION_TYPE_PACKET_BEGIN:
1171 ret = handle_notif_packet_begin(iterator, notif, notif_stream,
1172 notif_packet);
1173 break;
1174 case BT_NOTIFICATION_TYPE_PACKET_END:
1175 ret = handle_notif_packet_end(iterator, notif, notif_stream,
1176 notif_packet);
1177 break;
7cdc2bab
MD
1178 case BT_NOTIFICATION_TYPE_INACTIVITY:
1179 add_action_push_notif(iterator, notif);
1180 break;
3230ee6b
PP
1181 default:
1182 break;
1183 }
1184
1185 if (ret) {
1186 goto error;
1187 }
1188
1189 apply_actions(iterator);
1190 goto end;
1191
1192error:
1193 ret = -1;
1194
1195end:
1196 return ret;
1197}
1198
1199static
1200int handle_end(struct bt_notification_iterator *iterator)
1201{
1202 GHashTableIter stream_state_iter;
1203 gpointer stream_gptr, stream_state_gptr;
1204 int ret = 0;
1205
1206 /*
1207 * Emit a "stream end" notification for each non-ended stream
1208 * known by this iterator and mark them as ended.
1209 */
1210 g_hash_table_iter_init(&stream_state_iter, iterator->stream_states);
1211
1212 while (g_hash_table_iter_next(&stream_state_iter, &stream_gptr,
1213 &stream_state_gptr)) {
1214 struct stream_state *stream_state = stream_state_gptr;
1215
1216 assert(stream_state_gptr);
1217
1218 if (stream_state->is_ended) {
1219 continue;
1220 }
1221
1222 ret = handle_packet_switch(iterator, NULL, NULL, stream_state);
1223 if (ret) {
1224 goto error;
1225 }
1226
1227 ret = add_action_push_notif_stream_end(iterator, stream_gptr);
1228 if (ret) {
1229 goto error;
1230 }
1231
1232 add_action_set_stream_state_is_ended(iterator, stream_state);
1233 }
1234
1235 apply_actions(iterator);
1236 goto end;
1237
1238error:
1239 ret = -1;
1240
1241end:
1242 return ret;
1243}
1244
1245static
1246enum bt_notification_iterator_status ensure_queue_has_notifications(
1247 struct bt_notification_iterator *iterator)
53d45b87 1248{
890882ef
PP
1249 struct bt_private_notification_iterator *priv_iterator =
1250 bt_private_notification_iterator_from_notification_iterator(iterator);
d3eb6e8f 1251 bt_component_class_notification_iterator_next_method next_method = NULL;
fe8ad2b6
PP
1252 struct bt_notification_iterator_next_return next_return = {
1253 .status = BT_NOTIFICATION_ITERATOR_STATUS_OK,
1254 .notification = NULL,
1255 };
3230ee6b
PP
1256 enum bt_notification_iterator_status status =
1257 BT_NOTIFICATION_ITERATOR_STATUS_OK;
1258 int ret;
41a2b7ae 1259
3230ee6b
PP
1260 assert(iterator);
1261
1262 if (iterator->queue->length > 0) {
1263 /* We already have enough */
1264 goto end;
1265 }
1266
1267 if (iterator->is_ended) {
1268 status = BT_NOTIFICATION_ITERATOR_STATUS_END;
41a2b7ae
PP
1269 goto end;
1270 }
d3eb6e8f 1271
3230ee6b
PP
1272 assert(iterator->upstream_component);
1273 assert(iterator->upstream_component->class);
d3eb6e8f 1274
3230ee6b
PP
1275 /* Pick the appropriate "next" method */
1276 switch (iterator->upstream_component->class->type) {
d3eb6e8f
PP
1277 case BT_COMPONENT_CLASS_TYPE_SOURCE:
1278 {
1279 struct bt_component_class_source *source_class =
3230ee6b 1280 container_of(iterator->upstream_component->class,
d3eb6e8f
PP
1281 struct bt_component_class_source, parent);
1282
1283 assert(source_class->methods.iterator.next);
1284 next_method = source_class->methods.iterator.next;
1285 break;
1286 }
1287 case BT_COMPONENT_CLASS_TYPE_FILTER:
1288 {
1289 struct bt_component_class_filter *filter_class =
3230ee6b 1290 container_of(iterator->upstream_component->class,
d3eb6e8f
PP
1291 struct bt_component_class_filter, parent);
1292
1293 assert(filter_class->methods.iterator.next);
1294 next_method = filter_class->methods.iterator.next;
1295 break;
1296 }
1297 default:
c55a9f58 1298 assert(BT_FALSE);
d3eb6e8f
PP
1299 break;
1300 }
1301
3230ee6b
PP
1302 /*
1303 * Call the user's "next" method to get the next notification
fa054faf 1304 * and status.
3230ee6b 1305 */
d3eb6e8f 1306 assert(next_method);
3230ee6b 1307
fa054faf
PP
1308 while (iterator->queue->length == 0) {
1309 next_return = next_method(priv_iterator);
1310 if (next_return.status < 0) {
1311 status = next_return.status;
3230ee6b
PP
1312 goto end;
1313 }
1314
fa054faf
PP
1315 switch (next_return.status) {
1316 case BT_NOTIFICATION_ITERATOR_STATUS_END:
1317 ret = handle_end(iterator);
1318 if (ret) {
1319 status = BT_NOTIFICATION_ITERATOR_STATUS_ERROR;
1320 goto end;
1321 }
3230ee6b 1322
fa054faf
PP
1323 if (iterator->queue->length == 0) {
1324 status = BT_NOTIFICATION_ITERATOR_STATUS_END;
1325 }
41a2b7ae 1326
c55a9f58 1327 iterator->is_ended = BT_TRUE;
3230ee6b 1328 goto end;
fa054faf
PP
1329 case BT_NOTIFICATION_ITERATOR_STATUS_AGAIN:
1330 status = BT_NOTIFICATION_ITERATOR_STATUS_AGAIN;
1331 goto end;
1332 case BT_NOTIFICATION_ITERATOR_STATUS_OK:
1333 if (!next_return.notification) {
1334 status = BT_NOTIFICATION_ITERATOR_STATUS_ERROR;
1335 goto end;
1336 }
1337
1338 /*
1339 * We know the notification is valid. Before we
1340 * push it to the head of the queue, push the
1341 * appropriate automatic notifications if any.
1342 */
1343 ret = enqueue_notification_and_automatic(iterator,
1344 next_return.notification);
1345 BT_PUT(next_return.notification);
1346 if (ret) {
1347 status = BT_NOTIFICATION_ITERATOR_STATUS_ERROR;
1348 goto end;
1349 }
1350 break;
1351 default:
1352 /* Unknown non-error status */
c55a9f58 1353 assert(BT_FALSE);
3230ee6b 1354 }
41a2b7ae
PP
1355 }
1356
1357end:
3230ee6b
PP
1358 return status;
1359}
1360
1361enum bt_notification_iterator_status
1362bt_notification_iterator_next(struct bt_notification_iterator *iterator)
1363{
1364 enum bt_notification_iterator_status status;
1365
1366 if (!iterator) {
1367 status = BT_NOTIFICATION_ITERATOR_STATUS_INVALID;
1368 goto end;
1369 }
1370
1371 /*
1372 * Make sure that the iterator's queue contains at least one
1373 * notification.
1374 */
1375 status = ensure_queue_has_notifications(iterator);
1376 if (status != BT_NOTIFICATION_ITERATOR_STATUS_OK) {
1377 goto end;
1378 }
1379
1380 /*
1381 * Move the notification at the tail of the queue to the
1382 * iterator's current notification.
1383 */
1384 assert(iterator->queue->length > 0);
1385 bt_put(iterator->current_notification);
1386 iterator->current_notification = g_queue_pop_tail(iterator->queue);
1387 assert(iterator->current_notification);
1388
1389end:
1390 return status;
53d45b87
JG
1391}
1392
413bc2c4
JG
1393struct bt_component *bt_notification_iterator_get_component(
1394 struct bt_notification_iterator *iterator)
1395{
3230ee6b 1396 return bt_get(iterator->upstream_component);
413bc2c4
JG
1397}
1398
91457551
PP
1399struct bt_private_component *
1400bt_private_notification_iterator_get_private_component(
1401 struct bt_private_notification_iterator *private_iterator)
1402{
1403 return bt_private_component_from_component(
1404 bt_notification_iterator_get_component(
1405 bt_notification_iterator_from_private(private_iterator)));
1406}
1407
9531634f
JG
1408enum bt_notification_iterator_status bt_notification_iterator_seek_time(
1409 struct bt_notification_iterator *iterator,
1410 enum bt_notification_iterator_seek_origin seek_origin,
1411 int64_t time)
1412{
b7726e32
MD
1413 enum bt_notification_iterator_status ret =
1414 BT_NOTIFICATION_ITERATOR_STATUS_UNSUPPORTED;
9531634f
JG
1415 return ret;
1416}
This page took 0.084894 seconds and 4 git commands to generate.