Commit | Line | Data |
---|---|---|
c0418dd9 | 1 | /* |
e2f7325d | 2 | * Copyright 2017-2018 Philippe Proulx <pproulx@efficios.com> |
f60c8b34 | 3 | * Copyright 2017 Jérémie Galarneau <jeremie.galarneau@efficios.com> |
c0418dd9 JG |
4 | * |
5 | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
6 | * of this software and associated documentation files (the "Software"), to deal | |
7 | * in the Software without restriction, including without limitation the rights | |
8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
9 | * copies of the Software, and to permit persons to whom the Software is | |
10 | * furnished to do so, subject to the following conditions: | |
11 | * | |
12 | * The above copyright notice and this permission notice shall be included in | |
13 | * all copies or substantial portions of the Software. | |
14 | * | |
15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
21 | * SOFTWARE. | |
22 | */ | |
23 | ||
350ad6c1 | 24 | #define BT_LOG_TAG "LIB/GRAPH" |
c2d9d9cf | 25 | #include "lib/logging.h" |
262e5473 | 26 | |
578e048b MJ |
27 | #include "common/assert.h" |
28 | #include "lib/assert-pre.h" | |
f6f301d7 | 29 | #include "lib/assert-post.h" |
3fadfbc0 MJ |
30 | #include <babeltrace2/graph/graph.h> |
31 | #include <babeltrace2/graph/graph-const.h> | |
3fadfbc0 MJ |
32 | #include <babeltrace2/graph/component-source-const.h> |
33 | #include <babeltrace2/graph/component-filter-const.h> | |
34 | #include <babeltrace2/graph/port-const.h> | |
578e048b MJ |
35 | #include "lib/graph/message/message.h" |
36 | #include "compat/compiler.h" | |
37 | #include "common/common.h" | |
3fadfbc0 MJ |
38 | #include <babeltrace2/types.h> |
39 | #include <babeltrace2/value.h> | |
40 | #include <babeltrace2/value-const.h> | |
578e048b | 41 | #include "lib/value.h" |
f60c8b34 | 42 | #include <unistd.h> |
c4f23e30 | 43 | #include <stdbool.h> |
1bf957a0 PP |
44 | #include <glib.h> |
45 | ||
078033ed | 46 | #include "component-class-sink-simple.h" |
578e048b MJ |
47 | #include "component.h" |
48 | #include "component-sink.h" | |
49 | #include "connection.h" | |
50 | #include "graph.h" | |
9b4f9b42 | 51 | #include "interrupter.h" |
578e048b MJ |
52 | #include "message/event.h" |
53 | #include "message/packet.h" | |
54 | ||
d24d5663 PP |
55 | typedef enum bt_graph_listener_func_status |
56 | (*port_added_func_t)(const void *, const void *, void *); | |
0d72b8c3 | 57 | |
21a9f056 | 58 | typedef enum bt_component_class_initialize_method_status |
59225a3e | 59 | (*comp_init_method_t)(const void *, void *, const void *, void *); |
d24d5663 | 60 | |
1bf957a0 | 61 | struct bt_graph_listener { |
0d72b8c3 | 62 | bt_graph_listener_removed_func removed; |
1bf957a0 PP |
63 | void *data; |
64 | }; | |
c0418dd9 | 65 | |
d94d92ac PP |
66 | struct bt_graph_listener_port_added { |
67 | struct bt_graph_listener base; | |
68 | port_added_func_t func; | |
69 | }; | |
8cc092c9 | 70 | |
d94d92ac PP |
71 | #define INIT_LISTENERS_ARRAY(_type, _listeners) \ |
72 | do { \ | |
73 | _listeners = g_array_new(FALSE, TRUE, sizeof(_type)); \ | |
74 | if (!(_listeners)) { \ | |
870631a2 PP |
75 | BT_LIB_LOGE_APPEND_CAUSE( \ |
76 | "Failed to allocate one GArray."); \ | |
d94d92ac PP |
77 | } \ |
78 | } while (0) | |
79 | ||
80 | #define CALL_REMOVE_LISTENERS(_type, _listeners) \ | |
81 | do { \ | |
82 | size_t i; \ | |
83 | \ | |
9fde7203 FD |
84 | if (!_listeners) { \ |
85 | break; \ | |
86 | } \ | |
d94d92ac PP |
87 | for (i = 0; i < (_listeners)->len; i++) { \ |
88 | _type *listener = \ | |
89 | &g_array_index((_listeners), _type, i); \ | |
90 | \ | |
91 | if (listener->base.removed) { \ | |
92 | listener->base.removed(listener->base.data); \ | |
93 | } \ | |
94 | } \ | |
95 | } while (0) | |
8cc092c9 | 96 | |
f60c8b34 | 97 | static |
d94d92ac | 98 | void destroy_graph(struct bt_object *obj) |
c0418dd9 | 99 | { |
0d72b8c3 | 100 | struct bt_graph *graph = container_of(obj, struct bt_graph, base); |
c0418dd9 | 101 | |
bd14d768 PP |
102 | /* |
103 | * The graph's reference count is 0 if we're here. Increment | |
104 | * it to avoid a double-destroy (possibly infinitely recursive) | |
105 | * in this situation: | |
106 | * | |
107 | * 1. We put and destroy a connection. | |
d0fea130 PP |
108 | * 2. This connection's destructor finalizes its active message |
109 | * iterators. | |
110 | * 3. A message iterator's finalization function gets a new | |
111 | * reference on its component (reference count goes from 0 to | |
112 | * 1). | |
bd14d768 PP |
113 | * 4. Since this component's reference count goes to 1, it takes |
114 | * a reference on its parent (this graph). This graph's | |
115 | * reference count goes from 0 to 1. | |
d6e69534 | 116 | * 5. The message iterator's finalization function puts its |
bd14d768 PP |
117 | * component reference (reference count goes from 1 to 0). |
118 | * 6. Since this component's reference count goes from 1 to 0, | |
119 | * it puts its parent (this graph). This graph's reference | |
120 | * count goes from 1 to 0. | |
d0fea130 PP |
121 | * 7. Since this graph's reference count goes from 1 to 0, its |
122 | * destructor is called (this function). | |
bd14d768 PP |
123 | * |
124 | * With the incrementation below, the graph's reference count at | |
125 | * step 4 goes from 1 to 2, and from 2 to 1 at step 6. This | |
126 | * ensures that this function is not called two times. | |
127 | */ | |
3f7d4d90 | 128 | BT_LIB_LOGI("Destroying graph: %!+g", graph); |
3fea54f6 | 129 | obj->ref_count++; |
9b4f9b42 | 130 | graph->config_state = BT_GRAPH_CONFIGURATION_STATE_DESTROYING; |
49682acd | 131 | |
8cc092c9 | 132 | /* Call all remove listeners */ |
d94d92ac PP |
133 | CALL_REMOVE_LISTENERS(struct bt_graph_listener_port_added, |
134 | graph->listeners.source_output_port_added); | |
135 | CALL_REMOVE_LISTENERS(struct bt_graph_listener_port_added, | |
136 | graph->listeners.filter_output_port_added); | |
137 | CALL_REMOVE_LISTENERS(struct bt_graph_listener_port_added, | |
138 | graph->listeners.filter_input_port_added); | |
139 | CALL_REMOVE_LISTENERS(struct bt_graph_listener_port_added, | |
140 | graph->listeners.sink_input_port_added); | |
8cc092c9 | 141 | |
d6e69534 PP |
142 | if (graph->messages) { |
143 | g_ptr_array_free(graph->messages, TRUE); | |
144 | graph->messages = NULL; | |
5c563278 PP |
145 | } |
146 | ||
c0418dd9 | 147 | if (graph->connections) { |
262e5473 | 148 | BT_LOGD_STR("Destroying connections."); |
c0418dd9 | 149 | g_ptr_array_free(graph->connections, TRUE); |
d94d92ac | 150 | graph->connections = NULL; |
c0418dd9 | 151 | } |
5c563278 | 152 | |
bd14d768 | 153 | if (graph->components) { |
262e5473 | 154 | BT_LOGD_STR("Destroying components."); |
bd14d768 | 155 | g_ptr_array_free(graph->components, TRUE); |
d94d92ac | 156 | graph->components = NULL; |
bd14d768 | 157 | } |
5c563278 | 158 | |
9b4f9b42 PP |
159 | if (graph->interrupters) { |
160 | BT_LOGD_STR("Putting interrupters."); | |
161 | g_ptr_array_free(graph->interrupters, TRUE); | |
162 | graph->interrupters = NULL; | |
163 | } | |
164 | ||
165 | BT_OBJECT_PUT_REF_AND_RESET(graph->default_interrupter); | |
166 | ||
f60c8b34 JG |
167 | if (graph->sinks_to_consume) { |
168 | g_queue_free(graph->sinks_to_consume); | |
d94d92ac PP |
169 | graph->sinks_to_consume = NULL; |
170 | } | |
171 | ||
172 | if (graph->listeners.source_output_port_added) { | |
173 | g_array_free(graph->listeners.source_output_port_added, TRUE); | |
174 | graph->listeners.source_output_port_added = NULL; | |
175 | } | |
176 | ||
177 | if (graph->listeners.filter_output_port_added) { | |
178 | g_array_free(graph->listeners.filter_output_port_added, TRUE); | |
179 | graph->listeners.filter_output_port_added = NULL; | |
180 | } | |
181 | ||
182 | if (graph->listeners.filter_input_port_added) { | |
183 | g_array_free(graph->listeners.filter_input_port_added, TRUE); | |
184 | graph->listeners.filter_input_port_added = NULL; | |
c0418dd9 | 185 | } |
1bf957a0 | 186 | |
d94d92ac PP |
187 | if (graph->listeners.sink_input_port_added) { |
188 | g_array_free(graph->listeners.sink_input_port_added, TRUE); | |
189 | graph->listeners.sink_input_port_added = NULL; | |
1bf957a0 PP |
190 | } |
191 | ||
d6e69534 PP |
192 | bt_object_pool_finalize(&graph->event_msg_pool); |
193 | bt_object_pool_finalize(&graph->packet_begin_msg_pool); | |
194 | bt_object_pool_finalize(&graph->packet_end_msg_pool); | |
c0418dd9 JG |
195 | g_free(graph); |
196 | } | |
197 | ||
5c563278 | 198 | static |
d6e69534 | 199 | void destroy_message_event(struct bt_message *msg, |
5c563278 PP |
200 | struct bt_graph *graph) |
201 | { | |
d6e69534 | 202 | bt_message_event_destroy(msg); |
5c563278 PP |
203 | } |
204 | ||
205 | static | |
d6e69534 | 206 | void destroy_message_packet_begin(struct bt_message *msg, |
5c563278 PP |
207 | struct bt_graph *graph) |
208 | { | |
5df26c89 | 209 | bt_message_packet_destroy(msg); |
5c563278 PP |
210 | } |
211 | ||
212 | static | |
d6e69534 | 213 | void destroy_message_packet_end(struct bt_message *msg, |
5c563278 PP |
214 | struct bt_graph *graph) |
215 | { | |
5df26c89 | 216 | bt_message_packet_destroy(msg); |
5c563278 PP |
217 | } |
218 | ||
219 | static | |
d6e69534 | 220 | void notify_message_graph_is_destroyed(struct bt_message *msg) |
5c563278 | 221 | { |
d6e69534 | 222 | bt_message_unlink_graph(msg); |
5c563278 PP |
223 | } |
224 | ||
056deb59 | 225 | struct bt_graph *bt_graph_create(uint64_t mip_version) |
c0418dd9 | 226 | { |
f60c8b34 | 227 | struct bt_graph *graph; |
1bf957a0 | 228 | int ret; |
c0418dd9 | 229 | |
17f3083a | 230 | BT_ASSERT_PRE_NO_ERROR(); |
056deb59 PP |
231 | BT_ASSERT_PRE(mip_version <= bt_get_maximal_mip_version(), |
232 | "Unknown MIP version: mip-version=%" PRIu64 ", " | |
233 | "max-mip-version=%" PRIu64, | |
234 | mip_version, bt_get_maximal_mip_version()); | |
3f7d4d90 | 235 | BT_LOGI_STR("Creating graph object."); |
f60c8b34 | 236 | graph = g_new0(struct bt_graph, 1); |
c0418dd9 | 237 | if (!graph) { |
870631a2 | 238 | BT_LIB_LOGE_APPEND_CAUSE("Failed to allocate one graph."); |
c0418dd9 JG |
239 | goto end; |
240 | } | |
241 | ||
d94d92ac | 242 | bt_object_init_shared(&graph->base, destroy_graph); |
056deb59 | 243 | graph->mip_version = mip_version; |
3fea54f6 PP |
244 | graph->connections = g_ptr_array_new_with_free_func( |
245 | (GDestroyNotify) bt_object_try_spec_release); | |
c0418dd9 | 246 | if (!graph->connections) { |
870631a2 | 247 | BT_LIB_LOGE_APPEND_CAUSE("Failed to allocate one GPtrArray."); |
c0418dd9 JG |
248 | goto error; |
249 | } | |
3fea54f6 PP |
250 | graph->components = g_ptr_array_new_with_free_func( |
251 | (GDestroyNotify) bt_object_try_spec_release); | |
f60c8b34 | 252 | if (!graph->components) { |
870631a2 | 253 | BT_LIB_LOGE_APPEND_CAUSE("Failed to allocate one GPtrArray."); |
f60c8b34 JG |
254 | goto error; |
255 | } | |
256 | graph->sinks_to_consume = g_queue_new(); | |
257 | if (!graph->sinks_to_consume) { | |
870631a2 | 258 | BT_LIB_LOGE_APPEND_CAUSE("Failed to allocate one GQueue."); |
c0418dd9 JG |
259 | goto error; |
260 | } | |
1bf957a0 | 261 | |
d94d92ac PP |
262 | bt_graph_set_can_consume(graph, true); |
263 | INIT_LISTENERS_ARRAY(struct bt_graph_listener_port_added, | |
264 | graph->listeners.source_output_port_added); | |
265 | ||
266 | if (!graph->listeners.source_output_port_added) { | |
1bf957a0 PP |
267 | goto error; |
268 | } | |
269 | ||
d94d92ac PP |
270 | INIT_LISTENERS_ARRAY(struct bt_graph_listener_port_added, |
271 | graph->listeners.filter_output_port_added); | |
272 | ||
273 | if (!graph->listeners.filter_output_port_added) { | |
1bf957a0 PP |
274 | goto error; |
275 | } | |
276 | ||
d94d92ac PP |
277 | INIT_LISTENERS_ARRAY(struct bt_graph_listener_port_added, |
278 | graph->listeners.filter_input_port_added); | |
279 | ||
280 | if (!graph->listeners.filter_input_port_added) { | |
1bf957a0 PP |
281 | goto error; |
282 | } | |
283 | ||
d94d92ac PP |
284 | INIT_LISTENERS_ARRAY(struct bt_graph_listener_port_added, |
285 | graph->listeners.sink_input_port_added); | |
286 | ||
287 | if (!graph->listeners.sink_input_port_added) { | |
d94d92ac PP |
288 | goto error; |
289 | } | |
290 | ||
9b4f9b42 | 291 | graph->interrupters = g_ptr_array_new_with_free_func( |
6871026b | 292 | (GDestroyNotify) bt_object_put_ref_no_null_check); |
9b4f9b42 PP |
293 | if (!graph->interrupters) { |
294 | BT_LIB_LOGE_APPEND_CAUSE("Failed to allocate one GPtrArray."); | |
295 | goto error; | |
296 | } | |
297 | ||
298 | graph->default_interrupter = bt_interrupter_create(); | |
299 | if (!graph->default_interrupter) { | |
300 | BT_LIB_LOGE_APPEND_CAUSE( | |
301 | "Failed to create one interrupter object."); | |
302 | goto error; | |
303 | } | |
304 | ||
305 | bt_graph_add_interrupter(graph, graph->default_interrupter); | |
d6e69534 PP |
306 | ret = bt_object_pool_initialize(&graph->event_msg_pool, |
307 | (bt_object_pool_new_object_func) bt_message_event_new, | |
308 | (bt_object_pool_destroy_object_func) destroy_message_event, | |
5c563278 PP |
309 | graph); |
310 | if (ret) { | |
870631a2 PP |
311 | BT_LIB_LOGE_APPEND_CAUSE( |
312 | "Failed to initialize event message pool: ret=%d", | |
5c563278 PP |
313 | ret); |
314 | goto error; | |
315 | } | |
316 | ||
d6e69534 PP |
317 | ret = bt_object_pool_initialize(&graph->packet_begin_msg_pool, |
318 | (bt_object_pool_new_object_func) bt_message_packet_beginning_new, | |
319 | (bt_object_pool_destroy_object_func) destroy_message_packet_begin, | |
5c563278 PP |
320 | graph); |
321 | if (ret) { | |
870631a2 PP |
322 | BT_LIB_LOGE_APPEND_CAUSE( |
323 | "Failed to initialize packet beginning message pool: ret=%d", | |
5c563278 PP |
324 | ret); |
325 | goto error; | |
326 | } | |
327 | ||
d6e69534 PP |
328 | ret = bt_object_pool_initialize(&graph->packet_end_msg_pool, |
329 | (bt_object_pool_new_object_func) bt_message_packet_end_new, | |
330 | (bt_object_pool_destroy_object_func) destroy_message_packet_end, | |
5c563278 PP |
331 | graph); |
332 | if (ret) { | |
870631a2 PP |
333 | BT_LIB_LOGE_APPEND_CAUSE( |
334 | "Failed to initialize packet end message pool: ret=%d", | |
5c563278 PP |
335 | ret); |
336 | goto error; | |
337 | } | |
338 | ||
d6e69534 PP |
339 | graph->messages = g_ptr_array_new_with_free_func( |
340 | (GDestroyNotify) notify_message_graph_is_destroyed); | |
3f7d4d90 | 341 | BT_LIB_LOGI("Created graph object: %!+g", graph); |
262e5473 | 342 | |
c0418dd9 | 343 | end: |
a2d06fd5 | 344 | return (void *) graph; |
d94d92ac | 345 | |
c0418dd9 | 346 | error: |
65300d60 | 347 | BT_OBJECT_PUT_REF_AND_RESET(graph); |
c0418dd9 JG |
348 | goto end; |
349 | } | |
350 | ||
d24d5663 | 351 | enum bt_graph_connect_ports_status bt_graph_connect_ports( |
0d72b8c3 PP |
352 | struct bt_graph *graph, |
353 | const struct bt_port_output *upstream_port_out, | |
354 | const struct bt_port_input *downstream_port_in, | |
355 | const struct bt_connection **user_connection) | |
f60c8b34 | 356 | { |
d24d5663 | 357 | enum bt_graph_connect_ports_status status = BT_FUNC_STATUS_OK; |
f60c8b34 | 358 | struct bt_connection *connection = NULL; |
d94d92ac PP |
359 | struct bt_port *upstream_port = (void *) upstream_port_out; |
360 | struct bt_port *downstream_port = (void *) downstream_port_in; | |
f60c8b34 JG |
361 | struct bt_component *upstream_component = NULL; |
362 | struct bt_component *downstream_component = NULL; | |
d24d5663 | 363 | enum bt_component_class_port_connected_method_status port_connected_status; |
d94d92ac | 364 | bool init_can_consume; |
f60c8b34 | 365 | |
17f3083a | 366 | BT_ASSERT_PRE_NO_ERROR(); |
d94d92ac PP |
367 | BT_ASSERT_PRE_NON_NULL(graph, "Graph"); |
368 | BT_ASSERT_PRE_NON_NULL(upstream_port, "Upstream port"); | |
369 | BT_ASSERT_PRE_NON_NULL(downstream_port, "Downstream port port"); | |
5badd463 PP |
370 | BT_ASSERT_PRE( |
371 | graph->config_state == BT_GRAPH_CONFIGURATION_STATE_CONFIGURING, | |
38cda5da | 372 | "Graph is not in the \"configuring\" state: %!+g", graph); |
d94d92ac PP |
373 | BT_ASSERT_PRE(!bt_port_is_connected(upstream_port), |
374 | "Upstream port is already connected: %!+p", upstream_port); | |
375 | BT_ASSERT_PRE(!bt_port_is_connected(downstream_port), | |
376 | "Downstream port is already connected: %!+p", downstream_port); | |
0d72b8c3 | 377 | BT_ASSERT_PRE(bt_port_borrow_component_inline((void *) upstream_port), |
d94d92ac PP |
378 | "Upstream port does not belong to a component: %!+p", |
379 | upstream_port); | |
0d72b8c3 | 380 | BT_ASSERT_PRE(bt_port_borrow_component_inline((void *) downstream_port), |
d94d92ac PP |
381 | "Downstream port does not belong to a component: %!+p", |
382 | downstream_port); | |
4aa7981f | 383 | init_can_consume = graph->can_consume; |
3f7d4d90 | 384 | BT_LIB_LOGI("Connecting component ports within graph: " |
d94d92ac PP |
385 | "%![graph-]+g, %![up-port-]+p, %![down-port-]+p", |
386 | graph, upstream_port, downstream_port); | |
387 | bt_graph_set_can_consume(graph, false); | |
0d72b8c3 | 388 | upstream_component = bt_port_borrow_component_inline( |
d94d92ac | 389 | (void *) upstream_port); |
0d72b8c3 | 390 | downstream_component = bt_port_borrow_component_inline( |
d94d92ac | 391 | (void *) downstream_port); |
262e5473 | 392 | |
262e5473 | 393 | BT_LOGD_STR("Creating connection."); |
d94d92ac PP |
394 | connection = bt_connection_create(graph, (void *) upstream_port, |
395 | (void *) downstream_port); | |
f60c8b34 | 396 | if (!connection) { |
870631a2 | 397 | BT_LIB_LOGE_APPEND_CAUSE("Cannot create connection object."); |
d24d5663 | 398 | status = BT_FUNC_STATUS_MEMORY_ERROR; |
a256a42d | 399 | goto end; |
f60c8b34 JG |
400 | } |
401 | ||
d94d92ac | 402 | BT_LIB_LOGD("Connection object created: %!+x", connection); |
262e5473 | 403 | |
f60c8b34 | 404 | /* |
72b913fb PP |
405 | * Ownership of upstream_component/downstream_component and of |
406 | * the connection object is transferred to the graph. | |
f60c8b34 JG |
407 | */ |
408 | g_ptr_array_add(graph->connections, connection); | |
ffeb0eed | 409 | |
f60c8b34 | 410 | /* |
0d8b4d8e | 411 | * Notify both components that their port is connected. |
f60c8b34 | 412 | */ |
d94d92ac PP |
413 | BT_LIB_LOGD("Notifying upstream component that its port is connected: " |
414 | "%![comp-]+c, %![port-]+p", upstream_component, upstream_port); | |
d24d5663 | 415 | port_connected_status = bt_component_port_connected(upstream_component, |
d94d92ac | 416 | (void *) upstream_port, (void *) downstream_port); |
d24d5663 | 417 | if (port_connected_status != BT_FUNC_STATUS_OK) { |
870631a2 PP |
418 | if (port_connected_status < 0) { |
419 | BT_LIB_LOGW_APPEND_CAUSE( | |
420 | "Upstream component's \"port connected\" method failed: " | |
421 | "status=%s, %![graph-]+g, %![up-comp-]+c, " | |
422 | "%![down-comp-]+c, %![up-port-]+p, %![down-port-]+p", | |
423 | bt_common_func_status_string( | |
424 | port_connected_status), | |
425 | graph, upstream_component, downstream_component, | |
426 | upstream_port, downstream_port); | |
427 | } | |
428 | ||
bf55043c | 429 | bt_connection_end(connection, true); |
d24d5663 | 430 | status = (int) port_connected_status; |
bf55043c PP |
431 | goto end; |
432 | } | |
433 | ||
85031ceb | 434 | connection->notified_upstream_port_connected = true; |
d94d92ac PP |
435 | BT_LIB_LOGD("Notifying downstream component that its port is connected: " |
436 | "%![comp-]+c, %![port-]+p", downstream_component, | |
437 | downstream_port); | |
d24d5663 | 438 | port_connected_status = bt_component_port_connected(downstream_component, |
d94d92ac | 439 | (void *) downstream_port, (void *) upstream_port); |
d24d5663 | 440 | if (port_connected_status != BT_FUNC_STATUS_OK) { |
870631a2 PP |
441 | if (port_connected_status < 0) { |
442 | BT_LIB_LOGW_APPEND_CAUSE( | |
443 | "Downstream component's \"port connected\" method failed: " | |
444 | "status=%s, %![graph-]+g, %![up-comp-]+c, " | |
445 | "%![down-comp-]+c, %![up-port-]+p, %![down-port-]+p", | |
446 | bt_common_func_status_string( | |
447 | port_connected_status), | |
448 | graph, upstream_component, downstream_component, | |
449 | upstream_port, downstream_port); | |
450 | } | |
451 | ||
bf55043c | 452 | bt_connection_end(connection, true); |
d24d5663 | 453 | status = (int) port_connected_status; |
bf55043c PP |
454 | goto end; |
455 | } | |
456 | ||
85031ceb | 457 | connection->notified_downstream_port_connected = true; |
1bf957a0 | 458 | |
3f7d4d90 | 459 | BT_LIB_LOGI("Connected component ports within graph: " |
d94d92ac PP |
460 | "%![graph-]+g, %![up-comp-]+c, %![down-comp-]+c, " |
461 | "%![up-port-]+p, %![down-port-]+p", | |
462 | graph, upstream_component, downstream_component, | |
463 | upstream_port, downstream_port); | |
1bf957a0 | 464 | |
a256a42d | 465 | if (user_connection) { |
1a6a376a PP |
466 | /* Move reference to user */ |
467 | *user_connection = connection; | |
468 | connection = NULL; | |
a256a42d PP |
469 | } |
470 | ||
f60c8b34 | 471 | end: |
d24d5663 | 472 | if (status != BT_FUNC_STATUS_OK) { |
8cc56726 | 473 | bt_graph_make_faulty(graph); |
38cda5da PP |
474 | } |
475 | ||
65300d60 | 476 | bt_object_put_ref(connection); |
d94d92ac PP |
477 | (void) init_can_consume; |
478 | bt_graph_set_can_consume(graph, init_can_consume); | |
a256a42d | 479 | return status; |
f60c8b34 JG |
480 | } |
481 | ||
ad847455 | 482 | static inline |
d24d5663 | 483 | int consume_graph_sink(struct bt_component_sink *comp) |
c0418dd9 | 484 | { |
d24d5663 | 485 | enum bt_component_class_sink_consume_method_status consume_status; |
d94d92ac PP |
486 | struct bt_component_class_sink *sink_class = NULL; |
487 | ||
98b15851 | 488 | BT_ASSERT_DBG(comp); |
d94d92ac | 489 | sink_class = (void *) comp->parent.class; |
98b15851 | 490 | BT_ASSERT_DBG(sink_class->methods.consume); |
d94d92ac | 491 | BT_LIB_LOGD("Calling user's consume method: %!+c", comp); |
d24d5663 | 492 | consume_status = sink_class->methods.consume((void *) comp); |
d94d92ac | 493 | BT_LOGD("User method returned: status=%s", |
d24d5663 | 494 | bt_common_func_status_string(consume_status)); |
bdb288b3 | 495 | BT_ASSERT_POST_DEV(consume_status == BT_FUNC_STATUS_OK || |
d24d5663 PP |
496 | consume_status == BT_FUNC_STATUS_END || |
497 | consume_status == BT_FUNC_STATUS_AGAIN || | |
498 | consume_status == BT_FUNC_STATUS_ERROR || | |
499 | consume_status == BT_FUNC_STATUS_MEMORY_ERROR, | |
d94d92ac | 500 | "Invalid component status returned by consuming method: " |
d24d5663 | 501 | "status=%s", bt_common_func_status_string(consume_status)); |
6ecdcca3 | 502 | BT_ASSERT_POST_DEV_NO_ERROR_IF_NO_ERROR_STATUS(consume_status); |
870631a2 PP |
503 | if (consume_status) { |
504 | if (consume_status < 0) { | |
505 | BT_LIB_LOGW_APPEND_CAUSE( | |
506 | "Component's \"consume\" method failed: " | |
507 | "status=%s, %![comp-]+c", | |
508 | bt_common_func_status_string(consume_status), | |
509 | comp); | |
510 | } | |
511 | ||
d94d92ac PP |
512 | goto end; |
513 | } | |
514 | ||
3f7d4d90 | 515 | BT_LIB_LOGD("Consumed from sink: %![comp-]+c, status=%s", |
d24d5663 | 516 | comp, bt_common_func_status_string(consume_status)); |
d94d92ac PP |
517 | |
518 | end: | |
d24d5663 | 519 | return consume_status; |
8ed535b5 PP |
520 | } |
521 | ||
522 | /* | |
523 | * `node` is removed from the queue of sinks to consume when passed to | |
524 | * this function. This function adds it back to the queue if there's | |
525 | * still something to consume afterwards. | |
526 | */ | |
ad847455 | 527 | static inline |
d24d5663 | 528 | int consume_sink_node(struct bt_graph *graph, GList *node) |
8ed535b5 | 529 | { |
d24d5663 | 530 | int status; |
d94d92ac | 531 | struct bt_component_sink *sink; |
8ed535b5 PP |
532 | |
533 | sink = node->data; | |
534 | status = consume_graph_sink(sink); | |
d24d5663 | 535 | if (G_UNLIKELY(status != BT_FUNC_STATUS_END)) { |
8ed535b5 | 536 | g_queue_push_tail_link(graph->sinks_to_consume, node); |
f60c8b34 JG |
537 | goto end; |
538 | } | |
539 | ||
540 | /* End reached, the node is not added back to the queue and free'd. */ | |
8ed535b5 | 541 | g_queue_delete_link(graph->sinks_to_consume, node); |
f60c8b34 JG |
542 | |
543 | /* Don't forward an END status if there are sinks left to consume. */ | |
544 | if (!g_queue_is_empty(graph->sinks_to_consume)) { | |
d24d5663 | 545 | status = BT_FUNC_STATUS_OK; |
f60c8b34 JG |
546 | goto end; |
547 | } | |
8ed535b5 PP |
548 | |
549 | end: | |
3f7d4d90 | 550 | BT_LIB_LOGD("Consumed sink node: %![comp-]+c, status=%s", |
d24d5663 | 551 | sink, bt_common_func_status_string(status)); |
8ed535b5 PP |
552 | return status; |
553 | } | |
554 | ||
555 | BT_HIDDEN | |
d24d5663 | 556 | int bt_graph_consume_sink_no_check(struct bt_graph *graph, |
d94d92ac | 557 | struct bt_component_sink *sink) |
8ed535b5 | 558 | { |
d24d5663 | 559 | int status; |
8ed535b5 PP |
560 | GList *sink_node; |
561 | int index; | |
562 | ||
3f7d4d90 | 563 | BT_LIB_LOGD("Making specific sink consume: %![comp-]+c", sink); |
98b15851 | 564 | BT_ASSERT_DBG(bt_component_borrow_graph((void *) sink) == graph); |
8ed535b5 PP |
565 | |
566 | if (g_queue_is_empty(graph->sinks_to_consume)) { | |
3f7d4d90 | 567 | BT_LOGD_STR("Graph's sink queue is empty: end of graph."); |
d24d5663 | 568 | status = BT_FUNC_STATUS_END; |
8ed535b5 PP |
569 | goto end; |
570 | } | |
571 | ||
572 | index = g_queue_index(graph->sinks_to_consume, sink); | |
573 | if (index < 0) { | |
3f7d4d90 PP |
574 | BT_LIB_LOGD("Sink component is not marked as consumable: " |
575 | "component sink is ended: %![comp-]+c", sink); | |
d24d5663 | 576 | status = BT_FUNC_STATUS_END; |
8ed535b5 PP |
577 | goto end; |
578 | } | |
579 | ||
580 | sink_node = g_queue_pop_nth_link(graph->sinks_to_consume, index); | |
98b15851 | 581 | BT_ASSERT_DBG(sink_node); |
8ed535b5 PP |
582 | status = consume_sink_node(graph, sink_node); |
583 | ||
584 | end: | |
585 | return status; | |
586 | } | |
587 | ||
ad847455 | 588 | static inline |
d24d5663 | 589 | int consume_no_check(struct bt_graph *graph) |
8ed535b5 | 590 | { |
d24d5663 | 591 | int status = BT_FUNC_STATUS_OK; |
8ed535b5 PP |
592 | struct bt_component *sink; |
593 | GList *current_node; | |
594 | ||
bdb288b3 | 595 | BT_ASSERT_PRE_DEV(graph->has_sink, |
f6ccaed9 | 596 | "Graph has no sink component: %!+g", graph); |
3f7d4d90 | 597 | BT_LIB_LOGD("Making next sink component consume: %![graph-]+g", graph); |
8ed535b5 | 598 | |
91d81473 | 599 | if (G_UNLIKELY(g_queue_is_empty(graph->sinks_to_consume))) { |
3f7d4d90 | 600 | BT_LOGD_STR("Graph's sink queue is empty: end of graph."); |
d24d5663 | 601 | status = BT_FUNC_STATUS_END; |
8ed535b5 PP |
602 | goto end; |
603 | } | |
604 | ||
605 | current_node = g_queue_pop_head_link(graph->sinks_to_consume); | |
606 | sink = current_node->data; | |
3f7d4d90 | 607 | BT_LIB_LOGD("Chose next sink to consume: %!+c", sink); |
8ed535b5 PP |
608 | status = consume_sink_node(graph, current_node); |
609 | ||
f60c8b34 JG |
610 | end: |
611 | return status; | |
c0418dd9 JG |
612 | } |
613 | ||
648dab91 | 614 | enum bt_graph_run_once_status bt_graph_run_once(struct bt_graph *graph) |
851b70bd | 615 | { |
648dab91 | 616 | enum bt_graph_run_once_status status; |
1d915789 | 617 | |
17f3083a | 618 | BT_ASSERT_PRE_NO_ERROR(); |
bdb288b3 | 619 | BT_ASSERT_PRE_DEV_NON_NULL(graph, "Graph"); |
bdb288b3 | 620 | BT_ASSERT_PRE_DEV(graph->can_consume, |
f6ccaed9 | 621 | "Cannot consume graph in its current state: %!+g", graph); |
bdb288b3 PP |
622 | BT_ASSERT_PRE_DEV(graph->config_state != |
623 | BT_GRAPH_CONFIGURATION_STATE_FAULTY, | |
38cda5da | 624 | "Graph is in a faulty state: %!+g", graph); |
4725a201 | 625 | bt_graph_set_can_consume(graph, false); |
5badd463 | 626 | status = bt_graph_configure(graph); |
91d81473 | 627 | if (G_UNLIKELY(status)) { |
5badd463 PP |
628 | /* bt_graph_configure() logs errors */ |
629 | goto end; | |
630 | } | |
631 | ||
d94d92ac | 632 | status = consume_no_check(graph); |
4725a201 | 633 | bt_graph_set_can_consume(graph, true); |
5badd463 PP |
634 | |
635 | end: | |
26a15756 | 636 | return status; |
851b70bd PP |
637 | } |
638 | ||
d24d5663 | 639 | enum bt_graph_run_status bt_graph_run(struct bt_graph *graph) |
f60c8b34 | 640 | { |
d24d5663 | 641 | enum bt_graph_run_status status; |
f60c8b34 | 642 | |
17f3083a | 643 | BT_ASSERT_PRE_NO_ERROR(); |
d94d92ac | 644 | BT_ASSERT_PRE_NON_NULL(graph, "Graph"); |
ad847455 PP |
645 | BT_ASSERT_PRE(graph->can_consume, |
646 | "Cannot consume graph in its current state: %!+g", graph); | |
38cda5da PP |
647 | BT_ASSERT_PRE(graph->config_state != BT_GRAPH_CONFIGURATION_STATE_FAULTY, |
648 | "Graph is in a faulty state: %!+g", graph); | |
4725a201 | 649 | bt_graph_set_can_consume(graph, false); |
5badd463 | 650 | status = bt_graph_configure(graph); |
91d81473 | 651 | if (G_UNLIKELY(status)) { |
5badd463 PP |
652 | /* bt_graph_configure() logs errors */ |
653 | goto end; | |
654 | } | |
655 | ||
3f7d4d90 | 656 | BT_LIB_LOGI("Running graph: %!+g", graph); |
262e5473 | 657 | |
f60c8b34 | 658 | do { |
851b70bd | 659 | /* |
9b4f9b42 PP |
660 | * Check if the graph is interrupted at each iteration. |
661 | * If the graph was interrupted by another thread or by | |
662 | * a signal handler, this is NOT a warning nor an error; | |
663 | * it was intentional: log with an INFO level only. | |
851b70bd | 664 | */ |
9b4f9b42 PP |
665 | if (G_UNLIKELY(bt_graph_is_interrupted(graph))) { |
666 | BT_LIB_LOGI("Stopping the graph: " | |
667 | "graph was interrupted: %!+g", graph); | |
668 | status = BT_FUNC_STATUS_AGAIN; | |
851b70bd PP |
669 | goto end; |
670 | } | |
671 | ||
d94d92ac | 672 | status = consume_no_check(graph); |
d24d5663 | 673 | if (G_UNLIKELY(status == BT_FUNC_STATUS_AGAIN)) { |
f60c8b34 | 674 | /* |
202a3a13 PP |
675 | * If AGAIN is received and there are multiple |
676 | * sinks, go ahead and consume from the next | |
677 | * sink. | |
f60c8b34 | 678 | * |
202a3a13 PP |
679 | * However, in the case where a single sink is |
680 | * left, the caller can decide to busy-wait and | |
0d72b8c3 | 681 | * call bt_graph_run() continuously |
d94d92ac PP |
682 | * until the source is ready or it can decide to |
683 | * sleep for an arbitrary amount of time. | |
f60c8b34 JG |
684 | */ |
685 | if (graph->sinks_to_consume->length > 1) { | |
d24d5663 | 686 | status = BT_FUNC_STATUS_OK; |
f60c8b34 JG |
687 | } |
688 | } | |
d24d5663 | 689 | } while (status == BT_FUNC_STATUS_OK); |
f60c8b34 | 690 | |
9669d693 PP |
691 | if (status == BT_FUNC_STATUS_END) { |
692 | /* | |
693 | * The last call to consume_no_check() returned | |
694 | * `BT_FUNC_STATUS_END`, but bt_graph_run() has no | |
695 | * `BT_GRAPH_RUN_STATUS_END` status: replace with | |
696 | * `BT_GRAPH_RUN_STATUS_OK` (success: graph ran | |
697 | * completely). | |
698 | */ | |
699 | status = BT_FUNC_STATUS_OK; | |
f60c8b34 | 700 | } |
262e5473 | 701 | |
202a3a13 | 702 | end: |
3f7d4d90 | 703 | BT_LIB_LOGI("Graph ran: %![graph-]+g, status=%s", graph, |
d24d5663 | 704 | bt_common_func_status_string(status)); |
4725a201 | 705 | bt_graph_set_can_consume(graph, true); |
72b913fb | 706 | return status; |
f60c8b34 | 707 | } |
1bf957a0 | 708 | |
d24d5663 | 709 | enum bt_graph_add_listener_status |
0d72b8c3 PP |
710 | bt_graph_add_source_component_output_port_added_listener( |
711 | struct bt_graph *graph, | |
712 | bt_graph_source_component_output_port_added_listener_func func, | |
713 | bt_graph_listener_removed_func listener_removed, void *data, | |
2054a0d1 | 714 | bt_listener_id *out_listener_id) |
1bf957a0 | 715 | { |
d94d92ac PP |
716 | struct bt_graph_listener_port_added listener = { |
717 | .base = { | |
718 | .removed = listener_removed, | |
719 | .data = data, | |
720 | }, | |
721 | .func = (port_added_func_t) func, | |
1bf957a0 | 722 | }; |
2054a0d1 | 723 | bt_listener_id listener_id; |
1bf957a0 | 724 | |
17f3083a | 725 | BT_ASSERT_PRE_NO_ERROR(); |
d94d92ac PP |
726 | BT_ASSERT_PRE_NON_NULL(graph, "Graph"); |
727 | BT_ASSERT_PRE_NON_NULL(func, "Listener"); | |
728 | BT_ASSERT_PRE_NON_NULL(func, "\"Listener removed\" listener"); | |
729 | BT_ASSERT_PRE(!graph->in_remove_listener, | |
730 | "Graph currently executing a \"listener removed\" listener: " | |
731 | "%!+g", graph); | |
732 | g_array_append_val(graph->listeners.source_output_port_added, listener); | |
733 | listener_id = graph->listeners.source_output_port_added->len - 1; | |
3f7d4d90 | 734 | BT_LIB_LOGD("Added \"source component output port added\" listener to graph: " |
d94d92ac PP |
735 | "%![graph-]+g, listener-addr=%p, id=%d", graph, listener, |
736 | listener_id); | |
737 | ||
738 | if (listener_id) { | |
739 | *out_listener_id = listener_id; | |
740 | } | |
741 | ||
d24d5663 | 742 | return BT_FUNC_STATUS_OK; |
1bf957a0 PP |
743 | } |
744 | ||
d24d5663 | 745 | enum bt_graph_add_listener_status |
0d72b8c3 PP |
746 | bt_graph_add_filter_component_output_port_added_listener( |
747 | struct bt_graph *graph, | |
748 | bt_graph_filter_component_output_port_added_listener_func func, | |
749 | bt_graph_listener_removed_func listener_removed, void *data, | |
2054a0d1 | 750 | bt_listener_id *out_listener_id) |
1bf957a0 | 751 | { |
d94d92ac PP |
752 | struct bt_graph_listener_port_added listener = { |
753 | .base = { | |
754 | .removed = listener_removed, | |
755 | .data = data, | |
756 | }, | |
757 | .func = (port_added_func_t) func, | |
758 | }; | |
2054a0d1 | 759 | bt_listener_id listener_id; |
1bf957a0 | 760 | |
17f3083a | 761 | BT_ASSERT_PRE_NO_ERROR(); |
d94d92ac PP |
762 | BT_ASSERT_PRE_NON_NULL(graph, "Graph"); |
763 | BT_ASSERT_PRE_NON_NULL(func, "Listener"); | |
764 | BT_ASSERT_PRE_NON_NULL(func, "\"Listener removed\" listener"); | |
765 | BT_ASSERT_PRE(!graph->in_remove_listener, | |
766 | "Graph currently executing a \"listener removed\" listener: " | |
767 | "%!+g", graph); | |
768 | g_array_append_val(graph->listeners.filter_output_port_added, listener); | |
769 | listener_id = graph->listeners.filter_output_port_added->len - 1; | |
3f7d4d90 | 770 | BT_LIB_LOGD("Added \"filter component output port added\" listener to graph: " |
d94d92ac PP |
771 | "%![graph-]+g, listener-addr=%p, id=%d", graph, listener, |
772 | listener_id); | |
773 | ||
774 | if (listener_id) { | |
775 | *out_listener_id = listener_id; | |
776 | } | |
777 | ||
d24d5663 | 778 | return BT_FUNC_STATUS_OK; |
d94d92ac | 779 | } |
262e5473 | 780 | |
d24d5663 | 781 | enum bt_graph_add_listener_status |
0d72b8c3 PP |
782 | bt_graph_add_filter_component_input_port_added_listener( |
783 | struct bt_graph *graph, | |
784 | bt_graph_filter_component_input_port_added_listener_func func, | |
785 | bt_graph_listener_removed_func listener_removed, void *data, | |
2054a0d1 | 786 | bt_listener_id *out_listener_id) |
d94d92ac | 787 | { |
d94d92ac PP |
788 | struct bt_graph_listener_port_added listener = { |
789 | .base = { | |
790 | .removed = listener_removed, | |
791 | .data = data, | |
792 | }, | |
793 | .func = (port_added_func_t) func, | |
794 | }; | |
2054a0d1 | 795 | bt_listener_id listener_id; |
8cc092c9 | 796 | |
17f3083a | 797 | BT_ASSERT_PRE_NO_ERROR(); |
d94d92ac PP |
798 | BT_ASSERT_PRE_NON_NULL(graph, "Graph"); |
799 | BT_ASSERT_PRE_NON_NULL(func, "Listener"); | |
800 | BT_ASSERT_PRE_NON_NULL(func, "\"Listener removed\" listener"); | |
801 | BT_ASSERT_PRE(!graph->in_remove_listener, | |
802 | "Graph currently executing a \"listener removed\" listener: " | |
803 | "%!+g", graph); | |
804 | g_array_append_val(graph->listeners.filter_input_port_added, listener); | |
805 | listener_id = graph->listeners.filter_input_port_added->len - 1; | |
3f7d4d90 | 806 | BT_LIB_LOGD("Added \"filter component input port added\" listener to graph: " |
d94d92ac PP |
807 | "%![graph-]+g, listener-addr=%p, id=%d", graph, listener, |
808 | listener_id); | |
809 | ||
810 | if (listener_id) { | |
811 | *out_listener_id = listener_id; | |
812 | } | |
813 | ||
d24d5663 | 814 | return BT_FUNC_STATUS_OK; |
d94d92ac | 815 | } |
1bf957a0 | 816 | |
d24d5663 | 817 | enum bt_graph_add_listener_status |
0d72b8c3 PP |
818 | bt_graph_add_sink_component_input_port_added_listener( |
819 | struct bt_graph *graph, | |
820 | bt_graph_sink_component_input_port_added_listener_func func, | |
821 | bt_graph_listener_removed_func listener_removed, void *data, | |
2054a0d1 | 822 | bt_listener_id *out_listener_id) |
d94d92ac | 823 | { |
d94d92ac PP |
824 | struct bt_graph_listener_port_added listener = { |
825 | .base = { | |
826 | .removed = listener_removed, | |
827 | .data = data, | |
828 | }, | |
829 | .func = (port_added_func_t) func, | |
830 | }; | |
2054a0d1 | 831 | bt_listener_id listener_id; |
1bf957a0 | 832 | |
17f3083a | 833 | BT_ASSERT_PRE_NO_ERROR(); |
d94d92ac PP |
834 | BT_ASSERT_PRE_NON_NULL(graph, "Graph"); |
835 | BT_ASSERT_PRE_NON_NULL(func, "Listener"); | |
836 | BT_ASSERT_PRE_NON_NULL(func, "\"Listener removed\" listener"); | |
837 | BT_ASSERT_PRE(!graph->in_remove_listener, | |
838 | "Graph currently executing a \"listener removed\" listener: " | |
839 | "%!+g", graph); | |
840 | g_array_append_val(graph->listeners.sink_input_port_added, listener); | |
841 | listener_id = graph->listeners.sink_input_port_added->len - 1; | |
3f7d4d90 | 842 | BT_LIB_LOGD("Added \"sink component input port added\" listener to graph: " |
d94d92ac PP |
843 | "%![graph-]+g, listener-addr=%p, id=%d", graph, listener, |
844 | listener_id); | |
845 | ||
846 | if (listener_id) { | |
847 | *out_listener_id = listener_id; | |
848 | } | |
849 | ||
d24d5663 | 850 | return BT_FUNC_STATUS_OK; |
1bf957a0 PP |
851 | } |
852 | ||
1bf957a0 | 853 | BT_HIDDEN |
d24d5663 | 854 | enum bt_graph_listener_func_status bt_graph_notify_port_added( |
8cc56726 | 855 | struct bt_graph *graph, struct bt_port *port) |
1bf957a0 | 856 | { |
d94d92ac PP |
857 | uint64_t i; |
858 | GArray *listeners; | |
859 | struct bt_component *comp; | |
d24d5663 | 860 | enum bt_graph_listener_func_status status = BT_FUNC_STATUS_OK; |
1bf957a0 | 861 | |
d94d92ac PP |
862 | BT_ASSERT(graph); |
863 | BT_ASSERT(port); | |
3f7d4d90 | 864 | BT_LIB_LOGD("Notifying graph listeners that a port was added: " |
d94d92ac | 865 | "%![graph-]+g, %![port-]+p", graph, port); |
0d72b8c3 | 866 | comp = bt_port_borrow_component_inline(port); |
d94d92ac PP |
867 | BT_ASSERT(comp); |
868 | ||
869 | switch (comp->class->type) { | |
870 | case BT_COMPONENT_CLASS_TYPE_SOURCE: | |
871 | { | |
872 | switch (port->type) { | |
873 | case BT_PORT_TYPE_OUTPUT: | |
874 | listeners = graph->listeners.source_output_port_added; | |
875 | break; | |
876 | default: | |
498e7994 | 877 | bt_common_abort(); |
d94d92ac PP |
878 | } |
879 | ||
880 | break; | |
881 | } | |
882 | case BT_COMPONENT_CLASS_TYPE_FILTER: | |
883 | { | |
884 | switch (port->type) { | |
885 | case BT_PORT_TYPE_INPUT: | |
886 | listeners = graph->listeners.filter_input_port_added; | |
887 | break; | |
888 | case BT_PORT_TYPE_OUTPUT: | |
889 | listeners = graph->listeners.filter_output_port_added; | |
890 | break; | |
891 | default: | |
498e7994 | 892 | bt_common_abort(); |
d94d92ac | 893 | } |
262e5473 | 894 | |
d94d92ac PP |
895 | break; |
896 | } | |
897 | case BT_COMPONENT_CLASS_TYPE_SINK: | |
898 | { | |
899 | switch (port->type) { | |
900 | case BT_PORT_TYPE_INPUT: | |
901 | listeners = graph->listeners.sink_input_port_added; | |
902 | break; | |
903 | default: | |
498e7994 | 904 | bt_common_abort(); |
d94d92ac | 905 | } |
1bf957a0 | 906 | |
d94d92ac PP |
907 | break; |
908 | } | |
909 | default: | |
498e7994 | 910 | bt_common_abort(); |
d94d92ac PP |
911 | } |
912 | ||
913 | for (i = 0; i < listeners->len; i++) { | |
914 | struct bt_graph_listener_port_added *listener = | |
915 | &g_array_index(listeners, | |
916 | struct bt_graph_listener_port_added, i); | |
917 | ||
8cc56726 | 918 | |
d94d92ac | 919 | BT_ASSERT(listener->func); |
8cc56726 | 920 | status = listener->func(comp, port, listener->base.data); |
6ecdcca3 | 921 | BT_ASSERT_POST_NO_ERROR_IF_NO_ERROR_STATUS(status); |
d24d5663 | 922 | if (status != BT_FUNC_STATUS_OK) { |
8cc56726 SM |
923 | goto end; |
924 | } | |
1bf957a0 | 925 | } |
8cc56726 SM |
926 | |
927 | end: | |
928 | return status; | |
1bf957a0 PP |
929 | } |
930 | ||
f167d3c0 PP |
931 | BT_HIDDEN |
932 | void bt_graph_remove_connection(struct bt_graph *graph, | |
933 | struct bt_connection *connection) | |
934 | { | |
f6ccaed9 PP |
935 | BT_ASSERT(graph); |
936 | BT_ASSERT(connection); | |
3f7d4d90 | 937 | BT_LIB_LOGD("Removing graph's connection: %![graph-]+g, %![conn-]+x", |
262e5473 | 938 | graph, connection); |
f167d3c0 PP |
939 | g_ptr_array_remove(graph->connections, connection); |
940 | } | |
36712f1d | 941 | |
d94d92ac PP |
942 | static inline |
943 | bool component_name_exists(struct bt_graph *graph, const char *name) | |
944 | { | |
945 | bool exists = false; | |
946 | uint64_t i; | |
947 | ||
948 | for (i = 0; i < graph->components->len; i++) { | |
949 | struct bt_component *other_comp = graph->components->pdata[i]; | |
950 | ||
951 | if (strcmp(name, bt_component_get_name(other_comp)) == 0) { | |
952 | BT_ASSERT_PRE_MSG("Another component with the same name already exists in the graph: " | |
953 | "%![other-comp-]+c, name=\"%s\"", | |
954 | other_comp, name); | |
955 | exists = true; | |
956 | goto end; | |
957 | } | |
958 | } | |
959 | ||
960 | end: | |
961 | return exists; | |
962 | } | |
963 | ||
964 | static | |
d24d5663 | 965 | int add_component_with_init_method_data( |
0d72b8c3 | 966 | struct bt_graph *graph, |
d94d92ac PP |
967 | struct bt_component_class *comp_cls, |
968 | comp_init_method_t init_method, | |
05e21286 | 969 | const char *name, const struct bt_value *params, |
e874da19 | 970 | void *init_method_data, bt_logging_level log_level, |
9628043c | 971 | const struct bt_component **user_component) |
36712f1d | 972 | { |
d24d5663 | 973 | int status = BT_FUNC_STATUS_OK; |
21a9f056 | 974 | enum bt_component_class_initialize_method_status init_status; |
36712f1d | 975 | struct bt_component *component = NULL; |
d94d92ac PP |
976 | int ret; |
977 | bool init_can_consume; | |
05e21286 | 978 | struct bt_value *new_params = NULL; |
36712f1d | 979 | |
d94d92ac PP |
980 | BT_ASSERT(comp_cls); |
981 | BT_ASSERT_PRE_NON_NULL(graph, "Graph"); | |
982 | BT_ASSERT_PRE_NON_NULL(name, "Name"); | |
5badd463 PP |
983 | BT_ASSERT_PRE( |
984 | graph->config_state == BT_GRAPH_CONFIGURATION_STATE_CONFIGURING, | |
38cda5da | 985 | "Graph is not in the \"configuring\" state: %!+g", graph); |
d94d92ac PP |
986 | BT_ASSERT_PRE(!component_name_exists(graph, name), |
987 | "Duplicate component name: %!+g, name=\"%s\"", graph, name); | |
988 | BT_ASSERT_PRE(!params || bt_value_is_map(params), | |
989 | "Parameter value is not a map value: %!+v", params); | |
4aa7981f | 990 | init_can_consume = graph->can_consume; |
d94d92ac | 991 | bt_graph_set_can_consume(graph, false); |
3f7d4d90 | 992 | BT_LIB_LOGI("Adding component to graph: " |
e874da19 PP |
993 | "%![graph-]+g, %![cc-]+C, name=\"%s\", log-level=%s, " |
994 | "%![params-]+v, init-method-data-addr=%p", | |
995 | graph, comp_cls, name, | |
996 | bt_common_logging_level_string(log_level), params, | |
997 | init_method_data); | |
36712f1d | 998 | |
d94d92ac | 999 | if (!params) { |
05e21286 PP |
1000 | new_params = bt_value_map_create(); |
1001 | if (!new_params) { | |
870631a2 PP |
1002 | BT_LIB_LOGE_APPEND_CAUSE( |
1003 | "Cannot create empty map value object."); | |
d24d5663 | 1004 | status = BT_FUNC_STATUS_MEMORY_ERROR; |
36712f1d PP |
1005 | goto end; |
1006 | } | |
05e21286 PP |
1007 | |
1008 | params = new_params; | |
36712f1d PP |
1009 | } |
1010 | ||
e874da19 | 1011 | ret = bt_component_create(comp_cls, name, log_level, &component); |
d94d92ac | 1012 | if (ret) { |
870631a2 PP |
1013 | BT_LIB_LOGE_APPEND_CAUSE( |
1014 | "Cannot create empty component object: ret=%d", | |
d94d92ac | 1015 | ret); |
d24d5663 | 1016 | status = BT_FUNC_STATUS_MEMORY_ERROR; |
36712f1d PP |
1017 | goto end; |
1018 | } | |
1019 | ||
1020 | /* | |
1021 | * The user's initialization method needs to see that this | |
1022 | * component is part of the graph. If the user method fails, we | |
1023 | * immediately remove the component from the graph's components. | |
1024 | */ | |
1025 | g_ptr_array_add(graph->components, component); | |
1026 | bt_component_set_graph(component, graph); | |
0d47d31b | 1027 | bt_value_freeze(params); |
36712f1d | 1028 | |
d94d92ac | 1029 | if (init_method) { |
59225a3e SM |
1030 | /* |
1031 | * There is no use for config objects right now, so just pass | |
1032 | * NULL. | |
1033 | */ | |
36712f1d | 1034 | BT_LOGD_STR("Calling user's initialization method."); |
59225a3e | 1035 | init_status = init_method(component, NULL, params, init_method_data); |
36712f1d | 1036 | BT_LOGD("User method returned: status=%s", |
d24d5663 | 1037 | bt_common_func_status_string(init_status)); |
6ecdcca3 | 1038 | BT_ASSERT_POST_DEV_NO_ERROR_IF_NO_ERROR_STATUS(init_status); |
d24d5663 | 1039 | if (init_status != BT_FUNC_STATUS_OK) { |
870631a2 PP |
1040 | if (init_status < 0) { |
1041 | BT_LIB_LOGW_APPEND_CAUSE( | |
1042 | "Component initialization method failed: " | |
1043 | "status=%s, %![comp-]+c", | |
1044 | bt_common_func_status_string(init_status), | |
1045 | component); | |
1046 | } | |
1047 | ||
d24d5663 | 1048 | status = init_status; |
36712f1d PP |
1049 | bt_component_set_graph(component, NULL); |
1050 | g_ptr_array_remove_fast(graph->components, component); | |
1051 | goto end; | |
1052 | } | |
1053 | } | |
1054 | ||
1055 | /* | |
1056 | * Mark the component as initialized so that its finalization | |
1057 | * method is called when it is destroyed. | |
1058 | */ | |
1059 | component->initialized = true; | |
1060 | ||
1061 | /* | |
1062 | * If it's a sink component, it needs to be part of the graph's | |
648dab91 PP |
1063 | * sink queue to be consumed by bt_graph_run() or |
1064 | * bt_graph_run_once(). | |
36712f1d PP |
1065 | */ |
1066 | if (bt_component_is_sink(component)) { | |
d94d92ac | 1067 | graph->has_sink = true; |
36712f1d PP |
1068 | g_queue_push_tail(graph->sinks_to_consume, component); |
1069 | } | |
1070 | ||
1071 | /* | |
1072 | * Freeze the component class now that it's instantiated at | |
1073 | * least once. | |
1074 | */ | |
1075 | BT_LOGD_STR("Freezing component class."); | |
d94d92ac | 1076 | bt_component_class_freeze(comp_cls); |
3f7d4d90 | 1077 | BT_LIB_LOGI("Added component to graph: " |
e874da19 PP |
1078 | "%![graph-]+g, %![cc-]+C, name=\"%s\", log-level=%s, " |
1079 | "%![params-]+v, init-method-data-addr=%p, %![comp-]+c", | |
1080 | graph, comp_cls, name, | |
1081 | bt_common_logging_level_string(log_level), params, | |
1082 | init_method_data, component); | |
36712f1d PP |
1083 | |
1084 | if (user_component) { | |
1085 | /* Move reference to user */ | |
1086 | *user_component = component; | |
1087 | component = NULL; | |
1088 | } | |
1089 | ||
1090 | end: | |
d24d5663 | 1091 | if (status != BT_FUNC_STATUS_OK) { |
8cc56726 | 1092 | bt_graph_make_faulty(graph); |
38cda5da PP |
1093 | } |
1094 | ||
65300d60 | 1095 | bt_object_put_ref(component); |
05e21286 | 1096 | bt_object_put_ref(new_params); |
d94d92ac PP |
1097 | (void) init_can_consume; |
1098 | bt_graph_set_can_consume(graph, init_can_consume); | |
d24d5663 | 1099 | return status; |
36712f1d PP |
1100 | } |
1101 | ||
d24d5663 | 1102 | enum bt_graph_add_component_status |
21a9f056 | 1103 | bt_graph_add_source_component_with_initialize_method_data( |
0d72b8c3 PP |
1104 | struct bt_graph *graph, |
1105 | const struct bt_component_class_source *comp_cls, | |
05e21286 | 1106 | const char *name, const struct bt_value *params, |
e874da19 | 1107 | void *init_method_data, bt_logging_level log_level, |
0d72b8c3 | 1108 | const struct bt_component_source **component) |
d94d92ac | 1109 | { |
17f3083a | 1110 | BT_ASSERT_PRE_NO_ERROR(); |
d94d92ac PP |
1111 | BT_ASSERT_PRE_NON_NULL(comp_cls, "Component class"); |
1112 | return add_component_with_init_method_data(graph, | |
1113 | (void *) comp_cls, (comp_init_method_t) comp_cls->methods.init, | |
e874da19 | 1114 | name, params, init_method_data, log_level, (void *) component); |
d94d92ac PP |
1115 | } |
1116 | ||
d24d5663 | 1117 | enum bt_graph_add_component_status bt_graph_add_source_component( |
0d72b8c3 PP |
1118 | struct bt_graph *graph, |
1119 | const struct bt_component_class_source *comp_cls, | |
05e21286 | 1120 | const char *name, const struct bt_value *params, |
9628043c | 1121 | enum bt_logging_level log_level, |
0d72b8c3 | 1122 | const struct bt_component_source **component) |
d94d92ac | 1123 | { |
17f3083a | 1124 | BT_ASSERT_PRE_NO_ERROR(); |
21a9f056 | 1125 | return bt_graph_add_source_component_with_initialize_method_data( |
e874da19 | 1126 | graph, comp_cls, name, params, NULL, log_level, component); |
d94d92ac PP |
1127 | } |
1128 | ||
d24d5663 | 1129 | enum bt_graph_add_component_status |
21a9f056 | 1130 | bt_graph_add_filter_component_with_initialize_method_data( |
0d72b8c3 PP |
1131 | struct bt_graph *graph, |
1132 | const struct bt_component_class_filter *comp_cls, | |
05e21286 | 1133 | const char *name, const struct bt_value *params, |
9628043c | 1134 | void *init_method_data, enum bt_logging_level log_level, |
0d72b8c3 | 1135 | const struct bt_component_filter **component) |
d94d92ac | 1136 | { |
17f3083a | 1137 | BT_ASSERT_PRE_NO_ERROR(); |
d94d92ac PP |
1138 | BT_ASSERT_PRE_NON_NULL(comp_cls, "Component class"); |
1139 | return add_component_with_init_method_data(graph, | |
1140 | (void *) comp_cls, (comp_init_method_t) comp_cls->methods.init, | |
e874da19 | 1141 | name, params, init_method_data, log_level, (void *) component); |
d94d92ac PP |
1142 | } |
1143 | ||
d24d5663 | 1144 | enum bt_graph_add_component_status bt_graph_add_filter_component( |
0d72b8c3 PP |
1145 | struct bt_graph *graph, |
1146 | const struct bt_component_class_filter *comp_cls, | |
05e21286 | 1147 | const char *name, const struct bt_value *params, |
9628043c | 1148 | enum bt_logging_level log_level, |
0d72b8c3 | 1149 | const struct bt_component_filter **component) |
d94d92ac | 1150 | { |
17f3083a | 1151 | BT_ASSERT_PRE_NO_ERROR(); |
21a9f056 | 1152 | return bt_graph_add_filter_component_with_initialize_method_data( |
e874da19 | 1153 | graph, comp_cls, name, params, NULL, log_level, component); |
d94d92ac PP |
1154 | } |
1155 | ||
d24d5663 | 1156 | enum bt_graph_add_component_status |
21a9f056 | 1157 | bt_graph_add_sink_component_with_initialize_method_data( |
0d72b8c3 PP |
1158 | struct bt_graph *graph, |
1159 | const struct bt_component_class_sink *comp_cls, | |
05e21286 | 1160 | const char *name, const struct bt_value *params, |
9628043c | 1161 | void *init_method_data, enum bt_logging_level log_level, |
0d72b8c3 | 1162 | const struct bt_component_sink **component) |
36712f1d | 1163 | { |
17f3083a | 1164 | BT_ASSERT_PRE_NO_ERROR(); |
d94d92ac PP |
1165 | BT_ASSERT_PRE_NON_NULL(comp_cls, "Component class"); |
1166 | return add_component_with_init_method_data(graph, | |
1167 | (void *) comp_cls, (comp_init_method_t) comp_cls->methods.init, | |
e874da19 | 1168 | name, params, init_method_data, log_level, (void *) component); |
d94d92ac PP |
1169 | } |
1170 | ||
d24d5663 | 1171 | enum bt_graph_add_component_status bt_graph_add_sink_component( |
0d72b8c3 PP |
1172 | struct bt_graph *graph, |
1173 | const struct bt_component_class_sink *comp_cls, | |
05e21286 | 1174 | const char *name, const struct bt_value *params, |
9628043c | 1175 | enum bt_logging_level log_level, |
0d72b8c3 | 1176 | const struct bt_component_sink **component) |
d94d92ac | 1177 | { |
17f3083a | 1178 | BT_ASSERT_PRE_NO_ERROR(); |
21a9f056 | 1179 | return bt_graph_add_sink_component_with_initialize_method_data( |
e874da19 | 1180 | graph, comp_cls, name, params, NULL, log_level, component); |
36712f1d | 1181 | } |
8ed535b5 | 1182 | |
078033ed PP |
1183 | enum bt_graph_add_component_status |
1184 | bt_graph_add_simple_sink_component(struct bt_graph *graph, const char *name, | |
21a9f056 | 1185 | bt_graph_simple_sink_component_initialize_func init_func, |
078033ed PP |
1186 | bt_graph_simple_sink_component_consume_func consume_func, |
1187 | bt_graph_simple_sink_component_finalize_func finalize_func, | |
1188 | void *user_data, const bt_component_sink **component) | |
1189 | { | |
1190 | enum bt_graph_add_component_status status; | |
1191 | struct bt_component_class_sink *comp_cls; | |
1192 | struct simple_sink_init_method_data init_method_data = { | |
1193 | .init_func = init_func, | |
1194 | .consume_func = consume_func, | |
1195 | .finalize_func = finalize_func, | |
1196 | .user_data = user_data, | |
1197 | }; | |
1198 | ||
17f3083a SM |
1199 | BT_ASSERT_PRE_NO_ERROR(); |
1200 | ||
078033ed PP |
1201 | /* |
1202 | * Other preconditions are checked by | |
1203 | * bt_graph_add_sink_component_with_init_method_data(). | |
1204 | */ | |
1205 | BT_ASSERT_PRE_NON_NULL(consume_func, "Consume function"); | |
1206 | ||
1207 | comp_cls = bt_component_class_sink_simple_borrow(); | |
1208 | if (!comp_cls) { | |
1209 | BT_LIB_LOGE_APPEND_CAUSE( | |
1210 | "Cannot borrow simple sink component class."); | |
1211 | status = BT_FUNC_STATUS_MEMORY_ERROR; | |
1212 | goto end; | |
1213 | } | |
1214 | ||
21a9f056 | 1215 | status = bt_graph_add_sink_component_with_initialize_method_data(graph, |
078033ed PP |
1216 | comp_cls, name, NULL, &init_method_data, |
1217 | BT_LOGGING_LEVEL_NONE, component); | |
1218 | ||
1219 | end: | |
1220 | return status; | |
1221 | } | |
1222 | ||
5c563278 | 1223 | BT_HIDDEN |
d6e69534 PP |
1224 | void bt_graph_add_message(struct bt_graph *graph, |
1225 | struct bt_message *msg) | |
5c563278 PP |
1226 | { |
1227 | BT_ASSERT(graph); | |
d6e69534 | 1228 | BT_ASSERT(msg); |
5c563278 PP |
1229 | |
1230 | /* | |
1231 | * It's okay not to take a reference because, when a | |
d6e69534 | 1232 | * message's reference count drops to 0, either: |
5c563278 PP |
1233 | * |
1234 | * * It is recycled back to one of this graph's pool. | |
1235 | * * It is destroyed because it doesn't have any link to any | |
1236 | * graph, which means the original graph is already destroyed. | |
1237 | */ | |
d6e69534 | 1238 | g_ptr_array_add(graph->messages, msg); |
5c563278 | 1239 | } |
c5b9b441 | 1240 | |
9b4f9b42 PP |
1241 | BT_HIDDEN |
1242 | bool bt_graph_is_interrupted(const struct bt_graph *graph) | |
1243 | { | |
98b15851 | 1244 | BT_ASSERT_DBG(graph); |
9b4f9b42 PP |
1245 | return bt_interrupter_array_any_is_set(graph->interrupters); |
1246 | } | |
1247 | ||
1248 | enum bt_graph_add_interrupter_status bt_graph_add_interrupter( | |
1249 | struct bt_graph *graph, const struct bt_interrupter *intr) | |
1250 | { | |
17f3083a | 1251 | BT_ASSERT_PRE_NO_ERROR(); |
9b4f9b42 PP |
1252 | BT_ASSERT_PRE_NON_NULL(graph, "Graph"); |
1253 | BT_ASSERT_PRE_NON_NULL(intr, "Interrupter"); | |
1254 | g_ptr_array_add(graph->interrupters, (void *) intr); | |
6871026b | 1255 | bt_object_get_ref_no_null_check(intr); |
9b4f9b42 PP |
1256 | BT_LIB_LOGD("Added interrupter to graph: %![graph-]+g, %![intr-]+z", |
1257 | graph, intr); | |
1258 | return BT_FUNC_STATUS_OK; | |
1259 | } | |
1260 | ||
1261 | void bt_graph_interrupt(struct bt_graph *graph) | |
1262 | { | |
1263 | BT_ASSERT_PRE_NON_NULL(graph, "Graph"); | |
1264 | bt_interrupter_set(graph->default_interrupter); | |
1265 | BT_LIB_LOGI("Interrupted graph: %!+g", graph); | |
1266 | } | |
1267 | ||
c5b9b441 PP |
1268 | void bt_graph_get_ref(const struct bt_graph *graph) |
1269 | { | |
1270 | bt_object_get_ref(graph); | |
1271 | } | |
1272 | ||
1273 | void bt_graph_put_ref(const struct bt_graph *graph) | |
1274 | { | |
1275 | bt_object_put_ref(graph); | |
1276 | } |