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