Fix: do not allow a notif. iter. to call the user's "next" method once finalized
[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
5af447e5
PP
28#define BT_LOG_TAG "NOTIF-ITER"
29#include <babeltrace/lib-logging-internal.h>
30
3d9990ac 31#include <babeltrace/compiler-internal.h>
b8a06801 32#include <babeltrace/ref.h>
3230ee6b
PP
33#include <babeltrace/ctf-ir/event-internal.h>
34#include <babeltrace/ctf-ir/packet-internal.h>
35#include <babeltrace/ctf-ir/stream-internal.h>
bd14d768 36#include <babeltrace/graph/connection-internal.h>
b2e0c907
PP
37#include <babeltrace/graph/component.h>
38#include <babeltrace/graph/component-source-internal.h>
39#include <babeltrace/graph/component-class-internal.h>
fa054faf 40#include <babeltrace/graph/notification.h>
b2e0c907
PP
41#include <babeltrace/graph/notification-iterator.h>
42#include <babeltrace/graph/notification-iterator-internal.h>
e7fa96c3 43#include <babeltrace/graph/notification-internal.h>
3230ee6b
PP
44#include <babeltrace/graph/notification-event.h>
45#include <babeltrace/graph/notification-event-internal.h>
46#include <babeltrace/graph/notification-packet.h>
47#include <babeltrace/graph/notification-packet-internal.h>
48#include <babeltrace/graph/notification-stream.h>
49#include <babeltrace/graph/notification-stream-internal.h>
50#include <babeltrace/graph/port.h>
c55a9f58 51#include <babeltrace/types.h>
fa054faf 52#include <stdint.h>
0fbb9a9f 53#include <stdlib.h>
3230ee6b
PP
54
55struct stream_state {
56 struct bt_ctf_stream *stream; /* owned by this */
57 struct bt_ctf_packet *cur_packet; /* owned by this */
c55a9f58 58 bt_bool is_ended;
3230ee6b
PP
59};
60
61enum action_type {
62 ACTION_TYPE_PUSH_NOTIF,
63 ACTION_TYPE_MAP_PORT_TO_COMP_IN_STREAM,
64 ACTION_TYPE_ADD_STREAM_STATE,
65 ACTION_TYPE_SET_STREAM_STATE_IS_ENDED,
66 ACTION_TYPE_SET_STREAM_STATE_CUR_PACKET,
67};
68
69struct action {
70 enum action_type type;
71 union {
72 /* ACTION_TYPE_PUSH_NOTIF */
73 struct {
74 struct bt_notification *notif; /* owned by this */
75 } push_notif;
76
77 /* ACTION_TYPE_MAP_PORT_TO_COMP_IN_STREAM */
78 struct {
79 struct bt_ctf_stream *stream; /* owned by this */
80 struct bt_component *component; /* owned by this */
81 struct bt_port *port; /* owned by this */
82 } map_port_to_comp_in_stream;
83
84 /* ACTION_TYPE_ADD_STREAM_STATE */
85 struct {
86 struct bt_ctf_stream *stream; /* owned by this */
87 struct stream_state *stream_state; /* owned by this */
88 } add_stream_state;
89
90 /* ACTION_TYPE_SET_STREAM_STATE_IS_ENDED */
91 struct {
92 struct stream_state *stream_state; /* weak */
93 } set_stream_state_is_ended;
94
95 /* ACTION_TYPE_SET_STREAM_STATE_CUR_PACKET */
96 struct {
97 struct stream_state *stream_state; /* weak */
98 struct bt_ctf_packet *packet; /* owned by this */
99 } set_stream_state_cur_packet;
100 } payload;
101};
102
103static
104void stream_destroy_listener(struct bt_ctf_stream *stream, void *data)
105{
106 struct bt_notification_iterator *iterator = data;
107
108 /* Remove associated stream state */
109 g_hash_table_remove(iterator->stream_states, stream);
110}
111
112static
113void destroy_stream_state(struct stream_state *stream_state)
114{
115 if (!stream_state) {
116 return;
117 }
118
5af447e5
PP
119 BT_LOGV("Destroying stream state: stream-state-addr=%p", stream_state);
120 BT_LOGV_STR("Putting stream state's current packet.");
3230ee6b 121 bt_put(stream_state->cur_packet);
5af447e5 122 BT_LOGV_STR("Putting stream state's stream.");
3230ee6b
PP
123 bt_put(stream_state->stream);
124 g_free(stream_state);
125}
126
127static
128void destroy_action(struct action *action)
129{
130 assert(action);
131
132 switch (action->type) {
133 case ACTION_TYPE_PUSH_NOTIF:
134 BT_PUT(action->payload.push_notif.notif);
135 break;
136 case ACTION_TYPE_MAP_PORT_TO_COMP_IN_STREAM:
137 BT_PUT(action->payload.map_port_to_comp_in_stream.stream);
138 BT_PUT(action->payload.map_port_to_comp_in_stream.component);
139 BT_PUT(action->payload.map_port_to_comp_in_stream.port);
140 break;
141 case ACTION_TYPE_ADD_STREAM_STATE:
142 BT_PUT(action->payload.add_stream_state.stream);
143 destroy_stream_state(
144 action->payload.add_stream_state.stream_state);
145 action->payload.add_stream_state.stream_state = NULL;
146 break;
147 case ACTION_TYPE_SET_STREAM_STATE_CUR_PACKET:
148 BT_PUT(action->payload.set_stream_state_cur_packet.packet);
149 break;
150 case ACTION_TYPE_SET_STREAM_STATE_IS_ENDED:
151 break;
152 default:
0fbb9a9f 153 abort();
3230ee6b
PP
154 }
155}
156
157static
158void add_action(struct bt_notification_iterator *iterator,
159 struct action *action)
160{
161 g_array_append_val(iterator->actions, *action);
162}
163
164static
165void clear_actions(struct bt_notification_iterator *iterator)
166{
167 size_t i;
168
169 for (i = 0; i < iterator->actions->len; i++) {
170 struct action *action = &g_array_index(iterator->actions,
171 struct action, i);
172
173 destroy_action(action);
174 }
175
176 g_array_set_size(iterator->actions, 0);
177}
178
5af447e5
PP
179static inline
180const char *action_type_string(enum action_type type)
181{
182 switch (type) {
183 case ACTION_TYPE_PUSH_NOTIF:
184 return "ACTION_TYPE_PUSH_NOTIF";
185 case ACTION_TYPE_MAP_PORT_TO_COMP_IN_STREAM:
186 return "ACTION_TYPE_MAP_PORT_TO_COMP_IN_STREAM";
187 case ACTION_TYPE_ADD_STREAM_STATE:
188 return "ACTION_TYPE_ADD_STREAM_STATE";
189 case ACTION_TYPE_SET_STREAM_STATE_IS_ENDED:
190 return "ACTION_TYPE_SET_STREAM_STATE_IS_ENDED";
191 case ACTION_TYPE_SET_STREAM_STATE_CUR_PACKET:
192 return "ACTION_TYPE_SET_STREAM_STATE_CUR_PACKET";
193 default:
194 return "(unknown)";
195 }
196}
197
3230ee6b
PP
198static
199void apply_actions(struct bt_notification_iterator *iterator)
200{
201 size_t i;
202
5af447e5
PP
203 BT_LOGV("Applying notification's iterator current actions: "
204 "count=%u", iterator->actions->len);
205
3230ee6b
PP
206 for (i = 0; i < iterator->actions->len; i++) {
207 struct action *action = &g_array_index(iterator->actions,
208 struct action, i);
209
5af447e5
PP
210 BT_LOGV("Applying action: index=%zu, type=%s",
211 i, action_type_string(action->type));
212
3230ee6b
PP
213 switch (action->type) {
214 case ACTION_TYPE_PUSH_NOTIF:
215 /* Move notification to queue */
216 g_queue_push_head(iterator->queue,
217 action->payload.push_notif.notif);
218 bt_notification_freeze(
219 action->payload.push_notif.notif);
220 action->payload.push_notif.notif = NULL;
221 break;
222 case ACTION_TYPE_MAP_PORT_TO_COMP_IN_STREAM:
223 bt_ctf_stream_map_component_to_port(
224 action->payload.map_port_to_comp_in_stream.stream,
225 action->payload.map_port_to_comp_in_stream.component,
226 action->payload.map_port_to_comp_in_stream.port);
227 break;
228 case ACTION_TYPE_ADD_STREAM_STATE:
229 /* Move stream state to hash table */
230 g_hash_table_insert(iterator->stream_states,
231 action->payload.add_stream_state.stream,
232 action->payload.add_stream_state.stream_state);
233
234 action->payload.add_stream_state.stream_state = NULL;
235 break;
236 case ACTION_TYPE_SET_STREAM_STATE_IS_ENDED:
237 /*
238 * We know that this stream is ended. We need to
239 * remember this as long as the stream exists to
240 * enforce that the same stream does not end
241 * twice.
242 *
243 * Here we add a destroy listener to the stream
244 * which we put after (becomes weak as the hash
245 * table key). If we were the last object to own
246 * this stream, the destroy listener is called
247 * when we call bt_put() which removes this
248 * stream state completely. This is important
249 * because the memory used by this stream object
250 * could be reused for another stream, and they
251 * must have different states.
252 */
253 bt_ctf_stream_add_destroy_listener(
254 action->payload.set_stream_state_is_ended.stream_state->stream,
255 stream_destroy_listener, iterator);
c55a9f58 256 action->payload.set_stream_state_is_ended.stream_state->is_ended = BT_TRUE;
3230ee6b
PP
257 BT_PUT(action->payload.set_stream_state_is_ended.stream_state->stream);
258 break;
259 case ACTION_TYPE_SET_STREAM_STATE_CUR_PACKET:
260 /* Move packet to stream state's current packet */
261 BT_MOVE(action->payload.set_stream_state_cur_packet.stream_state->cur_packet,
262 action->payload.set_stream_state_cur_packet.packet);
263 break;
264 default:
0fbb9a9f 265 abort();
3230ee6b
PP
266 }
267 }
268
269 clear_actions(iterator);
270}
271
272static
273struct stream_state *create_stream_state(struct bt_ctf_stream *stream)
274{
275 struct stream_state *stream_state = g_new0(struct stream_state, 1);
276
277 if (!stream_state) {
5af447e5 278 BT_LOGE_STR("Failed to allocate one stream state.");
3230ee6b
PP
279 goto end;
280 }
281
282 /*
283 * We keep a reference to the stream until we know it's ended
284 * because we need to be able to create an automatic "stream
285 * end" notification when the user's "next" method returns
286 * BT_NOTIFICATION_ITERATOR_STATUS_END.
287 *
288 * We put this reference when the stream is marked as ended.
289 */
290 stream_state->stream = bt_get(stream);
5af447e5
PP
291 BT_LOGV("Created stream state: stream-addr=%p, stream-name=\"%s\", "
292 "stream-state-addr=%p",
293 stream, bt_ctf_stream_get_name(stream), stream_state);
3230ee6b
PP
294
295end:
296 return stream_state;
297}
47e5a032
JG
298
299static
b8a06801 300void bt_notification_iterator_destroy(struct bt_object *obj)
47e5a032 301{
8738a040
JG
302 struct bt_notification_iterator *iterator;
303
b8a06801 304 assert(obj);
d3eb6e8f 305
bd14d768
PP
306 /*
307 * The notification iterator's reference count is 0 if we're
308 * here. Increment it to avoid a double-destroy (possibly
309 * infinitely recursive). This could happen for example if the
310 * notification iterator's finalization function does bt_get()
311 * (or anything that causes bt_get() to be called) on itself
312 * (ref. count goes from 0 to 1), and then bt_put(): the
313 * reference count would go from 1 to 0 again and this function
314 * would be called again.
315 */
316 obj->ref_count.count++;
317 iterator = container_of(obj, struct bt_notification_iterator, base);
5af447e5
PP
318 BT_LOGD("Destroying notification iterator object: addr=%p",
319 iterator);
bd14d768 320 bt_notification_iterator_finalize(iterator);
d3eb6e8f 321
3230ee6b
PP
322 if (iterator->queue) {
323 struct bt_notification *notif;
324
5af447e5
PP
325 BT_LOGD("Putting notifications in queue.");
326
3230ee6b
PP
327 while ((notif = g_queue_pop_tail(iterator->queue))) {
328 bt_put(notif);
329 }
330
331 g_queue_free(iterator->queue);
332 }
333
334 if (iterator->stream_states) {
335 /*
336 * Remove our destroy listener from each stream which
337 * has a state in this iterator. Otherwise the destroy
338 * listener would be called with an invalid/other
339 * notification iterator object.
340 */
341 GHashTableIter ht_iter;
342 gpointer stream_gptr, stream_state_gptr;
343
344 g_hash_table_iter_init(&ht_iter, iterator->stream_states);
345
346 while (g_hash_table_iter_next(&ht_iter, &stream_gptr, &stream_state_gptr)) {
347 assert(stream_gptr);
5af447e5
PP
348
349 BT_LOGD_STR("Removing stream's destroy listener for notification iterator.");
3230ee6b
PP
350 bt_ctf_stream_remove_destroy_listener(
351 (void *) stream_gptr, stream_destroy_listener,
352 iterator);
353 }
354
355 g_hash_table_destroy(iterator->stream_states);
356 }
357
358 if (iterator->actions) {
359 g_array_free(iterator->actions, TRUE);
360 }
361
bd14d768
PP
362 if (iterator->connection) {
363 /*
364 * Remove ourself from the originating connection so
365 * that it does not try to finalize a dangling pointer
366 * later.
367 */
368 bt_connection_remove_iterator(iterator->connection, iterator);
369 }
370
5af447e5 371 BT_LOGD_STR("Putting current notification.");
3230ee6b 372 bt_put(iterator->current_notification);
8738a040 373 g_free(iterator);
47e5a032
JG
374}
375
bd14d768
PP
376BT_HIDDEN
377void bt_notification_iterator_finalize(
378 struct bt_notification_iterator *iterator)
379{
380 struct bt_component_class *comp_class = NULL;
381 bt_component_class_notification_iterator_finalize_method
382 finalize_method = NULL;
383
384 assert(iterator);
385
386 switch (iterator->state) {
387 case BT_NOTIFICATION_ITERATOR_STATE_FINALIZED:
388 case BT_NOTIFICATION_ITERATOR_STATE_FINALIZED_AND_ENDED:
389 /* Already finalized */
5af447e5
PP
390 BT_LOGD("Not finalizing notification iterator: already finalized: "
391 "addr=%p", iterator);
bd14d768
PP
392 return;
393 default:
394 break;
395 }
396
5af447e5
PP
397 BT_LOGD("Finalizing notification iterator: addr=%p", iterator);
398
df14f8af 399 if (iterator->state == BT_NOTIFICATION_ITERATOR_STATE_ENDED) {
5af447e5
PP
400 BT_LOGD("Updating notification iterator's state: "
401 "new-state=BT_NOTIFICATION_ITERATOR_STATE_FINALIZED_AND_ENDED");
df14f8af
MD
402 iterator->state = BT_NOTIFICATION_ITERATOR_STATE_FINALIZED_AND_ENDED;
403 } else {
5af447e5
PP
404 BT_LOGD("Updating notification iterator's state: "
405 "new-state=BT_NOTIFICATION_ITERATOR_STATE_FINALIZED");
df14f8af
MD
406 iterator->state = BT_NOTIFICATION_ITERATOR_STATE_FINALIZED;
407 }
408
bd14d768
PP
409 assert(iterator->upstream_component);
410 comp_class = iterator->upstream_component->class;
411
412 /* Call user-defined destroy method */
413 switch (comp_class->type) {
414 case BT_COMPONENT_CLASS_TYPE_SOURCE:
415 {
416 struct bt_component_class_source *source_class;
417
418 source_class = container_of(comp_class, struct bt_component_class_source, parent);
419 finalize_method = source_class->methods.iterator.finalize;
420 break;
421 }
422 case BT_COMPONENT_CLASS_TYPE_FILTER:
423 {
424 struct bt_component_class_filter *filter_class;
425
426 filter_class = container_of(comp_class, struct bt_component_class_filter, parent);
427 finalize_method = filter_class->methods.iterator.finalize;
428 break;
429 }
430 default:
431 /* Unreachable */
0fbb9a9f 432 abort();
bd14d768
PP
433 }
434
435 if (finalize_method) {
5af447e5
PP
436 BT_LOGD("Calling user's finalization method: addr=%p",
437 iterator);
bd14d768
PP
438 finalize_method(
439 bt_private_notification_iterator_from_notification_iterator(iterator));
440 }
441
bd14d768
PP
442 iterator->upstream_component = NULL;
443 iterator->upstream_port = NULL;
5af447e5 444 BT_LOGD("Finalized notification iterator: addr=%p", iterator);
bd14d768
PP
445}
446
447BT_HIDDEN
448void bt_notification_iterator_set_connection(
449 struct bt_notification_iterator *iterator,
450 struct bt_connection *connection)
451{
452 assert(iterator);
453 iterator->connection = connection;
5af447e5
PP
454 BT_LOGV("Set notification iterator's connection: "
455 "iter-addr=%p, conn-addr=%p", iterator, connection);
bd14d768
PP
456}
457
fa054faf
PP
458static
459int create_subscription_mask_from_notification_types(
460 struct bt_notification_iterator *iterator,
461 const enum bt_notification_type *notif_types)
462{
463 const enum bt_notification_type *notif_type;
464 int ret = 0;
465
466 assert(notif_types);
467 iterator->subscription_mask = 0;
468
469 for (notif_type = notif_types;
470 *notif_type != BT_NOTIFICATION_TYPE_SENTINEL;
471 notif_type++) {
472 switch (*notif_type) {
473 case BT_NOTIFICATION_TYPE_ALL:
474 iterator->subscription_mask |=
475 BT_NOTIFICATION_ITERATOR_NOTIF_TYPE_EVENT |
476 BT_NOTIFICATION_ITERATOR_NOTIF_TYPE_INACTIVITY |
477 BT_NOTIFICATION_ITERATOR_NOTIF_TYPE_STREAM_BEGIN |
478 BT_NOTIFICATION_ITERATOR_NOTIF_TYPE_STREAM_END |
479 BT_NOTIFICATION_ITERATOR_NOTIF_TYPE_PACKET_BEGIN |
480 BT_NOTIFICATION_ITERATOR_NOTIF_TYPE_PACKET_END;
481 break;
482 case BT_NOTIFICATION_TYPE_EVENT:
483 iterator->subscription_mask |= BT_NOTIFICATION_ITERATOR_NOTIF_TYPE_EVENT;
484 break;
485 case BT_NOTIFICATION_TYPE_INACTIVITY:
486 iterator->subscription_mask |= BT_NOTIFICATION_ITERATOR_NOTIF_TYPE_INACTIVITY;
487 break;
488 case BT_NOTIFICATION_TYPE_STREAM_BEGIN:
489 iterator->subscription_mask |= BT_NOTIFICATION_ITERATOR_NOTIF_TYPE_STREAM_BEGIN;
490 break;
491 case BT_NOTIFICATION_TYPE_STREAM_END:
492 iterator->subscription_mask |= BT_NOTIFICATION_ITERATOR_NOTIF_TYPE_STREAM_END;
493 break;
494 case BT_NOTIFICATION_TYPE_PACKET_BEGIN:
495 iterator->subscription_mask |= BT_NOTIFICATION_ITERATOR_NOTIF_TYPE_PACKET_BEGIN;
496 break;
497 case BT_NOTIFICATION_TYPE_PACKET_END:
498 iterator->subscription_mask |= BT_NOTIFICATION_ITERATOR_NOTIF_TYPE_PACKET_END;
499 break;
500 default:
501 ret = -1;
502 goto end;
503 }
5af447e5
PP
504
505 BT_LOGV("Added notification type to subscription mask: "
506 "type=%s, mask=%x",
507 bt_notification_type_string(*notif_type),
508 iterator->subscription_mask);
fa054faf
PP
509 }
510
511end:
512 return ret;
513}
514
47e5a032
JG
515BT_HIDDEN
516struct bt_notification_iterator *bt_notification_iterator_create(
3230ee6b 517 struct bt_component *upstream_comp,
fa054faf 518 struct bt_port *upstream_port,
bd14d768
PP
519 const enum bt_notification_type *notification_types,
520 struct bt_connection *connection)
47e5a032 521{
d3e4dcd8 522 enum bt_component_class_type type;
47e5a032
JG
523 struct bt_notification_iterator *iterator = NULL;
524
3230ee6b
PP
525 assert(upstream_comp);
526 assert(upstream_port);
fa054faf 527 assert(notification_types);
3230ee6b 528 assert(bt_port_is_connected(upstream_port));
5af447e5
PP
529 BT_LOGD("Creating notification iterator: "
530 "upstream-comp-addr=%p, upstream-comp-name=\"%s\", "
531 "upstream-port-addr=%p, upstream-port-name=\"%s\", "
532 "conn-addr=%p",
533 upstream_comp, bt_component_get_name(upstream_comp),
534 upstream_port, bt_port_get_name(upstream_port),
535 connection);
3230ee6b 536 type = bt_component_get_class_type(upstream_comp);
ef2f7566
PP
537 assert(type == BT_COMPONENT_CLASS_TYPE_SOURCE ||
538 type == BT_COMPONENT_CLASS_TYPE_FILTER);
47e5a032
JG
539 iterator = g_new0(struct bt_notification_iterator, 1);
540 if (!iterator) {
5af447e5 541 BT_LOGE_STR("Failed to allocate one notification iterator.");
3230ee6b 542 goto error;
47e5a032
JG
543 }
544
b8a06801 545 bt_object_init(iterator, bt_notification_iterator_destroy);
3230ee6b 546
fa054faf
PP
547 if (create_subscription_mask_from_notification_types(iterator,
548 notification_types)) {
5af447e5 549 BT_LOGW_STR("Cannot create subscription mask from notification types.");
fa054faf
PP
550 goto error;
551 }
552
3230ee6b
PP
553 iterator->stream_states = g_hash_table_new_full(g_direct_hash,
554 g_direct_equal, NULL, (GDestroyNotify) destroy_stream_state);
555 if (!iterator->stream_states) {
5af447e5 556 BT_LOGE_STR("Failed to allocate a GHashTable.");
3230ee6b
PP
557 goto error;
558 }
559
560 iterator->queue = g_queue_new();
561 if (!iterator->queue) {
5af447e5 562 BT_LOGE_STR("Failed to allocate a GQueue.");
3230ee6b
PP
563 goto error;
564 }
565
566 iterator->actions = g_array_new(FALSE, FALSE, sizeof(struct action));
567 if (!iterator->actions) {
5af447e5 568 BT_LOGE_STR("Failed to allocate a GArray.");
3230ee6b
PP
569 goto error;
570 }
571
bd14d768
PP
572 iterator->upstream_component = upstream_comp;
573 iterator->upstream_port = upstream_port;
574 iterator->connection = connection;
575 iterator->state = BT_NOTIFICATION_ITERATOR_STATE_ACTIVE;
5af447e5
PP
576 BT_LOGD("Created notification iterator: "
577 "upstream-comp-addr=%p, upstream-comp-name=\"%s\", "
578 "upstream-port-addr=%p, upstream-port-name=\"%s\", "
579 "conn-addr=%p, iter-addr=%p",
580 upstream_comp, bt_component_get_name(upstream_comp),
581 upstream_port, bt_port_get_name(upstream_port),
582 connection, iterator);
3230ee6b
PP
583 goto end;
584
585error:
586 BT_PUT(iterator);
587
47e5a032
JG
588end:
589 return iterator;
590}
591
890882ef
PP
592void *bt_private_notification_iterator_get_user_data(
593 struct bt_private_notification_iterator *private_iterator)
ea8d3e58 594{
890882ef
PP
595 struct bt_notification_iterator *iterator =
596 bt_notification_iterator_from_private(private_iterator);
597
ea8d3e58
JG
598 return iterator ? iterator->user_data : NULL;
599}
600
601enum bt_notification_iterator_status
890882ef
PP
602bt_private_notification_iterator_set_user_data(
603 struct bt_private_notification_iterator *private_iterator,
604 void *data)
ea8d3e58
JG
605{
606 enum bt_notification_iterator_status ret =
607 BT_NOTIFICATION_ITERATOR_STATUS_OK;
890882ef
PP
608 struct bt_notification_iterator *iterator =
609 bt_notification_iterator_from_private(private_iterator);
ea8d3e58 610
d3eb6e8f 611 if (!iterator) {
5af447e5 612 BT_LOGW_STR("Invalid parameter: notification iterator is NULL.");
fe8ad2b6 613 ret = BT_NOTIFICATION_ITERATOR_STATUS_INVALID;
ea8d3e58
JG
614 goto end;
615 }
616
617 iterator->user_data = data;
5af447e5
PP
618 BT_LOGV("Set notification iterator's user data: "
619 "iter-addr=%p, user-data-addr=%p", iterator, data);
620
8738a040
JG
621end:
622 return ret;
623}
413bc2c4 624
53d45b87
JG
625struct bt_notification *bt_notification_iterator_get_notification(
626 struct bt_notification_iterator *iterator)
627{
41a2b7ae 628 struct bt_notification *notification = NULL;
d3eb6e8f 629
41a2b7ae 630 if (!iterator) {
5af447e5 631 BT_LOGW_STR("Invalid parameter: notification iterator is NULL.");
41a2b7ae 632 goto end;
d3eb6e8f 633 }
d3eb6e8f 634
41a2b7ae 635 notification = bt_get(iterator->current_notification);
d3eb6e8f 636
41a2b7ae
PP
637end:
638 return notification;
53d45b87
JG
639}
640
fa054faf
PP
641static
642enum bt_notification_iterator_notif_type
643bt_notification_iterator_notif_type_from_notif_type(
644 enum bt_notification_type notif_type)
645{
646 enum bt_notification_iterator_notif_type iter_notif_type;
647
648 switch (notif_type) {
649 case BT_NOTIFICATION_TYPE_EVENT:
650 iter_notif_type = BT_NOTIFICATION_ITERATOR_NOTIF_TYPE_EVENT;
651 break;
652 case BT_NOTIFICATION_TYPE_INACTIVITY:
653 iter_notif_type = BT_NOTIFICATION_ITERATOR_NOTIF_TYPE_INACTIVITY;
654 break;
655 case BT_NOTIFICATION_TYPE_STREAM_BEGIN:
656 iter_notif_type = BT_NOTIFICATION_ITERATOR_NOTIF_TYPE_STREAM_BEGIN;
657 break;
658 case BT_NOTIFICATION_TYPE_STREAM_END:
659 iter_notif_type = BT_NOTIFICATION_ITERATOR_NOTIF_TYPE_STREAM_END;
660 break;
661 case BT_NOTIFICATION_TYPE_PACKET_BEGIN:
662 iter_notif_type = BT_NOTIFICATION_ITERATOR_NOTIF_TYPE_PACKET_BEGIN;
663 break;
664 case BT_NOTIFICATION_TYPE_PACKET_END:
665 iter_notif_type = BT_NOTIFICATION_ITERATOR_NOTIF_TYPE_PACKET_END;
666 break;
667 default:
0fbb9a9f 668 abort();
fa054faf
PP
669 }
670
671 return iter_notif_type;
672}
673
3230ee6b 674static
c55a9f58 675bt_bool validate_notification(struct bt_notification_iterator *iterator,
3230ee6b
PP
676 struct bt_notification *notif,
677 struct bt_ctf_stream *notif_stream,
678 struct bt_ctf_packet *notif_packet)
679{
c55a9f58 680 bt_bool is_valid = BT_TRUE;
3230ee6b
PP
681 struct stream_state *stream_state;
682 struct bt_port *stream_comp_cur_port;
683
684 assert(notif_stream);
685 stream_comp_cur_port =
686 bt_ctf_stream_port_for_component(notif_stream,
687 iterator->upstream_component);
688 if (!stream_comp_cur_port) {
689 /*
690 * This is the first time this notification iterator
691 * bumps into this stream. Add an action to map the
692 * iterator's upstream component to the iterator's
693 * upstream port in this stream.
694 */
695 struct action action = {
696 .type = ACTION_TYPE_MAP_PORT_TO_COMP_IN_STREAM,
697 .payload.map_port_to_comp_in_stream = {
698 .stream = bt_get(notif_stream),
699 .component = bt_get(iterator->upstream_component),
700 .port = bt_get(iterator->upstream_port),
701 },
702 };
703
704 add_action(iterator, &action);
705 } else {
706 if (stream_comp_cur_port != iterator->upstream_port) {
707 /*
708 * It looks like two different ports of the same
709 * component are emitting notifications which
710 * have references to the same stream. This is
711 * bad: the API guarantees that it can never
712 * happen.
713 */
5af447e5
PP
714 BT_LOGW("Two different ports of the same component are emitting notifications which refer to the same stream: "
715 "stream-addr=%p, stream-name=\"%s\", "
716 "stream-comp-cur-port-addr=%p, "
717 "stream-comp-cur-port-name=%p, "
718 "iter-upstream-port-addr=%p, "
719 "iter-upstream-port-name=%s",
720 notif_stream,
721 bt_ctf_stream_get_name(notif_stream),
722 stream_comp_cur_port,
723 bt_port_get_name(stream_comp_cur_port),
724 iterator->upstream_port,
725 bt_port_get_name(iterator->upstream_port));
c55a9f58 726 is_valid = BT_FALSE;
3230ee6b
PP
727 goto end;
728 }
729
730 }
731
732 stream_state = g_hash_table_lookup(iterator->stream_states,
733 notif_stream);
734 if (stream_state) {
5af447e5
PP
735 BT_LOGV("Stream state already exists: "
736 "stream-addr=%p, stream-name=\"%s\", "
737 "stream-state-addr=%p",
738 notif_stream,
739 bt_ctf_stream_get_name(notif_stream), stream_state);
740
3230ee6b
PP
741 if (stream_state->is_ended) {
742 /*
743 * There's a new notification which has a
744 * reference to a stream which, from this
745 * iterator's point of view, is ended ("end of
746 * stream" notification was returned). This is
747 * bad: the API guarantees that it can never
748 * happen.
749 */
5af447e5
PP
750 BT_LOGW("Stream is already ended: "
751 "stream-addr=%p, stream-name=\"%s\"",
752 notif_stream,
753 bt_ctf_stream_get_name(notif_stream));
c55a9f58 754 is_valid = BT_FALSE;
3230ee6b
PP
755 goto end;
756 }
757
758 switch (notif->type) {
759 case BT_NOTIFICATION_TYPE_STREAM_BEGIN:
760 /*
761 * We already have a stream state, which means
762 * we already returned a "stream begin"
763 * notification: this is an invalid duplicate.
764 */
5af447e5
PP
765 BT_LOGW("Duplicate stream beginning notification: "
766 "stream-addr=%p, stream-name=\"%s\"",
767 notif_stream,
768 bt_ctf_stream_get_name(notif_stream));
c55a9f58 769 is_valid = BT_FALSE;
3230ee6b
PP
770 goto end;
771 case BT_NOTIFICATION_TYPE_PACKET_BEGIN:
772 if (notif_packet == stream_state->cur_packet) {
773 /* Duplicate "packet begin" notification */
5af447e5
PP
774 BT_LOGW("Duplicate stream beginning notification: "
775 "stream-addr=%p, stream-name=\"%s\", "
776 "packet-addr=%p",
777 notif_stream,
778 bt_ctf_stream_get_name(notif_stream),
779 notif_packet);
c55a9f58 780 is_valid = BT_FALSE;
3230ee6b
PP
781 goto end;
782 }
783 break;
784 default:
785 break;
786 }
787 }
788
789end:
790 return is_valid;
791}
792
fa054faf 793static
c55a9f58 794bt_bool is_subscribed_to_notification_type(struct bt_notification_iterator *iterator,
fa054faf
PP
795 enum bt_notification_type notif_type)
796{
797 uint32_t iter_notif_type =
798 (uint32_t) bt_notification_iterator_notif_type_from_notif_type(
799 notif_type);
800
c55a9f58 801 return (iter_notif_type & iterator->subscription_mask) ? BT_TRUE : BT_FALSE;
fa054faf
PP
802}
803
3230ee6b
PP
804static
805void add_action_push_notif(struct bt_notification_iterator *iterator,
806 struct bt_notification *notif)
807{
808 struct action action = {
809 .type = ACTION_TYPE_PUSH_NOTIF,
3230ee6b
PP
810 };
811
812 assert(notif);
fa054faf
PP
813
814 if (!is_subscribed_to_notification_type(iterator, notif->type)) {
815 return;
816 }
817
818 action.payload.push_notif.notif = bt_get(notif);
3230ee6b 819 add_action(iterator, &action);
5af447e5 820 BT_LOGV("Added \"push notification\" action: notif-addr=%p", notif);
3230ee6b
PP
821}
822
823static
824int add_action_push_notif_stream_begin(
825 struct bt_notification_iterator *iterator,
826 struct bt_ctf_stream *stream)
827{
828 int ret = 0;
829 struct bt_notification *stream_begin_notif = NULL;
830
fa054faf
PP
831 if (!is_subscribed_to_notification_type(iterator,
832 BT_NOTIFICATION_TYPE_STREAM_BEGIN)) {
5af447e5
PP
833 BT_LOGV("Not adding \"push stream beginning notification\" action: "
834 "notification iterator is not subscribed: addr=%p",
835 iterator);
fa054faf
PP
836 goto end;
837 }
838
3230ee6b
PP
839 assert(stream);
840 stream_begin_notif = bt_notification_stream_begin_create(stream);
841 if (!stream_begin_notif) {
5af447e5 842 BT_LOGE_STR("Cannot create stream beginning notification.");
3230ee6b
PP
843 goto error;
844 }
845
846 add_action_push_notif(iterator, stream_begin_notif);
5af447e5
PP
847 BT_LOGV("Added \"push stream beginning notification\" action: "
848 "stream-addr=%p, stream-name=\"%s\"",
849 stream, bt_ctf_stream_get_name(stream));
3230ee6b
PP
850 goto end;
851
852error:
853 ret = -1;
854
855end:
856 bt_put(stream_begin_notif);
857 return ret;
858}
859
860static
861int add_action_push_notif_stream_end(
862 struct bt_notification_iterator *iterator,
863 struct bt_ctf_stream *stream)
864{
865 int ret = 0;
866 struct bt_notification *stream_end_notif = NULL;
867
fa054faf
PP
868 if (!is_subscribed_to_notification_type(iterator,
869 BT_NOTIFICATION_TYPE_STREAM_END)) {
5af447e5
PP
870 BT_LOGV("Not adding \"push stream end notification\" action: "
871 "notification iterator is not subscribed: addr=%p",
872 iterator);
fa054faf
PP
873 goto end;
874 }
875
3230ee6b
PP
876 assert(stream);
877 stream_end_notif = bt_notification_stream_end_create(stream);
878 if (!stream_end_notif) {
5af447e5 879 BT_LOGE_STR("Cannot create stream end notification.");
3230ee6b
PP
880 goto error;
881 }
882
883 add_action_push_notif(iterator, stream_end_notif);
5af447e5
PP
884 BT_LOGV("Added \"push stream end notification\" action: "
885 "stream-addr=%p, stream-name=\"%s\"",
886 stream, bt_ctf_stream_get_name(stream));
3230ee6b
PP
887 goto end;
888
889error:
890 ret = -1;
891
892end:
893 bt_put(stream_end_notif);
894 return ret;
895}
896
897static
898int add_action_push_notif_packet_begin(
899 struct bt_notification_iterator *iterator,
900 struct bt_ctf_packet *packet)
901{
902 int ret = 0;
903 struct bt_notification *packet_begin_notif = NULL;
904
fa054faf
PP
905 if (!is_subscribed_to_notification_type(iterator,
906 BT_NOTIFICATION_TYPE_PACKET_BEGIN)) {
5af447e5
PP
907 BT_LOGV("Not adding \"push packet beginning notification\" action: "
908 "notification iterator is not subscribed: addr=%p",
909 iterator);
fa054faf
PP
910 goto end;
911 }
912
3230ee6b
PP
913 assert(packet);
914 packet_begin_notif = bt_notification_packet_begin_create(packet);
915 if (!packet_begin_notif) {
5af447e5 916 BT_LOGE_STR("Cannot create packet beginning notification.");
3230ee6b
PP
917 goto error;
918 }
919
920 add_action_push_notif(iterator, packet_begin_notif);
5af447e5
PP
921 BT_LOGV("Added \"push packet beginning notification\" action: "
922 "packet-addr=%p", packet);
3230ee6b
PP
923 goto end;
924
925error:
926 ret = -1;
927
928end:
929 bt_put(packet_begin_notif);
930 return ret;
931}
932
933static
934int add_action_push_notif_packet_end(
935 struct bt_notification_iterator *iterator,
936 struct bt_ctf_packet *packet)
937{
938 int ret = 0;
939 struct bt_notification *packet_end_notif = NULL;
940
fa054faf
PP
941 if (!is_subscribed_to_notification_type(iterator,
942 BT_NOTIFICATION_TYPE_PACKET_END)) {
5af447e5
PP
943 BT_LOGV("Not adding \"push packet end notification\" action: "
944 "notification iterator is not subscribed: addr=%p",
945 iterator);
fa054faf
PP
946 goto end;
947 }
948
3230ee6b
PP
949 assert(packet);
950 packet_end_notif = bt_notification_packet_end_create(packet);
951 if (!packet_end_notif) {
5af447e5 952 BT_LOGE_STR("Cannot create packet end notification.");
3230ee6b
PP
953 goto error;
954 }
955
956 add_action_push_notif(iterator, packet_end_notif);
5af447e5
PP
957 BT_LOGV("Added \"push packet end notification\" action: "
958 "packet-addr=%p", packet);
3230ee6b
PP
959 goto end;
960
961error:
962 ret = -1;
963
964end:
965 bt_put(packet_end_notif);
966 return ret;
967}
968
969static
970void add_action_set_stream_state_is_ended(
971 struct bt_notification_iterator *iterator,
972 struct stream_state *stream_state)
973{
974 struct action action = {
975 .type = ACTION_TYPE_SET_STREAM_STATE_IS_ENDED,
976 .payload.set_stream_state_is_ended = {
977 .stream_state = stream_state,
978 },
979 };
980
981 assert(stream_state);
982 add_action(iterator, &action);
5af447e5
PP
983 BT_LOGV("Added \"set stream state's ended\" action: "
984 "stream-state-addr=%p", stream_state);
3230ee6b
PP
985}
986
987static
988void add_action_set_stream_state_cur_packet(
989 struct bt_notification_iterator *iterator,
990 struct stream_state *stream_state,
991 struct bt_ctf_packet *packet)
992{
993 struct action action = {
994 .type = ACTION_TYPE_SET_STREAM_STATE_CUR_PACKET,
995 .payload.set_stream_state_cur_packet = {
996 .stream_state = stream_state,
997 .packet = bt_get(packet),
998 },
999 };
1000
1001 assert(stream_state);
1002 add_action(iterator, &action);
5af447e5
PP
1003 BT_LOGV("Added \"set stream state's current packet\" action: "
1004 "stream-state-addr=%p, packet-addr=%p",
1005 stream_state, packet);
3230ee6b
PP
1006}
1007
1008static
1009int ensure_stream_state_exists(struct bt_notification_iterator *iterator,
1010 struct bt_notification *stream_begin_notif,
1011 struct bt_ctf_stream *notif_stream,
1012 struct stream_state **stream_state)
1013{
1014 int ret = 0;
1015
1016 if (!notif_stream) {
1017 /*
1018 * The notification does not reference any stream: no
1019 * need to get or create a stream state.
1020 */
1021 goto end;
1022 }
1023
1024 *stream_state = g_hash_table_lookup(iterator->stream_states,
1025 notif_stream);
1026 if (!*stream_state) {
1027 /*
1028 * This iterator did not bump into this stream yet:
1029 * create a stream state and a "stream begin"
1030 * notification.
1031 */
1032 struct action action = {
1033 .type = ACTION_TYPE_ADD_STREAM_STATE,
1034 .payload.add_stream_state = {
1035 .stream = bt_get(notif_stream),
1036 .stream_state = NULL,
1037 },
1038 };
1039
1040 *stream_state = create_stream_state(notif_stream);
1041 if (!stream_state) {
5af447e5 1042 BT_LOGE_STR("Cannot create stream state.");
3230ee6b
PP
1043 goto error;
1044 }
1045
1046 action.payload.add_stream_state.stream_state =
1047 *stream_state;
1048 add_action(iterator, &action);
1049
1050 if (stream_begin_notif) {
1051 add_action_push_notif(iterator, stream_begin_notif);
1052 } else {
1053 ret = add_action_push_notif_stream_begin(iterator,
1054 notif_stream);
1055 if (ret) {
5af447e5 1056 BT_LOGE_STR("Cannot add \"push stream beginning notification\" action.");
3230ee6b
PP
1057 goto error;
1058 }
1059 }
1060 }
1061
1062 goto end;
1063
1064error:
1065 destroy_stream_state(*stream_state);
1066 ret = -1;
1067
1068end:
1069 return ret;
1070}
1071
1072static
1073int handle_packet_switch(struct bt_notification_iterator *iterator,
1074 struct bt_notification *packet_begin_notif,
1075 struct bt_ctf_packet *new_packet,
1076 struct stream_state *stream_state)
1077{
1078 int ret = 0;
1079
1080 if (stream_state->cur_packet == new_packet) {
1081 goto end;
1082 }
1083
5af447e5
PP
1084 BT_LOGV("Handling packet switch: "
1085 "cur-packet-addr=%p, new-packet-addr=%p",
1086 stream_state->cur_packet, new_packet);
1087
3230ee6b
PP
1088 if (stream_state->cur_packet) {
1089 /* End of the current packet */
1090 ret = add_action_push_notif_packet_end(iterator,
1091 stream_state->cur_packet);
1092 if (ret) {
5af447e5 1093 BT_LOGE_STR("Cannot add \"push packet end notification\" action.");
3230ee6b
PP
1094 goto error;
1095 }
1096 }
1097
1098 /* Beginning of the new packet */
1099 if (packet_begin_notif) {
1100 add_action_push_notif(iterator, packet_begin_notif);
1101 } else if (new_packet) {
1102 ret = add_action_push_notif_packet_begin(iterator,
1103 new_packet);
1104 if (ret) {
5af447e5 1105 BT_LOGE_STR("Cannot add \"push packet beginning notification\" action.");
3230ee6b
PP
1106 goto error;
1107 }
1108 }
1109
1110 add_action_set_stream_state_cur_packet(iterator, stream_state,
1111 new_packet);
1112 goto end;
1113
1114error:
1115 ret = -1;
1116
1117end:
1118 return ret;
1119}
1120
1121static
1122int handle_notif_stream_begin(
1123 struct bt_notification_iterator *iterator,
1124 struct bt_notification *notif,
1125 struct bt_ctf_stream *notif_stream)
1126{
1127 int ret = 0;
1128 struct stream_state *stream_state;
1129
1130 assert(notif->type == BT_NOTIFICATION_TYPE_STREAM_BEGIN);
1131 assert(notif_stream);
1132 ret = ensure_stream_state_exists(iterator, notif, notif_stream,
1133 &stream_state);
1134 if (ret) {
5af447e5 1135 BT_LOGE_STR("Cannot ensure that stream state exists.");
3230ee6b
PP
1136 goto error;
1137 }
1138
1139 goto end;
1140
1141error:
1142 ret = -1;
1143
1144end:
1145 return ret;
1146}
1147
1148static
1149int handle_notif_stream_end(
1150 struct bt_notification_iterator *iterator,
1151 struct bt_notification *notif,
1152 struct bt_ctf_stream *notif_stream)
1153{
1154 int ret = 0;
1155 struct stream_state *stream_state;
1156
1157 assert(notif->type == BT_NOTIFICATION_TYPE_STREAM_END);
1158 assert(notif_stream);
1159 ret = ensure_stream_state_exists(iterator, NULL, notif_stream,
1160 &stream_state);
1161 if (ret) {
5af447e5 1162 BT_LOGE_STR("Cannot ensure that stream state exists.");
3230ee6b
PP
1163 goto error;
1164 }
1165
1166 ret = handle_packet_switch(iterator, NULL, NULL, stream_state);
1167 if (ret) {
5af447e5 1168 BT_LOGE_STR("Cannot handle packet switch.");
3230ee6b
PP
1169 goto error;
1170 }
1171
1172 add_action_push_notif(iterator, notif);
1173 add_action_set_stream_state_is_ended(iterator, stream_state);
1174 goto end;
1175
1176error:
1177 ret = -1;
1178
1179end:
1180 return ret;
1181}
1182
1183static
1184int handle_notif_packet_begin(
1185 struct bt_notification_iterator *iterator,
1186 struct bt_notification *notif,
1187 struct bt_ctf_stream *notif_stream,
1188 struct bt_ctf_packet *notif_packet)
1189{
1190 int ret = 0;
1191 struct stream_state *stream_state;
1192
1193 assert(notif->type == BT_NOTIFICATION_TYPE_PACKET_BEGIN);
1194 assert(notif_packet);
1195 ret = ensure_stream_state_exists(iterator, NULL, notif_stream,
1196 &stream_state);
1197 if (ret) {
5af447e5 1198 BT_LOGE_STR("Cannot ensure that stream state exists.");
3230ee6b
PP
1199 goto error;
1200 }
1201
1202 ret = handle_packet_switch(iterator, notif, notif_packet, stream_state);
1203 if (ret) {
5af447e5 1204 BT_LOGE_STR("Cannot handle packet switch.");
3230ee6b
PP
1205 goto error;
1206 }
1207
1208 goto end;
1209
1210error:
1211 ret = -1;
1212
1213end:
1214 return ret;
1215}
1216
1217static
1218int handle_notif_packet_end(
1219 struct bt_notification_iterator *iterator,
1220 struct bt_notification *notif,
1221 struct bt_ctf_stream *notif_stream,
1222 struct bt_ctf_packet *notif_packet)
1223{
1224 int ret = 0;
1225 struct stream_state *stream_state;
1226
1227 assert(notif->type == BT_NOTIFICATION_TYPE_PACKET_END);
1228 assert(notif_packet);
1229 ret = ensure_stream_state_exists(iterator, NULL, notif_stream,
1230 &stream_state);
1231 if (ret) {
5af447e5 1232 BT_LOGE_STR("Cannot ensure that stream state exists.");
3230ee6b
PP
1233 goto error;
1234 }
1235
1236 ret = handle_packet_switch(iterator, NULL, notif_packet, stream_state);
1237 if (ret) {
5af447e5 1238 BT_LOGE_STR("Cannot handle packet switch.");
3230ee6b
PP
1239 goto error;
1240 }
1241
1242 /* End of the current packet */
1243 add_action_push_notif(iterator, notif);
1244 add_action_set_stream_state_cur_packet(iterator, stream_state, NULL);
1245 goto end;
1246
1247error:
1248 ret = -1;
1249
1250end:
1251 return ret;
1252}
1253
1254static
1255int handle_notif_event(
1256 struct bt_notification_iterator *iterator,
1257 struct bt_notification *notif,
1258 struct bt_ctf_stream *notif_stream,
1259 struct bt_ctf_packet *notif_packet)
1260{
1261 int ret = 0;
1262 struct stream_state *stream_state;
1263
1264 assert(notif->type == BT_NOTIFICATION_TYPE_EVENT);
1265 assert(notif_packet);
1266 ret = ensure_stream_state_exists(iterator, NULL, notif_stream,
1267 &stream_state);
1268 if (ret) {
5af447e5 1269 BT_LOGE_STR("Cannot ensure that stream state exists.");
3230ee6b
PP
1270 goto error;
1271 }
1272
1273 ret = handle_packet_switch(iterator, NULL, notif_packet, stream_state);
1274 if (ret) {
5af447e5 1275 BT_LOGE_STR("Cannot handle packet switch.");
3230ee6b
PP
1276 goto error;
1277 }
1278
1279 add_action_push_notif(iterator, notif);
1280 goto end;
1281
1282error:
1283 ret = -1;
1284
1285end:
1286 return ret;
1287}
1288
1289static
1290int enqueue_notification_and_automatic(
1291 struct bt_notification_iterator *iterator,
1292 struct bt_notification *notif)
1293{
1294 int ret = 0;
1295 struct bt_ctf_event *notif_event = NULL;
1296 struct bt_ctf_stream *notif_stream = NULL;
1297 struct bt_ctf_packet *notif_packet = NULL;
1298
1299 assert(notif);
1300
5af447e5
PP
1301 BT_LOGV("Enqueuing user notification and automatic notifications: "
1302 "iter-addr=%p, notif-addr=%p", iterator, notif);
1303
fa054faf
PP
1304 // TODO: Skip most of this if the iterator is only subscribed
1305 // to event/inactivity notifications.
1306
3230ee6b
PP
1307 /* Get the stream and packet referred by the notification */
1308 switch (notif->type) {
1309 case BT_NOTIFICATION_TYPE_EVENT:
1310 notif_event = bt_notification_event_borrow_event(notif);
1311 assert(notif_event);
1312 notif_packet = bt_ctf_event_borrow_packet(notif_event);
1313 assert(notif_packet);
1314 break;
1315 case BT_NOTIFICATION_TYPE_STREAM_BEGIN:
1316 notif_stream =
1317 bt_notification_stream_begin_borrow_stream(notif);
1318 assert(notif_stream);
1319 break;
1320 case BT_NOTIFICATION_TYPE_STREAM_END:
1321 notif_stream = bt_notification_stream_end_borrow_stream(notif);
1322 assert(notif_stream);
1323 break;
1324 case BT_NOTIFICATION_TYPE_PACKET_BEGIN:
1325 notif_packet =
1326 bt_notification_packet_begin_borrow_packet(notif);
1327 assert(notif_packet);
1328 break;
1329 case BT_NOTIFICATION_TYPE_PACKET_END:
1330 notif_packet = bt_notification_packet_end_borrow_packet(notif);
1331 assert(notif_packet);
1332 break;
1333 case BT_NOTIFICATION_TYPE_INACTIVITY:
1334 /* Always valid */
7cdc2bab 1335 goto handle_notif;
3230ee6b
PP
1336 default:
1337 /*
1338 * Invalid type of notification. Only the notification
1339 * types above are allowed to be returned by a user
1340 * component.
1341 */
1342 goto error;
1343 }
1344
1345 if (notif_packet) {
1346 notif_stream = bt_ctf_packet_borrow_stream(notif_packet);
1347 assert(notif_stream);
1348 }
1349
1350 if (!notif_stream) {
1351 /*
1352 * The notification has no reference to a stream: it
1353 * cannot cause the creation of automatic notifications.
1354 */
5af447e5 1355 BT_LOGV_STR("Notification has no reference to any stream: skipping automatic notification generation.");
3230ee6b
PP
1356 goto end;
1357 }
1358
1359 if (!validate_notification(iterator, notif, notif_stream,
1360 notif_packet)) {
5af447e5 1361 BT_LOGW_STR("Invalid notification.");
3230ee6b
PP
1362 goto error;
1363 }
1364
7cdc2bab 1365handle_notif:
3230ee6b
PP
1366 switch (notif->type) {
1367 case BT_NOTIFICATION_TYPE_EVENT:
1368 ret = handle_notif_event(iterator, notif, notif_stream,
1369 notif_packet);
1370 break;
1371 case BT_NOTIFICATION_TYPE_STREAM_BEGIN:
1372 ret = handle_notif_stream_begin(iterator, notif, notif_stream);
1373 break;
1374 case BT_NOTIFICATION_TYPE_STREAM_END:
1375 ret = handle_notif_stream_end(iterator, notif, notif_stream);
1376 break;
1377 case BT_NOTIFICATION_TYPE_PACKET_BEGIN:
1378 ret = handle_notif_packet_begin(iterator, notif, notif_stream,
1379 notif_packet);
1380 break;
1381 case BT_NOTIFICATION_TYPE_PACKET_END:
1382 ret = handle_notif_packet_end(iterator, notif, notif_stream,
1383 notif_packet);
1384 break;
7cdc2bab
MD
1385 case BT_NOTIFICATION_TYPE_INACTIVITY:
1386 add_action_push_notif(iterator, notif);
1387 break;
3230ee6b
PP
1388 default:
1389 break;
1390 }
1391
1392 if (ret) {
5af447e5 1393 BT_LOGW_STR("Failed to handle notification for automatic notification generation.");
3230ee6b
PP
1394 goto error;
1395 }
1396
1397 apply_actions(iterator);
5af447e5
PP
1398 BT_LOGV("Enqueued user notification and automatic notifications: "
1399 "iter-addr=%p, notif-addr=%p", iterator, notif);
3230ee6b
PP
1400 goto end;
1401
1402error:
1403 ret = -1;
1404
1405end:
1406 return ret;
1407}
1408
1409static
1410int handle_end(struct bt_notification_iterator *iterator)
1411{
1412 GHashTableIter stream_state_iter;
1413 gpointer stream_gptr, stream_state_gptr;
1414 int ret = 0;
1415
5af447e5
PP
1416 BT_LOGV("Handling end of iteration: addr=%p", iterator);
1417
3230ee6b
PP
1418 /*
1419 * Emit a "stream end" notification for each non-ended stream
1420 * known by this iterator and mark them as ended.
1421 */
1422 g_hash_table_iter_init(&stream_state_iter, iterator->stream_states);
1423
1424 while (g_hash_table_iter_next(&stream_state_iter, &stream_gptr,
1425 &stream_state_gptr)) {
1426 struct stream_state *stream_state = stream_state_gptr;
1427
1428 assert(stream_state_gptr);
1429
1430 if (stream_state->is_ended) {
1431 continue;
1432 }
1433
1434 ret = handle_packet_switch(iterator, NULL, NULL, stream_state);
1435 if (ret) {
5af447e5 1436 BT_LOGE_STR("Cannot handle packet switch.");
3230ee6b
PP
1437 goto error;
1438 }
1439
1440 ret = add_action_push_notif_stream_end(iterator, stream_gptr);
1441 if (ret) {
5af447e5 1442 BT_LOGE_STR("Cannot add \"push stream end notification\" action.");
3230ee6b
PP
1443 goto error;
1444 }
1445
1446 add_action_set_stream_state_is_ended(iterator, stream_state);
1447 }
1448
1449 apply_actions(iterator);
5af447e5 1450 BT_LOGV("Handled end of iteration: addr=%p", iterator);
3230ee6b
PP
1451 goto end;
1452
1453error:
1454 ret = -1;
1455
1456end:
1457 return ret;
1458}
1459
1460static
1461enum bt_notification_iterator_status ensure_queue_has_notifications(
1462 struct bt_notification_iterator *iterator)
53d45b87 1463{
890882ef
PP
1464 struct bt_private_notification_iterator *priv_iterator =
1465 bt_private_notification_iterator_from_notification_iterator(iterator);
d3eb6e8f 1466 bt_component_class_notification_iterator_next_method next_method = NULL;
fe8ad2b6
PP
1467 struct bt_notification_iterator_next_return next_return = {
1468 .status = BT_NOTIFICATION_ITERATOR_STATUS_OK,
1469 .notification = NULL,
1470 };
3230ee6b
PP
1471 enum bt_notification_iterator_status status =
1472 BT_NOTIFICATION_ITERATOR_STATUS_OK;
1473 int ret;
41a2b7ae 1474
3230ee6b 1475 assert(iterator);
5af447e5 1476 BT_LOGD("Ensuring that notification iterator's queue has at least one notification: "
8f9d7550
PP
1477 "iter-addr=%p, queue-size=%u, iter-state=%s",
1478 iterator, iterator->queue->length,
1479 bt_notification_iterator_state_string(iterator->state));
3230ee6b
PP
1480
1481 if (iterator->queue->length > 0) {
8f9d7550
PP
1482 /*
1483 * We already have enough. Even if this notification
1484 * iterator is finalized, its user can still flush its
1485 * current queue's content by calling its "next" method
1486 * since this content is local and has no impact on what
1487 * used to be the iterator's upstream component.
1488 */
5af447e5 1489 BT_LOGD_STR("Queue already has at least one notification.");
3230ee6b
PP
1490 goto end;
1491 }
1492
bd14d768
PP
1493 switch (iterator->state) {
1494 case BT_NOTIFICATION_ITERATOR_STATE_FINALIZED_AND_ENDED:
8f9d7550 1495 case BT_NOTIFICATION_ITERATOR_STATE_FINALIZED:
5af447e5 1496 BT_LOGD_STR("Notification iterator's \"next\" called, but it is finalized.");
bd14d768
PP
1497 status = BT_NOTIFICATION_ITERATOR_STATUS_CANCELED;
1498 goto end;
1499 case BT_NOTIFICATION_ITERATOR_STATE_ENDED:
5af447e5 1500 BT_LOGD_STR("Notification iterator is ended.");
3230ee6b 1501 status = BT_NOTIFICATION_ITERATOR_STATUS_END;
41a2b7ae 1502 goto end;
bd14d768
PP
1503 default:
1504 break;
41a2b7ae 1505 }
d3eb6e8f 1506
3230ee6b
PP
1507 assert(iterator->upstream_component);
1508 assert(iterator->upstream_component->class);
d3eb6e8f 1509
3230ee6b
PP
1510 /* Pick the appropriate "next" method */
1511 switch (iterator->upstream_component->class->type) {
d3eb6e8f
PP
1512 case BT_COMPONENT_CLASS_TYPE_SOURCE:
1513 {
1514 struct bt_component_class_source *source_class =
3230ee6b 1515 container_of(iterator->upstream_component->class,
d3eb6e8f
PP
1516 struct bt_component_class_source, parent);
1517
1518 assert(source_class->methods.iterator.next);
1519 next_method = source_class->methods.iterator.next;
1520 break;
1521 }
1522 case BT_COMPONENT_CLASS_TYPE_FILTER:
1523 {
1524 struct bt_component_class_filter *filter_class =
3230ee6b 1525 container_of(iterator->upstream_component->class,
d3eb6e8f
PP
1526 struct bt_component_class_filter, parent);
1527
1528 assert(filter_class->methods.iterator.next);
1529 next_method = filter_class->methods.iterator.next;
1530 break;
1531 }
1532 default:
0fbb9a9f 1533 abort();
d3eb6e8f
PP
1534 }
1535
3230ee6b
PP
1536 /*
1537 * Call the user's "next" method to get the next notification
fa054faf 1538 * and status.
3230ee6b 1539 */
d3eb6e8f 1540 assert(next_method);
3230ee6b 1541
fa054faf 1542 while (iterator->queue->length == 0) {
5af447e5 1543 BT_LOGD_STR("Calling user's \"next\" method.");
fa054faf 1544 next_return = next_method(priv_iterator);
5af447e5
PP
1545 BT_LOGD("User method returned: status=%s",
1546 bt_notification_iterator_status_string(next_return.status));
fa054faf 1547 if (next_return.status < 0) {
5af447e5 1548 BT_LOGW_STR("User method failed.");
fa054faf 1549 status = next_return.status;
3230ee6b
PP
1550 goto end;
1551 }
1552
fa054faf
PP
1553 switch (next_return.status) {
1554 case BT_NOTIFICATION_ITERATOR_STATUS_END:
1555 ret = handle_end(iterator);
1556 if (ret) {
5af447e5 1557 BT_LOGW_STR("Cannot handle end of iteration.");
fa054faf
PP
1558 status = BT_NOTIFICATION_ITERATOR_STATUS_ERROR;
1559 goto end;
1560 }
3230ee6b 1561
8f9d7550
PP
1562 assert(iterator->state ==
1563 BT_NOTIFICATION_ITERATOR_STATE_ACTIVE);
1564 iterator->state = BT_NOTIFICATION_ITERATOR_STATE_ENDED;
bd14d768 1565
8f9d7550
PP
1566 if (iterator->queue->length == 0) {
1567 status = BT_NOTIFICATION_ITERATOR_STATUS_END;
bd14d768 1568 }
5af447e5
PP
1569
1570 BT_LOGD("Set new status: status=%s",
1571 bt_notification_iterator_status_string(status));
3230ee6b 1572 goto end;
fa054faf
PP
1573 case BT_NOTIFICATION_ITERATOR_STATUS_AGAIN:
1574 status = BT_NOTIFICATION_ITERATOR_STATUS_AGAIN;
1575 goto end;
1576 case BT_NOTIFICATION_ITERATOR_STATUS_OK:
1577 if (!next_return.notification) {
5af447e5 1578 BT_LOGW_STR("User method returned BT_NOTIFICATION_ITERATOR_STATUS_OK, but notification is NULL.");
fa054faf
PP
1579 status = BT_NOTIFICATION_ITERATOR_STATUS_ERROR;
1580 goto end;
1581 }
1582
1583 /*
1584 * We know the notification is valid. Before we
1585 * push it to the head of the queue, push the
1586 * appropriate automatic notifications if any.
1587 */
1588 ret = enqueue_notification_and_automatic(iterator,
1589 next_return.notification);
1590 BT_PUT(next_return.notification);
1591 if (ret) {
5af447e5 1592 BT_LOGW("Cannot enqueue notification and automatic notifications.");
fa054faf
PP
1593 status = BT_NOTIFICATION_ITERATOR_STATUS_ERROR;
1594 goto end;
1595 }
1596 break;
1597 default:
1598 /* Unknown non-error status */
0fbb9a9f 1599 abort();
3230ee6b 1600 }
41a2b7ae
PP
1601 }
1602
1603end:
3230ee6b
PP
1604 return status;
1605}
1606
1607enum bt_notification_iterator_status
1608bt_notification_iterator_next(struct bt_notification_iterator *iterator)
1609{
1610 enum bt_notification_iterator_status status;
1611
1612 if (!iterator) {
5af447e5 1613 BT_LOGW_STR("Invalid parameter: notification iterator is NULL.");
3230ee6b
PP
1614 status = BT_NOTIFICATION_ITERATOR_STATUS_INVALID;
1615 goto end;
1616 }
1617
5af447e5
PP
1618 BT_LOGD("Notification iterator's \"next\": iter-addr=%p", iterator);
1619
3230ee6b
PP
1620 /*
1621 * Make sure that the iterator's queue contains at least one
1622 * notification.
1623 */
1624 status = ensure_queue_has_notifications(iterator);
1625 if (status != BT_NOTIFICATION_ITERATOR_STATUS_OK) {
5af447e5 1626 /* Not an error */
3230ee6b
PP
1627 goto end;
1628 }
1629
1630 /*
1631 * Move the notification at the tail of the queue to the
1632 * iterator's current notification.
1633 */
1634 assert(iterator->queue->length > 0);
1635 bt_put(iterator->current_notification);
1636 iterator->current_notification = g_queue_pop_tail(iterator->queue);
1637 assert(iterator->current_notification);
1638
1639end:
1640 return status;
53d45b87
JG
1641}
1642
413bc2c4
JG
1643struct bt_component *bt_notification_iterator_get_component(
1644 struct bt_notification_iterator *iterator)
1645{
3230ee6b 1646 return bt_get(iterator->upstream_component);
413bc2c4
JG
1647}
1648
91457551
PP
1649struct bt_private_component *
1650bt_private_notification_iterator_get_private_component(
1651 struct bt_private_notification_iterator *private_iterator)
1652{
1653 return bt_private_component_from_component(
1654 bt_notification_iterator_get_component(
1655 bt_notification_iterator_from_private(private_iterator)));
1656}
1657
9531634f
JG
1658enum bt_notification_iterator_status bt_notification_iterator_seek_time(
1659 struct bt_notification_iterator *iterator,
1660 enum bt_notification_iterator_seek_origin seek_origin,
1661 int64_t time)
1662{
b7726e32
MD
1663 enum bt_notification_iterator_status ret =
1664 BT_NOTIFICATION_ITERATOR_STATUS_UNSUPPORTED;
9531634f
JG
1665 return ret;
1666}
This page took 0.100324 seconds and 4 git commands to generate.