+
+ /*
+ * The user's initialization method needs to see that this
+ * component is part of the graph. If the user method fails, we
+ * immediately remove the component from the graph's components.
+ */
+ g_ptr_array_add(graph->components, component);
+ bt_component_set_graph(component, graph);
+
+ if (init_method) {
+ BT_LOGD_STR("Calling user's initialization method.");
+ comp_status = init_method(component, params, init_method_data);
+ BT_LOGD("User method returned: status=%s",
+ bt_self_component_status_string(comp_status));
+ if (comp_status != BT_SELF_COMPONENT_STATUS_OK) {
+ BT_LOGW_STR("Initialization method failed.");
+ graph_status = (int) comp_status;
+ bt_component_set_graph(component, NULL);
+ g_ptr_array_remove_fast(graph->components, component);
+ goto end;
+ }
+ }
+
+ /*
+ * Mark the component as initialized so that its finalization
+ * method is called when it is destroyed.
+ */
+ component->initialized = true;
+
+ /*
+ * If it's a sink component, it needs to be part of the graph's
+ * sink queue to be consumed by bt_graph_consume().
+ */
+ if (bt_component_is_sink(component)) {
+ graph->has_sink = true;
+ g_queue_push_tail(graph->sinks_to_consume, component);
+ }
+
+ /*
+ * Freeze the component class now that it's instantiated at
+ * least once.
+ */
+ BT_LOGD_STR("Freezing component class.");
+ bt_component_class_freeze(comp_cls);
+ BT_LIB_LOGD("Added component to graph: "
+ "%![graph-]+g, %![cc-]+C, name=\"%s\", %![params-]+v, "
+ "init-method-data-addr=%p, %![comp-]+c",
+ graph, comp_cls, name, params, init_method_data, component);
+
+ if (user_component) {
+ /* Move reference to user */
+ *user_component = component;
+ component = NULL;
+ }
+
+end:
+ bt_object_put_ref(component);
+ bt_object_put_ref(new_params);
+ (void) init_can_consume;
+ bt_graph_set_can_consume(graph, init_can_consume);
+ return graph_status;
+}
+
+enum bt_graph_status
+bt_private_graph_add_source_component_with_init_method_data(
+ struct bt_private_graph *graph,
+ struct bt_component_class_source *comp_cls,
+ const char *name, const struct bt_value *params,
+ void *init_method_data, struct bt_component_source **component)
+{
+ BT_ASSERT_PRE_NON_NULL(comp_cls, "Component class");
+ return add_component_with_init_method_data(graph,
+ (void *) comp_cls, (comp_init_method_t) comp_cls->methods.init,
+ name, params, init_method_data, (void *) component);
+}
+
+enum bt_graph_status bt_private_graph_add_source_component(
+ struct bt_private_graph *graph,
+ struct bt_component_class_source *comp_cls,
+ const char *name, const struct bt_value *params,
+ struct bt_component_source **component)
+{
+ return bt_private_graph_add_source_component_with_init_method_data(
+ graph, comp_cls, name, params, NULL, component);
+}
+
+enum bt_graph_status
+bt_private_graph_add_filter_component_with_init_method_data(
+ struct bt_private_graph *graph,
+ struct bt_component_class_filter *comp_cls,
+ const char *name, const struct bt_value *params,
+ void *init_method_data, struct bt_component_filter **component)
+{
+ BT_ASSERT_PRE_NON_NULL(comp_cls, "Component class");
+ return add_component_with_init_method_data(graph,
+ (void *) comp_cls, (comp_init_method_t) comp_cls->methods.init,
+ name, params, init_method_data, (void *) component);
+}
+
+enum bt_graph_status bt_private_graph_add_filter_component(
+ struct bt_private_graph *graph,
+ struct bt_component_class_filter *comp_cls,
+ const char *name, const struct bt_value *params,
+ struct bt_component_filter **component)
+{
+ return bt_private_graph_add_filter_component_with_init_method_data(
+ graph, comp_cls, name, params, NULL, component);
+}
+
+enum bt_graph_status
+bt_private_graph_add_sink_component_with_init_method_data(
+ struct bt_private_graph *graph,
+ struct bt_component_class_sink *comp_cls,
+ const char *name, const struct bt_value *params,
+ void *init_method_data, struct bt_component_sink **component)
+{
+ BT_ASSERT_PRE_NON_NULL(comp_cls, "Component class");
+ return add_component_with_init_method_data(graph,
+ (void *) comp_cls, (comp_init_method_t) comp_cls->methods.init,
+ name, params, init_method_data, (void *) component);
+}
+
+enum bt_graph_status bt_private_graph_add_sink_component(
+ struct bt_private_graph *graph,
+ struct bt_component_class_sink *comp_cls,
+ const char *name, const struct bt_value *params,
+ struct bt_component_sink **component)
+{
+ return bt_private_graph_add_sink_component_with_init_method_data(
+ graph, comp_cls, name, params, NULL, component);
+}
+
+BT_HIDDEN
+int bt_graph_remove_unconnected_component(struct bt_graph *graph,
+ struct bt_component *component)
+{
+ bool init_can_consume;
+ uint64_t count;
+ uint64_t i;
+ int ret = 0;
+
+ BT_ASSERT(graph);
+ BT_ASSERT(component);
+ BT_ASSERT(component->base.ref_count == 0);
+ BT_ASSERT(bt_component_borrow_graph(component) == graph);
+
+ init_can_consume = graph->can_consume;
+ count = bt_component_get_input_port_count(component);
+
+ for (i = 0; i < count; i++) {
+ struct bt_port *port = (void *)
+ bt_component_borrow_input_port_by_index(component, i);
+
+ BT_ASSERT(port);
+
+ if (bt_port_is_connected(port)) {
+ BT_LIB_LOGW("Cannot remove component from graph: "
+ "an input port is connected: "
+ "%![graph-]+g, %![comp-]+c, %![port-]+p",
+ graph, component, port);
+ goto error;
+ }
+ }
+
+ count = bt_component_get_output_port_count(component);
+
+ for (i = 0; i < count; i++) {
+ struct bt_port *port = (void *)
+ bt_component_borrow_output_port_by_index(component, i);
+
+ BT_ASSERT(port);
+
+ if (bt_port_is_connected(port)) {
+ BT_LIB_LOGW("Cannot remove component from graph: "
+ "an output port is connected: "
+ "%![graph-]+g, %![comp-]+c, %![port-]+p",
+ graph, component, port);
+ goto error;
+ }
+ }
+
+ bt_graph_set_can_consume(graph, false);
+
+ /* Possibly remove from sinks to consume */
+ (void) g_queue_remove(graph->sinks_to_consume, component);
+
+ if (graph->sinks_to_consume->length == 0) {
+ graph->has_sink = false;
+ }
+
+ /*
+ * This calls bt_object_try_spec_release() on the component, and
+ * since its reference count is 0, its destructor is called. Its
+ * destructor calls the user's finalization method (if set).
+ */
+ g_ptr_array_remove(graph->components, component);
+ goto end;
+
+error:
+ ret = -1;
+
+end:
+ (void) init_can_consume;
+ bt_graph_set_can_consume(graph, init_can_consume);
+ return ret;
+}
+
+BT_HIDDEN
+void bt_graph_add_notification(struct bt_graph *graph,
+ struct bt_notification *notif)
+{
+ BT_ASSERT(graph);
+ BT_ASSERT(notif);
+
+ /*
+ * It's okay not to take a reference because, when a
+ * notification's reference count drops to 0, either:
+ *
+ * * It is recycled back to one of this graph's pool.
+ * * It is destroyed because it doesn't have any link to any
+ * graph, which means the original graph is already destroyed.
+ */
+ g_ptr_array_add(graph->notifications, notif);