Fix race where competing-link sweep destroys auto-links before .bound fires

ProcessSavedLinks checked auto_link_proxies by global ID (proxy->id),
but this is only set asynchronously in the .bound callback. On a second
CoreDone pass (e.g. when virtual Sink ports appear), the sweep could
classify a just-created rule link as competing and destroy it.

Track (output_port, input_port) on LinkProxy at creation time and match
by port pair instead of the racy global ID.
This commit is contained in:
Joey Yakimowich-Payne 2026-01-30 13:32:07 -07:00
commit ad45683f21

View file

@ -125,6 +125,8 @@ struct LinkProxy {
bool failed = false; bool failed = false;
std::string error; std::string error;
uint32_t id = SPA_ID_INVALID; uint32_t id = SPA_ID_INVALID;
uint32_t output_port = 0;
uint32_t input_port = 0;
}; };
void LinkProxyBound(void* data, uint32_t global_id) { void LinkProxyBound(void* data, uint32_t global_id) {
@ -1026,6 +1028,8 @@ void Client::Impl::CreateAutoLinkAsync(uint32_t output_port, uint32_t input_port
auto link_data = std::make_unique<LinkProxy>(); auto link_data = std::make_unique<LinkProxy>();
link_data->proxy = proxy; link_data->proxy = proxy;
link_data->loop = thread_loop; link_data->loop = thread_loop;
link_data->output_port = output_port;
link_data->input_port = input_port;
pw_proxy_add_listener(proxy, &link_data->listener, &kLinkProxyEvents, link_data.get()); pw_proxy_add_listener(proxy, &link_data->listener, &kLinkProxyEvents, link_data.get());
std::lock_guard<std::mutex> lock(cache_mutex); std::lock_guard<std::mutex> lock(cache_mutex);
@ -1122,13 +1126,17 @@ void Client::Impl::ProcessSavedLinks() {
} }
} }
if (!is_ours) { if (!is_ours) {
uint32_t out_port = link_entry.second.output_port.value;
for (const auto& proxy : auto_link_proxies) { for (const auto& proxy : auto_link_proxies) {
if (proxy && proxy->id == link_id) { is_ours = true; break; } if (proxy && proxy->output_port == out_port &&
proxy->input_port == in_port) { is_ours = true; break; }
} }
} }
if (!is_ours) { if (!is_ours) {
uint32_t out_port = link_entry.second.output_port.value;
for (const auto& proxy : saved_link_proxies) { for (const auto& proxy : saved_link_proxies) {
if (proxy && proxy->id == link_id) { is_ours = true; break; } if (proxy && proxy->output_port == out_port &&
proxy->input_port == in_port) { is_ours = true; break; }
} }
} }
if (!is_ours) { if (!is_ours) {
@ -1167,6 +1175,8 @@ void Client::Impl::CreateSavedLinkAsync(uint32_t output_port,
auto link_data = std::make_unique<LinkProxy>(); auto link_data = std::make_unique<LinkProxy>();
link_data->proxy = proxy; link_data->proxy = proxy;
link_data->loop = thread_loop; link_data->loop = thread_loop;
link_data->output_port = output_port;
link_data->input_port = input_port;
pw_proxy_add_listener(proxy, &link_data->listener, &kLinkProxyEvents, pw_proxy_add_listener(proxy, &link_data->listener, &kLinkProxyEvents,
link_data.get()); link_data.get());
@ -1874,6 +1884,8 @@ Result<Link> Client::CreateLink(PortId output, PortId input, const LinkOptions&
auto link_proxy = std::make_unique<LinkProxy>(); auto link_proxy = std::make_unique<LinkProxy>();
link_proxy->proxy = proxy; link_proxy->proxy = proxy;
link_proxy->loop = impl_->thread_loop; link_proxy->loop = impl_->thread_loop;
link_proxy->output_port = output.value;
link_proxy->input_port = input.value;
pw_proxy_add_listener(proxy, &link_proxy->listener, &kLinkProxyEvents, link_proxy.get()); pw_proxy_add_listener(proxy, &link_proxy->listener, &kLinkProxyEvents, link_proxy.get());
int wait_attempts = 0; int wait_attempts = 0;