Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Why does mainloop quit, when core_event::done is fired? #10

Open
mincequi opened this issue Nov 4, 2024 · 4 comments
Open

Why does mainloop quit, when core_event::done is fired? #10

mincequi opened this issue Nov 4, 2024 · 4 comments
Assignees
Labels
enhancement New feature or request

Comments

@mincequi
Copy link

mincequi commented Nov 4, 2024

Hi there and thanks for this nice pipewire wrapper.

I have a question:
consider this code snippet:

int main() {
    auto main_loop = pw::main_loop::create();
    auto context   = pw::context::create(main_loop);
    auto core      = pw::core::create(context);
    auto reg       = pw::registry::create(core);
    auto listener  = reg->listen();

    listener.on<pipewire::registry_event::global>([&](const pw::global& global) {
        if (global.type == pw::node::type) {
            auto node = *reg->bind<pw::node>(global.id).get();
            info("node: {}", node.id());
        }
    });

    main_loop->run();
    return 0;
}

This will run and quit as soon as all nodes are printed. Why is this happening? Actually, it should continue and print all nodes that are subsequently added.

Thanks in advance!

@Curve
Copy link
Owner

Curve commented Nov 4, 2024

Hi, thanks for the kind words!

The default overload for bind issues a core update, which in turn calls quit. You can prevent this by passing update_strategey::none as the second Parameter to bind.

I have to admit I'm not sure what my reasoning behind this design decision was, I'll quickly glance over it again and maybe change the default behavior.

@Curve
Copy link
Owner

Curve commented Nov 4, 2024

Alright, caught up to speed again - The update call is necessary, so that you can access the bound object from within the registry callback - the main-loop has to progress¹ to actually finish binding. In case you use the non updating bind call, pipewire will only finish binding the object at a later time, thus you'd have to save the future returned by bind and check if it's ready at a later time.

This is why it currently quits the main-loop after processing all nodes within the callback - the goal was to make it as convenient to use as possible, which includes not having to wait explicitly wait for the bound object to be available.

¹ This is as far as I know only possible with a call to pw_main_loop_run and then calling pw_main_loop_quit after run finished, sadly, there does not seem to be a non blocking run alternative.

I could look into providing an approach which stays more faithful to the default pipewire behavior, where a callback would get fired once the object was bound successfully² - in case that's something you'd want?

² This would probably require me to write some custom classes that re-implement part of <future>, which probably wouldn't hurt too much, as this would eliminate some workarounds that are currently required to make things go smoothly. Furthermore, it would also allow for some small optimizations :)

@mincequi
Copy link
Author

mincequi commented Nov 5, 2024

Ok, i guess i understand your ideas. However, i believe that the implemented behavior is a little inconsistent:
the mainloop will run forever if i listen to global events (like ports, nodes, links), but it stops as soon as i received all properties from a bound object (metadata). My assumption is, that it keeps running, until i quit it explicitly.
But this rises the question what to do if i want to integrate the pipewire mainloop into another mainloop implementation...?

To come back to your suggestion:
yes, i believe the callback based would fit better here (and also to my implementation, where i wrap all callback into reactive streams): https://github.com/mincequi/KlangDienst

Thanks already!

@Curve
Copy link
Owner

Curve commented Nov 5, 2024

Ok, i guess i understand your ideas. However, i believe that the implemented behavior is a little inconsistent: the mainloop will run forever if i listen to global events (like ports, nodes, links), but it stops as soon as i received all properties from a bound object (metadata).

Yes - This is what happens when you use the default bind overload. As mentioned previously, you can either pass update_strategy::none to avoid the sync, but with the draw-back that you'll have to make sure the object is available yourself when you try to access it later - or, as internally bind only uses the already existing listener interface, you can also fallback to that for the time being :)

My assumption is, that it keeps running, until i quit it explicitly. But this rises the question what to do if i want to integrate the pipewire mainloop into another mainloop implementation...?

I'm not sure how the current behavior impacts this, in the examples a simple while loop is used to run the main-loop as long as required, you could simply set the loop condition to false and then quit the main-loop explicitly. Integrating the pipewire mainloop into an arbitrary other main-loop is quite difficult, as mentioned previously, pipewire does not offer a non blocking run.

To come back to your suggestion: yes, i believe the callback based would fit better here

Agreed! I'll start work on this when I find the time to do so ^^

@Curve Curve self-assigned this Nov 5, 2024
@Curve Curve added the enhancement New feature or request label Nov 5, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants