Fix: cancel a notif. iter. finalized during its "next" method
[babeltrace.git] / lib / graph / iterator.c
index 1a295cb3c4f1865cf92dc59267c4ffa1eeb987b1..07eaa1fd146d42d8702c897ec6020bf0f95a46c1 100644 (file)
@@ -1474,17 +1474,25 @@ enum bt_notification_iterator_status ensure_queue_has_notifications(
 
        assert(iterator);
        BT_LOGD("Ensuring that notification iterator's queue has at least one notification: "
-               "iter-addr=%p, queue-size=%u",
-               iterator, iterator->queue->length);
+               "iter-addr=%p, queue-size=%u, iter-state=%s",
+               iterator, iterator->queue->length,
+               bt_notification_iterator_state_string(iterator->state));
 
        if (iterator->queue->length > 0) {
-               /* We already have enough */
+               /*
+                * We already have enough. Even if this notification
+                * iterator is finalized, its user can still flush its
+                * current queue's content by calling its "next" method
+                * since this content is local and has no impact on what
+                * used to be the iterator's upstream component.
+                */
                BT_LOGD_STR("Queue already has at least one notification.");
                goto end;
        }
 
        switch (iterator->state) {
        case BT_NOTIFICATION_ITERATOR_STATE_FINALIZED_AND_ENDED:
+       case BT_NOTIFICATION_ITERATOR_STATE_FINALIZED:
                BT_LOGD_STR("Notification iterator's \"next\" called, but it is finalized.");
                status = BT_NOTIFICATION_ITERATOR_STATUS_CANCELED;
                goto end;
@@ -1542,6 +1550,32 @@ enum bt_notification_iterator_status ensure_queue_has_notifications(
                        goto end;
                }
 
+               if (iterator->state == BT_NOTIFICATION_ITERATOR_STATE_FINALIZED ||
+                               iterator->state == BT_NOTIFICATION_ITERATOR_STATE_FINALIZED_AND_ENDED) {
+                       /*
+                        * The user's "next" method, somehow, cancelled
+                        * its own notification iterator. This can
+                        * happen, for example, when the user's method
+                        * removes the port on which there's the
+                        * connection from which the iterator was
+                        * created. In this case, said connection is
+                        * ended, and all its notification iterators are
+                        * finalized.
+                        *
+                        * Only bt_put() the returned notification if
+                        * the status is
+                        * BT_NOTIFICATION_ITERATOR_STATUS_OK because
+                        * otherwise this field could be garbage.
+                        */
+                       if (next_return.status ==
+                                       BT_NOTIFICATION_ITERATOR_STATUS_OK) {
+                               bt_put(next_return.notification);
+                       }
+
+                       status = BT_NOTIFICATION_ITERATOR_STATUS_CANCELED;
+                       goto end;
+               }
+
                switch (next_return.status) {
                case BT_NOTIFICATION_ITERATOR_STATUS_END:
                        ret = handle_end(iterator);
@@ -1551,20 +1585,12 @@ enum bt_notification_iterator_status ensure_queue_has_notifications(
                                goto end;
                        }
 
-                       if (iterator->state == BT_NOTIFICATION_ITERATOR_STATE_FINALIZED) {
-                               iterator->state =
-                                       BT_NOTIFICATION_ITERATOR_STATE_FINALIZED_AND_ENDED;
-
-                               if (iterator->queue->length == 0) {
-                                       status = BT_NOTIFICATION_ITERATOR_STATUS_CANCELED;
-                               }
-                       } else {
-                               iterator->state =
-                                       BT_NOTIFICATION_ITERATOR_STATE_ENDED;
+                       assert(iterator->state ==
+                               BT_NOTIFICATION_ITERATOR_STATE_ACTIVE);
+                       iterator->state = BT_NOTIFICATION_ITERATOR_STATE_ENDED;
 
-                               if (iterator->queue->length == 0) {
-                                       status = BT_NOTIFICATION_ITERATOR_STATUS_END;
-                               }
+                       if (iterator->queue->length == 0) {
+                               status = BT_NOTIFICATION_ITERATOR_STATUS_END;
                        }
 
                        BT_LOGD("Set new status: status=%s",
This page took 0.028729 seconds and 4 git commands to generate.