mirror of
https://gitlab.freedesktop.org/pipewire/wireplumber.git
synced 2026-01-06 11:30:13 +01:00
monitors/alsa: decouple name deduplication from node objects
Node name deduplication relying on presence of node objects creates race conditions, as the name cannot be marked unused if the node object was not created or was destroyed. Use separate (device_id, node_id) -> name table to track name ownership separately from the existence of node objects. Also clear up the reserved names when device is destroyed, by the monitor or device reservation. In these cases "object-removed" for nodes is not called, so this fixes names leaking when e.g. pw-reserve is used.
This commit is contained in:
parent
65989e7e38
commit
ada687a4cc
1 changed files with 36 additions and 20 deletions
|
|
@ -17,6 +17,10 @@ config.rules = Conf.get_section_as_json ("monitor.alsa.rules", Json.Array {})
|
|||
device_names_table = nil
|
||||
node_names_table = nil
|
||||
|
||||
-- SPA ids to node names: name = id_name_table[device_id][node_id]
|
||||
id_name_table = nil
|
||||
|
||||
|
||||
function nonempty(str)
|
||||
return str ~= "" and str or nil
|
||||
end
|
||||
|
|
@ -30,6 +34,7 @@ end
|
|||
|
||||
function createNode(parent, id, obj_type, factory, properties)
|
||||
local dev_props = parent.properties
|
||||
local parent_id = tonumber(dev_props["spa.object.id"])
|
||||
|
||||
-- set the device id and spa factory name; REQUIRED, do not change
|
||||
properties["device.id"] = parent["bound-id"]
|
||||
|
|
@ -103,7 +108,6 @@ function createNode(parent, id, obj_type, factory, properties)
|
|||
-- deduplicate nodes with the same name
|
||||
for counter = 2, 99, 1 do
|
||||
if node_names_table[properties["node.name"]] ~= true then
|
||||
node_names_table[properties["node.name"]] = true
|
||||
break
|
||||
end
|
||||
properties["node.name"] = name .. "." .. counter
|
||||
|
|
@ -161,43 +165,41 @@ function createNode(parent, id, obj_type, factory, properties)
|
|||
|
||||
if cutils.parseBool (properties ["node.disabled"]) then
|
||||
log:notice ("ALSA node " .. properties["node.name"] .. " disabled")
|
||||
node_names_table [properties ["node.name"]] = nil
|
||||
return
|
||||
end
|
||||
|
||||
node_names_table[properties["node.name"]] = true
|
||||
id_name_table[parent_id][id] = properties["node.name"]
|
||||
|
||||
-- create the node
|
||||
local node = Node("adapter", properties)
|
||||
node:activate(Feature.Proxy.BOUND, function (_, err)
|
||||
if err then
|
||||
log:warning ("Failed to create " .. properties ["node.name"]
|
||||
.. ": " .. tostring(err))
|
||||
|
||||
-- if it fails, object-removed gets called with missing
|
||||
-- properties, so clean up already here
|
||||
node_names_table [properties ["node.name"]] = nil
|
||||
end
|
||||
end)
|
||||
parent:store_managed_object(id, node)
|
||||
end
|
||||
|
||||
function removeNode(parent, id)
|
||||
local parent_id = tonumber(parent.properties["spa.object.id"])
|
||||
local node_name = id_name_table[parent_id][id]
|
||||
|
||||
if node_name ~= nil then
|
||||
log:info ("Removing node " .. node_name)
|
||||
node_names_table[node_name] = nil
|
||||
id_name_table[parent_id][id] = nil
|
||||
end
|
||||
end
|
||||
|
||||
function createDevice(parent, id, factory, properties)
|
||||
id_name_table[id] = {}
|
||||
properties["spa.object.id"] = id
|
||||
local device = SpaDevice(factory, properties)
|
||||
if device then
|
||||
device:connect("create-object", createNode)
|
||||
device:connect("object-removed", function (parent, id)
|
||||
local node = parent:get_managed_object(id)
|
||||
if not node then
|
||||
return
|
||||
end
|
||||
|
||||
local node_name = node.properties["node.name"]
|
||||
if node_name ~= nil then
|
||||
log:info ("Removing node " .. node_name)
|
||||
node_names_table[node_name] = nil
|
||||
else
|
||||
log:info ("Removing node with missing node.name")
|
||||
end
|
||||
end)
|
||||
device:connect("object-removed", removeNode)
|
||||
device:activate(Feature.SpaDevice.ENABLED | Feature.Proxy.BOUND)
|
||||
parent:store_managed_object(id, device)
|
||||
else
|
||||
|
|
@ -205,6 +207,16 @@ function createDevice(parent, id, factory, properties)
|
|||
end
|
||||
end
|
||||
|
||||
function removeDevice(parent, id)
|
||||
if id_name_table[id] ~= nil then
|
||||
for _, node_name in pairs(id_name_table[id]) do
|
||||
log:info ("Release " .. node_name)
|
||||
node_names_table[node_name] = nil
|
||||
end
|
||||
id_name_table[id] = nil
|
||||
end
|
||||
end
|
||||
|
||||
function prepareDevice(parent, id, obj_type, factory, properties)
|
||||
-- ensure the device has an appropriate name
|
||||
local name = "alsa_card." ..
|
||||
|
|
@ -317,6 +329,7 @@ function prepareDevice(parent, id, obj_type, factory, properties)
|
|||
|
||||
elseif state == "busy" then
|
||||
-- destroy the device
|
||||
removeDevice(parent, id)
|
||||
parent:store_managed_object(id, nil)
|
||||
end
|
||||
end)
|
||||
|
|
@ -347,6 +360,8 @@ function createMonitor ()
|
|||
|
||||
-- handle object-removed to destroy device reservations and recycle device name
|
||||
m:connect("object-removed", function (parent, id)
|
||||
removeDevice(parent, id)
|
||||
|
||||
local device = parent:get_managed_object(id)
|
||||
if not device then
|
||||
return
|
||||
|
|
@ -367,6 +382,7 @@ function createMonitor ()
|
|||
-- reset the name tables to make sure names are recycled
|
||||
device_names_table = {}
|
||||
node_names_table = {}
|
||||
id_name_table = {}
|
||||
|
||||
-- activate monitor
|
||||
log:info("Activating ALSA monitor")
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue