Remove bt_graph_add_component_as_sibling()
[babeltrace.git] / lib / graph / graph.c
CommitLineData
c0418dd9 1/*
7d55361f 2 * graph.c
c0418dd9
JG
3 *
4 * Babeltrace Plugin Component Graph
5 *
f60c8b34 6 * Copyright 2017 Jérémie Galarneau <jeremie.galarneau@efficios.com>
c0418dd9
JG
7 *
8 * Author: Jérémie Galarneau <jeremie.galarneau@efficios.com>
9 *
10 * Permission is hereby granted, free of charge, to any person obtaining a copy
11 * of this software and associated documentation files (the "Software"), to deal
12 * in the Software without restriction, including without limitation the rights
13 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14 * copies of the Software, and to permit persons to whom the Software is
15 * furnished to do so, subject to the following conditions:
16 *
17 * The above copyright notice and this permission notice shall be included in
18 * all copies or substantial portions of the Software.
19 *
20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26 * SOFTWARE.
27 */
28
b2e0c907
PP
29#include <babeltrace/graph/component-internal.h>
30#include <babeltrace/graph/graph-internal.h>
31#include <babeltrace/graph/connection-internal.h>
32#include <babeltrace/graph/component-sink-internal.h>
33#include <babeltrace/graph/component-source.h>
34#include <babeltrace/graph/component-filter.h>
35#include <babeltrace/graph/port.h>
3d9990ac 36#include <babeltrace/compiler-internal.h>
c55a9f58 37#include <babeltrace/types.h>
f60c8b34 38#include <unistd.h>
1bf957a0
PP
39#include <glib.h>
40
41struct bt_graph_listener {
42 void *func;
43 void *data;
44};
c0418dd9 45
f60c8b34
JG
46static
47void bt_graph_destroy(struct bt_object *obj)
c0418dd9 48{
f60c8b34
JG
49 struct bt_graph *graph = container_of(obj,
50 struct bt_graph, base);
c0418dd9 51
bd14d768
PP
52 /*
53 * The graph's reference count is 0 if we're here. Increment
54 * it to avoid a double-destroy (possibly infinitely recursive)
55 * in this situation:
56 *
57 * 1. We put and destroy a connection.
58 * 2. This connection's destructor finalizes its active
59 * notification iterators.
60 * 3. A notification iterator's finalization function gets a
61 * new reference on its component (reference count goes from
62 * 0 to 1).
63 * 4. Since this component's reference count goes to 1, it takes
64 * a reference on its parent (this graph). This graph's
65 * reference count goes from 0 to 1.
66 * 5. The notification iterator's finalization function puts its
67 * component reference (reference count goes from 1 to 0).
68 * 6. Since this component's reference count goes from 1 to 0,
69 * it puts its parent (this graph). This graph's reference
70 * count goes from 1 to 0.
71 * 7. Since this graph's reference count goes from 1 to 0,
72 * its destructor is called (this function).
73 *
74 * With the incrementation below, the graph's reference count at
75 * step 4 goes from 1 to 2, and from 2 to 1 at step 6. This
76 * ensures that this function is not called two times.
77 */
78 obj->ref_count.count++;
79
c0418dd9
JG
80 if (graph->connections) {
81 g_ptr_array_free(graph->connections, TRUE);
82 }
bd14d768
PP
83 if (graph->components) {
84 g_ptr_array_free(graph->components, TRUE);
85 }
f60c8b34
JG
86 if (graph->sinks_to_consume) {
87 g_queue_free(graph->sinks_to_consume);
c0418dd9 88 }
1bf957a0
PP
89
90 if (graph->listeners.port_added) {
91 g_array_free(graph->listeners.port_added, TRUE);
92 }
93
94 if (graph->listeners.port_removed) {
95 g_array_free(graph->listeners.port_removed, TRUE);
96 }
97
f345f8bb
PP
98 if (graph->listeners.ports_connected) {
99 g_array_free(graph->listeners.ports_connected, TRUE);
1bf957a0
PP
100 }
101
f345f8bb
PP
102 if (graph->listeners.ports_disconnected) {
103 g_array_free(graph->listeners.ports_disconnected, TRUE);
1bf957a0
PP
104 }
105
c0418dd9
JG
106 g_free(graph);
107}
108
1bf957a0
PP
109static
110int init_listeners_array(GArray **listeners)
111{
112 int ret = 0;
113
114 assert(listeners);
115 *listeners = g_array_new(FALSE, TRUE, sizeof(struct bt_graph_listener));
116 if (!*listeners) {
117 ret = -1;
118 goto end;
119 }
120
121end:
122 return ret;
123}
124
f60c8b34 125struct bt_graph *bt_graph_create(void)
c0418dd9 126{
f60c8b34 127 struct bt_graph *graph;
1bf957a0 128 int ret;
c0418dd9 129
f60c8b34 130 graph = g_new0(struct bt_graph, 1);
c0418dd9
JG
131 if (!graph) {
132 goto end;
133 }
134
f60c8b34 135 bt_object_init(graph, bt_graph_destroy);
c0418dd9 136
f60c8b34 137 graph->connections = g_ptr_array_new_with_free_func(bt_object_release);
c0418dd9
JG
138 if (!graph->connections) {
139 goto error;
140 }
f60c8b34
JG
141 graph->components = g_ptr_array_new_with_free_func(bt_object_release);
142 if (!graph->components) {
143 goto error;
144 }
145 graph->sinks_to_consume = g_queue_new();
146 if (!graph->sinks_to_consume) {
c0418dd9
JG
147 goto error;
148 }
1bf957a0
PP
149
150 ret = init_listeners_array(&graph->listeners.port_added);
151 if (ret) {
152 goto error;
153 }
154
155 ret = init_listeners_array(&graph->listeners.port_removed);
156 if (ret) {
157 goto error;
158 }
159
f345f8bb 160 ret = init_listeners_array(&graph->listeners.ports_connected);
1bf957a0
PP
161 if (ret) {
162 goto error;
163 }
164
f345f8bb 165 ret = init_listeners_array(&graph->listeners.ports_disconnected);
1bf957a0
PP
166 if (ret) {
167 goto error;
168 }
169
c0418dd9
JG
170end:
171 return graph;
172error:
173 BT_PUT(graph);
174 goto end;
175}
176
77a54f99 177struct bt_connection *bt_graph_connect_ports(struct bt_graph *graph,
f60c8b34
JG
178 struct bt_port *upstream_port,
179 struct bt_port *downstream_port)
180{
181 struct bt_connection *connection = NULL;
182 struct bt_graph *upstream_graph = NULL;
183 struct bt_graph *downstream_graph = NULL;
184 struct bt_component *upstream_component = NULL;
185 struct bt_component *downstream_component = NULL;
186 enum bt_component_status component_status;
c55a9f58
PP
187 bt_bool upstream_was_already_in_graph;
188 bt_bool downstream_was_already_in_graph;
f60c8b34
JG
189
190 if (!graph || !upstream_port || !downstream_port) {
191 goto end;
192 }
193
202a3a13
PP
194 if (graph->canceled) {
195 goto end;
196 }
197
72b913fb 198 /* Ensure appropriate types for upstream and downstream ports. */
f60c8b34
JG
199 if (bt_port_get_type(upstream_port) != BT_PORT_TYPE_OUTPUT) {
200 goto end;
201 }
202 if (bt_port_get_type(downstream_port) != BT_PORT_TYPE_INPUT) {
203 goto end;
204 }
205
72b913fb 206 /* Ensure that both ports are currently unconnected. */
0d8b4d8e 207 if (bt_port_is_connected(upstream_port)) {
72b913fb
PP
208 fprintf(stderr, "Upstream port is already connected\n");
209 goto end;
210 }
211
0d8b4d8e 212 if (bt_port_is_connected(downstream_port)) {
72b913fb
PP
213 fprintf(stderr, "Downstream port is already connected\n");
214 goto end;
215 }
216
217 /*
218 * Ensure that both ports are still attached to their creating
219 * component.
220 */
f60c8b34 221 upstream_component = bt_port_get_component(upstream_port);
72b913fb
PP
222 if (!upstream_component) {
223 fprintf(stderr, "Upstream port does not belong to a component\n");
224 goto end;
225 }
226
227 downstream_component = bt_port_get_component(downstream_port);
228 if (!downstream_component) {
229 fprintf(stderr, "Downstream port does not belong to a component\n");
230 goto end;
231 }
232
233 /* Ensure the components are not already part of another graph. */
f60c8b34
JG
234 upstream_graph = bt_component_get_graph(upstream_component);
235 if (upstream_graph && (graph != upstream_graph)) {
236 fprintf(stderr, "Upstream component is already part of another graph\n");
237 goto error;
238 }
ffeb0eed 239 upstream_was_already_in_graph = (graph == upstream_graph);
f60c8b34
JG
240 downstream_graph = bt_component_get_graph(downstream_component);
241 if (downstream_graph && (graph != downstream_graph)) {
242 fprintf(stderr, "Downstream component is already part of another graph\n");
243 goto error;
244 }
ffeb0eed 245 downstream_was_already_in_graph = (graph == downstream_graph);
f60c8b34 246
0d8b4d8e
PP
247 /*
248 * At this point the ports are not connected yet. Both
249 * components need to accept an eventual connection to their
250 * port by the other port before we continue.
251 */
252 component_status = bt_component_accept_port_connection(
253 upstream_component, upstream_port, downstream_port);
254 if (component_status != BT_COMPONENT_STATUS_OK) {
255 goto error;
256 }
257 component_status = bt_component_accept_port_connection(
258 downstream_component, downstream_port, upstream_port);
259 if (component_status != BT_COMPONENT_STATUS_OK) {
260 goto error;
261 }
262
f60c8b34
JG
263 connection = bt_connection_create(graph, upstream_port,
264 downstream_port);
265 if (!connection) {
266 goto error;
267 }
268
269 /*
72b913fb
PP
270 * Ownership of upstream_component/downstream_component and of
271 * the connection object is transferred to the graph.
f60c8b34
JG
272 */
273 g_ptr_array_add(graph->connections, connection);
ffeb0eed
JG
274
275 if (!upstream_was_already_in_graph) {
276 g_ptr_array_add(graph->components, upstream_component);
277 bt_component_set_graph(upstream_component, graph);
278 }
279 if (!downstream_was_already_in_graph) {
280 g_ptr_array_add(graph->components, downstream_component);
281 bt_component_set_graph(downstream_component, graph);
282 if (bt_component_get_class_type(downstream_component) ==
283 BT_COMPONENT_CLASS_TYPE_SINK) {
284 g_queue_push_tail(graph->sinks_to_consume,
285 downstream_component);
286 }
f60c8b34
JG
287 }
288
289 /*
0d8b4d8e
PP
290 * The graph is now the parent of these components which
291 * garantees their existence for the duration of the graph's
292 * lifetime.
f60c8b34 293 */
f60c8b34
JG
294
295 /*
0d8b4d8e 296 * Notify both components that their port is connected.
f60c8b34 297 */
0d8b4d8e
PP
298 bt_component_port_connected(upstream_component, upstream_port,
299 downstream_port);
300 bt_component_port_connected(downstream_component, downstream_port,
301 upstream_port);
1bf957a0
PP
302
303 /*
0d8b4d8e 304 * Notify the graph's creator that both ports are connected.
1bf957a0 305 */
f345f8bb 306 bt_graph_notify_ports_connected(graph, upstream_port, downstream_port);
1bf957a0 307
f60c8b34
JG
308end:
309 bt_put(upstream_graph);
310 bt_put(downstream_graph);
3eeacbb9
JG
311 bt_put(upstream_component);
312 bt_put(downstream_component);
f60c8b34 313 return connection;
3eeacbb9 314
3eeacbb9
JG
315error:
316 BT_PUT(upstream_component);
317 BT_PUT(downstream_component);
f60c8b34
JG
318 goto end;
319}
320
72b913fb 321enum bt_graph_status bt_graph_consume(struct bt_graph *graph)
c0418dd9 322{
f60c8b34 323 struct bt_component *sink;
72b913fb
PP
324 enum bt_graph_status status = BT_GRAPH_STATUS_OK;
325 enum bt_component_status comp_status;
f60c8b34
JG
326 GList *current_node;
327
328 if (!graph) {
72b913fb 329 status = BT_GRAPH_STATUS_INVALID;
f60c8b34
JG
330 goto end;
331 }
332
202a3a13
PP
333 if (graph->canceled) {
334 status = BT_GRAPH_STATUS_CANCELED;
335 goto end;
336 }
337
f60c8b34 338 if (g_queue_is_empty(graph->sinks_to_consume)) {
72b913fb 339 status = BT_GRAPH_STATUS_END;
f60c8b34
JG
340 goto end;
341 }
342
343 current_node = g_queue_pop_head_link(graph->sinks_to_consume);
344 sink = current_node->data;
72b913fb
PP
345 comp_status = bt_component_sink_consume(sink);
346 switch (comp_status) {
347 case BT_COMPONENT_STATUS_OK:
348 break;
349 case BT_COMPONENT_STATUS_END:
350 status = BT_GRAPH_STATUS_END;
351 break;
352 case BT_COMPONENT_STATUS_AGAIN:
353 status = BT_GRAPH_STATUS_AGAIN;
354 break;
355 case BT_COMPONENT_STATUS_INVALID:
356 status = BT_GRAPH_STATUS_INVALID;
357 break;
358 default:
359 status = BT_GRAPH_STATUS_ERROR;
360 break;
361 }
362
363 if (status != BT_GRAPH_STATUS_END) {
f60c8b34
JG
364 g_queue_push_tail_link(graph->sinks_to_consume, current_node);
365 goto end;
366 }
367
368 /* End reached, the node is not added back to the queue and free'd. */
369 g_queue_delete_link(graph->sinks_to_consume, current_node);
370
371 /* Don't forward an END status if there are sinks left to consume. */
372 if (!g_queue_is_empty(graph->sinks_to_consume)) {
373 status = BT_GRAPH_STATUS_OK;
374 goto end;
375 }
376end:
377 return status;
c0418dd9
JG
378}
379
72b913fb 380enum bt_graph_status bt_graph_run(struct bt_graph *graph)
f60c8b34 381{
72b913fb 382 enum bt_graph_status status = BT_GRAPH_STATUS_OK;
f60c8b34
JG
383
384 if (!graph) {
72b913fb 385 status = BT_GRAPH_STATUS_INVALID;
202a3a13 386 goto end;
f60c8b34
JG
387 }
388
389 do {
72b913fb
PP
390 status = bt_graph_consume(graph);
391 if (status == BT_GRAPH_STATUS_AGAIN) {
f60c8b34 392 /*
202a3a13
PP
393 * If AGAIN is received and there are multiple
394 * sinks, go ahead and consume from the next
395 * sink.
f60c8b34 396 *
202a3a13
PP
397 * However, in the case where a single sink is
398 * left, the caller can decide to busy-wait and
399 * call bt_graph_run() continuously until the
400 * source is ready or it can decide to sleep for
401 * an arbitrary amount of time.
f60c8b34
JG
402 */
403 if (graph->sinks_to_consume->length > 1) {
72b913fb 404 status = BT_GRAPH_STATUS_OK;
f60c8b34
JG
405 }
406 }
72b913fb 407 } while (status == BT_GRAPH_STATUS_OK);
f60c8b34
JG
408
409 if (g_queue_is_empty(graph->sinks_to_consume)) {
72b913fb 410 status = BT_GRAPH_STATUS_END;
f60c8b34 411 }
202a3a13 412end:
72b913fb 413 return status;
f60c8b34 414}
1bf957a0
PP
415
416static
417void add_listener(GArray *listeners, void *func, void *data)
418{
419 struct bt_graph_listener listener = {
420 .func = func,
421 .data = data,
422 };
423
424 g_array_append_val(listeners, listener);
425}
426
427enum bt_graph_status bt_graph_add_port_added_listener(
428 struct bt_graph *graph,
429 bt_graph_port_added_listener listener, void *data)
430{
431 enum bt_graph_status status = BT_GRAPH_STATUS_OK;
432
433 if (!graph || !listener) {
434 status = BT_GRAPH_STATUS_INVALID;
435 goto end;
436 }
437
438 add_listener(graph->listeners.port_added, listener, data);
439
440end:
441 return status;
442}
443
444enum bt_graph_status bt_graph_add_port_removed_listener(
445 struct bt_graph *graph,
446 bt_graph_port_removed_listener listener, void *data)
447{
448 enum bt_graph_status status = BT_GRAPH_STATUS_OK;
449
450 if (!graph || !listener) {
451 status = BT_GRAPH_STATUS_INVALID;
452 goto end;
453 }
454
455 add_listener(graph->listeners.port_removed, listener, data);
456
457end:
458 return status;
459}
460
f345f8bb 461enum bt_graph_status bt_graph_add_ports_connected_listener(
1bf957a0 462 struct bt_graph *graph,
f345f8bb 463 bt_graph_ports_connected_listener listener, void *data)
1bf957a0
PP
464{
465 enum bt_graph_status status = BT_GRAPH_STATUS_OK;
466
467 if (!graph || !listener) {
468 status = BT_GRAPH_STATUS_INVALID;
469 goto end;
470 }
471
f345f8bb 472 add_listener(graph->listeners.ports_connected, listener, data);
1bf957a0
PP
473
474end:
475 return status;
476}
477
f345f8bb 478enum bt_graph_status bt_graph_add_ports_disconnected_listener(
1bf957a0 479 struct bt_graph *graph,
f345f8bb 480 bt_graph_ports_disconnected_listener listener, void *data)
1bf957a0
PP
481{
482 enum bt_graph_status status = BT_GRAPH_STATUS_OK;
483
484 if (!graph || !listener) {
485 status = BT_GRAPH_STATUS_INVALID;
486 goto end;
487 }
488
f345f8bb 489 add_listener(graph->listeners.ports_disconnected, listener, data);
1bf957a0
PP
490
491end:
492 return status;
493}
494
495BT_HIDDEN
496void bt_graph_notify_port_added(struct bt_graph *graph, struct bt_port *port)
497{
498 size_t i;
499
500 for (i = 0; i < graph->listeners.port_added->len; i++) {
501 struct bt_graph_listener listener =
502 g_array_index(graph->listeners.port_added,
503 struct bt_graph_listener, i);
504 bt_graph_port_added_listener func = listener.func;
505
506 assert(func);
507 func(port, listener.data);
508 }
509}
510
511BT_HIDDEN
512void bt_graph_notify_port_removed(struct bt_graph *graph,
513 struct bt_component *comp, struct bt_port *port)
514{
515 size_t i;
516
517 for (i = 0; i < graph->listeners.port_removed->len; i++) {
518 struct bt_graph_listener listener =
519 g_array_index(graph->listeners.port_removed,
520 struct bt_graph_listener, i);
521 bt_graph_port_removed_listener func = listener.func;
522
523 assert(func);
524 func(comp, port, listener.data);
525 }
526}
527
528BT_HIDDEN
f345f8bb
PP
529void bt_graph_notify_ports_connected(struct bt_graph *graph,
530 struct bt_port *upstream_port, struct bt_port *downstream_port)
1bf957a0
PP
531{
532 size_t i;
533
f345f8bb 534 for (i = 0; i < graph->listeners.ports_connected->len; i++) {
1bf957a0 535 struct bt_graph_listener listener =
f345f8bb 536 g_array_index(graph->listeners.ports_connected,
1bf957a0 537 struct bt_graph_listener, i);
f345f8bb 538 bt_graph_ports_connected_listener func = listener.func;
1bf957a0
PP
539
540 assert(func);
f345f8bb 541 func(upstream_port, downstream_port, listener.data);
1bf957a0
PP
542 }
543}
544
545BT_HIDDEN
f345f8bb
PP
546void bt_graph_notify_ports_disconnected(struct bt_graph *graph,
547 struct bt_component *upstream_comp,
548 struct bt_component *downstream_comp,
549 struct bt_port *upstream_port, struct bt_port *downstream_port)
1bf957a0
PP
550{
551 size_t i;
552
f345f8bb 553 for (i = 0; i < graph->listeners.ports_disconnected->len; i++) {
1bf957a0 554 struct bt_graph_listener listener =
f345f8bb 555 g_array_index(graph->listeners.ports_disconnected,
1bf957a0 556 struct bt_graph_listener, i);
f345f8bb 557 bt_graph_ports_disconnected_listener func = listener.func;
1bf957a0
PP
558
559 assert(func);
f345f8bb
PP
560 func(upstream_comp, downstream_comp, upstream_port,
561 downstream_port, listener.data);
1bf957a0
PP
562 }
563}
202a3a13
PP
564
565extern enum bt_graph_status bt_graph_cancel(struct bt_graph *graph)
566{
567 enum bt_graph_status ret = BT_GRAPH_STATUS_OK;
568
569 if (!graph) {
570 ret = BT_GRAPH_STATUS_INVALID;
571 goto end;
572 }
573
574 graph->canceled = BT_TRUE;
575
576end:
577 return ret;
578}
579
580extern bt_bool bt_graph_is_canceled(struct bt_graph *graph)
581{
582 return graph ? graph->canceled : BT_FALSE;
583}
f167d3c0
PP
584
585BT_HIDDEN
586void bt_graph_remove_connection(struct bt_graph *graph,
587 struct bt_connection *connection)
588{
589 assert(graph);
590 assert(connection);
591 g_ptr_array_remove(graph->connections, connection);
592}
This page took 0.055922 seconds and 4 git commands to generate.