Commit 27423257 authored by Adam Goode's avatar Adam Goode Committed by Takashi Iwai

ALSA: seq: Continue broadcasting events to ports if one of them fails

Sometimes PORT_EXIT messages are lost when a process is exiting.
This happens if you subscribe to the announce port with client A,
then subscribe to the announce port with client B, then kill client A.
Client B will not see the PORT_EXIT message because client A's port is
closing and is earlier in the announce port subscription list. The
for each loop will try to send the announcement to client A and fail,
then will stop trying to broadcast to other ports. Killing B works fine
since the announcement will already have gone to A. The CLIENT_EXIT
message does not get lost.

How to reproduce problem:

*** termA
$ aseqdump -p 0:1
  0:1   Port subscribed            0:1 -> 128:0

*** termB
$ aseqdump -p 0:1

*** termA
  0:1   Client start               client 129
  0:1   Port start                 129:0
  0:1   Port subscribed            0:1 -> 129:0

*** termB
  0:1   Port subscribed            0:1 -> 129:0

*** termA
^C

*** termB
  0:1   Client exit                client 128
   <--- expected Port exit as well (before client exit)
Signed-off-by: default avatarAdam Goode <agoode@google.com>
Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent 1c9b8f51
...@@ -660,7 +660,7 @@ static int deliver_to_subscribers(struct snd_seq_client *client, ...@@ -660,7 +660,7 @@ static int deliver_to_subscribers(struct snd_seq_client *client,
int atomic, int hop) int atomic, int hop)
{ {
struct snd_seq_subscribers *subs; struct snd_seq_subscribers *subs;
int err = 0, num_ev = 0; int err, result = 0, num_ev = 0;
struct snd_seq_event event_saved; struct snd_seq_event event_saved;
struct snd_seq_client_port *src_port; struct snd_seq_client_port *src_port;
struct snd_seq_port_subs_info *grp; struct snd_seq_port_subs_info *grp;
...@@ -685,8 +685,12 @@ static int deliver_to_subscribers(struct snd_seq_client *client, ...@@ -685,8 +685,12 @@ static int deliver_to_subscribers(struct snd_seq_client *client,
subs->info.flags & SNDRV_SEQ_PORT_SUBS_TIME_REAL); subs->info.flags & SNDRV_SEQ_PORT_SUBS_TIME_REAL);
err = snd_seq_deliver_single_event(client, event, err = snd_seq_deliver_single_event(client, event,
0, atomic, hop); 0, atomic, hop);
if (err < 0) if (err < 0) {
break; /* save first error that occurs and continue */
if (!result)
result = err;
continue;
}
num_ev++; num_ev++;
/* restore original event record */ /* restore original event record */
*event = event_saved; *event = event_saved;
...@@ -697,7 +701,7 @@ static int deliver_to_subscribers(struct snd_seq_client *client, ...@@ -697,7 +701,7 @@ static int deliver_to_subscribers(struct snd_seq_client *client,
up_read(&grp->list_mutex); up_read(&grp->list_mutex);
*event = event_saved; /* restore */ *event = event_saved; /* restore */
snd_seq_port_unlock(src_port); snd_seq_port_unlock(src_port);
return (err < 0) ? err : num_ev; return (result < 0) ? result : num_ev;
} }
...@@ -709,7 +713,7 @@ static int port_broadcast_event(struct snd_seq_client *client, ...@@ -709,7 +713,7 @@ static int port_broadcast_event(struct snd_seq_client *client,
struct snd_seq_event *event, struct snd_seq_event *event,
int atomic, int hop) int atomic, int hop)
{ {
int num_ev = 0, err = 0; int num_ev = 0, err, result = 0;
struct snd_seq_client *dest_client; struct snd_seq_client *dest_client;
struct snd_seq_client_port *port; struct snd_seq_client_port *port;
...@@ -724,14 +728,18 @@ static int port_broadcast_event(struct snd_seq_client *client, ...@@ -724,14 +728,18 @@ static int port_broadcast_event(struct snd_seq_client *client,
err = snd_seq_deliver_single_event(NULL, event, err = snd_seq_deliver_single_event(NULL, event,
SNDRV_SEQ_FILTER_BROADCAST, SNDRV_SEQ_FILTER_BROADCAST,
atomic, hop); atomic, hop);
if (err < 0) if (err < 0) {
break; /* save first error that occurs and continue */
if (!result)
result = err;
continue;
}
num_ev++; num_ev++;
} }
read_unlock(&dest_client->ports_lock); read_unlock(&dest_client->ports_lock);
snd_seq_client_unlock(dest_client); snd_seq_client_unlock(dest_client);
event->dest.port = SNDRV_SEQ_ADDRESS_BROADCAST; /* restore */ event->dest.port = SNDRV_SEQ_ADDRESS_BROADCAST; /* restore */
return (err < 0) ? err : num_ev; return (result < 0) ? result : num_ev;
} }
/* /*
...@@ -741,7 +749,7 @@ static int port_broadcast_event(struct snd_seq_client *client, ...@@ -741,7 +749,7 @@ static int port_broadcast_event(struct snd_seq_client *client,
static int broadcast_event(struct snd_seq_client *client, static int broadcast_event(struct snd_seq_client *client,
struct snd_seq_event *event, int atomic, int hop) struct snd_seq_event *event, int atomic, int hop)
{ {
int err = 0, num_ev = 0; int err, result = 0, num_ev = 0;
int dest; int dest;
struct snd_seq_addr addr; struct snd_seq_addr addr;
...@@ -760,12 +768,16 @@ static int broadcast_event(struct snd_seq_client *client, ...@@ -760,12 +768,16 @@ static int broadcast_event(struct snd_seq_client *client,
err = snd_seq_deliver_single_event(NULL, event, err = snd_seq_deliver_single_event(NULL, event,
SNDRV_SEQ_FILTER_BROADCAST, SNDRV_SEQ_FILTER_BROADCAST,
atomic, hop); atomic, hop);
if (err < 0) if (err < 0) {
break; /* save first error that occurs and continue */
if (!result)
result = err;
continue;
}
num_ev += err; num_ev += err;
} }
event->dest = addr; /* restore */ event->dest = addr; /* restore */
return (err < 0) ? err : num_ev; return (result < 0) ? result : num_ev;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment