diff mbox

[-,seq,1/1] ALSA: seq: Continue broadcasting events to ports if one of them fails

Message ID 1401738082-28488-1-git-send-email-agoode@google.com (mailing list archive)
State New, archived
Headers show

Commit Message

Adam Goode June 2, 2014, 7:41 p.m. UTC
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: Adam Goode <agoode@google.com>

Comments

Takashi Iwai June 3, 2014, 5:42 a.m. UTC | #1
At Mon,  2 Jun 2014 15:41:22 -0400,
Adam Goode wrote:
> 
> 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: Adam Goode <agoode@google.com>

Thanks.  The changes look OK to me, though, I'd prefer a variable name
"result" instead of "sticky_err".  And you don't have to initialize
err any longer there.

Meanwhile, it's another question what to be returned from the
function.  Such broadcasting case, would it be better to return always
num_ev when it's positive, or always report any (non-serious) error?


Takashi

> 
> diff --git a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c
> index 9ca5e64..f9b5315 100644
> --- a/sound/core/seq/seq_clientmgr.c
> +++ b/sound/core/seq/seq_clientmgr.c
> @@ -660,7 +660,7 @@ static int deliver_to_subscribers(struct snd_seq_client *client,
>  				  int atomic, int hop)
>  {
>  	struct snd_seq_subscribers *subs;
> -	int err = 0, num_ev = 0;
> +	int err = 0, sticky_err = 0, num_ev = 0;
>  	struct snd_seq_event event_saved;
>  	struct snd_seq_client_port *src_port;
>  	struct snd_seq_port_subs_info *grp;
> @@ -685,8 +685,12 @@ static int deliver_to_subscribers(struct snd_seq_client *client,
>  						  subs->info.flags & SNDRV_SEQ_PORT_SUBS_TIME_REAL);
>  		err = snd_seq_deliver_single_event(client, event,
>  						   0, atomic, hop);
> -		if (err < 0)
> -			break;
> +		if (err < 0) {
> +			/* save first error that occurs and continue */
> +			if (!sticky_err)
> +				sticky_err = err;
> +			continue;
> +		}
>  		num_ev++;
>  		/* restore original event record */
>  		*event = event_saved;
> @@ -697,7 +701,7 @@ static int deliver_to_subscribers(struct snd_seq_client *client,
>  		up_read(&grp->list_mutex);
>  	*event = event_saved; /* restore */
>  	snd_seq_port_unlock(src_port);
> -	return (err < 0) ? err : num_ev;
> +	return (sticky_err < 0) ? sticky_err : num_ev;
>  }
>  
>  
> @@ -709,7 +713,7 @@ static int port_broadcast_event(struct snd_seq_client *client,
>  				struct snd_seq_event *event,
>  				int atomic, int hop)
>  {
> -	int num_ev = 0, err = 0;
> +	int num_ev = 0, err = 0, sticky_err = 0;
>  	struct snd_seq_client *dest_client;
>  	struct snd_seq_client_port *port;
>  
> @@ -724,14 +728,18 @@ static int port_broadcast_event(struct snd_seq_client *client,
>  		err = snd_seq_deliver_single_event(NULL, event,
>  						   SNDRV_SEQ_FILTER_BROADCAST,
>  						   atomic, hop);
> -		if (err < 0)
> -			break;
> +		if (err < 0) {
> +			/* save first error that occurs and continue */
> +			if (!sticky_err)
> +				sticky_err = err;
> +			continue;
> +		}
>  		num_ev++;
>  	}
>  	read_unlock(&dest_client->ports_lock);
>  	snd_seq_client_unlock(dest_client);
>  	event->dest.port = SNDRV_SEQ_ADDRESS_BROADCAST; /* restore */
> -	return (err < 0) ? err : num_ev;
> +	return (sticky_err < 0) ? sticky_err : num_ev;
>  }
>  
>  /*
> @@ -741,7 +749,7 @@ static int port_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)
>  {
> -	int err = 0, num_ev = 0;
> +	int err = 0, sticky_err = 0, num_ev = 0;
>  	int dest;
>  	struct snd_seq_addr addr;
>  
> @@ -760,12 +768,16 @@ static int broadcast_event(struct snd_seq_client *client,
>  			err = snd_seq_deliver_single_event(NULL, event,
>  							   SNDRV_SEQ_FILTER_BROADCAST,
>  							   atomic, hop);
> -		if (err < 0)
> -			break;
> +		if (err < 0) {
> +			/* save first error that occurs and continue */
> +			if (!sticky_err)
> +				sticky_err = err;
> +			continue;
> +		}
>  		num_ev += err;
>  	}
>  	event->dest = addr; /* restore */
> -	return (err < 0) ? err : num_ev;
> +	return (sticky_err < 0) ? sticky_err : num_ev;
>  }
>  
>  
> -- 
> 1.9.1.423.g4596e3a
>
Adam Goode June 3, 2014, 10:33 p.m. UTC | #2
On Tue, Jun 3, 2014 at 1:42 AM, Takashi Iwai <tiwai@suse.de> wrote:

> At Mon,  2 Jun 2014 15:41:22 -0400,
> Adam Goode wrote:
> >
> > 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: Adam Goode <agoode@google.com>
>
> Thanks.  The changes look OK to me, though, I'd prefer a variable name
> "result" instead of "sticky_err".  And you don't have to initialize
> err any longer there.
>
> Meanwhile, it's another question what to be returned from the
> function.  Such broadcasting case, would it be better to return always
> num_ev when it's positive, or always report any (non-serious) error?
>
>
Thanks, I will send another patch with "result" instead.

Right now, the function will always report an error if any error occurs. My
patch should not change this behavior. I believe my patch has the effect
equivalent to sorting the list such that the failed ports are all at the
end.

If you would like to change the behavior of the function, that is another
issue.


Thanks,

Adam
diff mbox

Patch

diff --git a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c
index 9ca5e64..f9b5315 100644
--- a/sound/core/seq/seq_clientmgr.c
+++ b/sound/core/seq/seq_clientmgr.c
@@ -660,7 +660,7 @@  static int deliver_to_subscribers(struct snd_seq_client *client,
 				  int atomic, int hop)
 {
 	struct snd_seq_subscribers *subs;
-	int err = 0, num_ev = 0;
+	int err = 0, sticky_err = 0, num_ev = 0;
 	struct snd_seq_event event_saved;
 	struct snd_seq_client_port *src_port;
 	struct snd_seq_port_subs_info *grp;
@@ -685,8 +685,12 @@  static int deliver_to_subscribers(struct snd_seq_client *client,
 						  subs->info.flags & SNDRV_SEQ_PORT_SUBS_TIME_REAL);
 		err = snd_seq_deliver_single_event(client, event,
 						   0, atomic, hop);
-		if (err < 0)
-			break;
+		if (err < 0) {
+			/* save first error that occurs and continue */
+			if (!sticky_err)
+				sticky_err = err;
+			continue;
+		}
 		num_ev++;
 		/* restore original event record */
 		*event = event_saved;
@@ -697,7 +701,7 @@  static int deliver_to_subscribers(struct snd_seq_client *client,
 		up_read(&grp->list_mutex);
 	*event = event_saved; /* restore */
 	snd_seq_port_unlock(src_port);
-	return (err < 0) ? err : num_ev;
+	return (sticky_err < 0) ? sticky_err : num_ev;
 }
 
 
@@ -709,7 +713,7 @@  static int port_broadcast_event(struct snd_seq_client *client,
 				struct snd_seq_event *event,
 				int atomic, int hop)
 {
-	int num_ev = 0, err = 0;
+	int num_ev = 0, err = 0, sticky_err = 0;
 	struct snd_seq_client *dest_client;
 	struct snd_seq_client_port *port;
 
@@ -724,14 +728,18 @@  static int port_broadcast_event(struct snd_seq_client *client,
 		err = snd_seq_deliver_single_event(NULL, event,
 						   SNDRV_SEQ_FILTER_BROADCAST,
 						   atomic, hop);
-		if (err < 0)
-			break;
+		if (err < 0) {
+			/* save first error that occurs and continue */
+			if (!sticky_err)
+				sticky_err = err;
+			continue;
+		}
 		num_ev++;
 	}
 	read_unlock(&dest_client->ports_lock);
 	snd_seq_client_unlock(dest_client);
 	event->dest.port = SNDRV_SEQ_ADDRESS_BROADCAST; /* restore */
-	return (err < 0) ? err : num_ev;
+	return (sticky_err < 0) ? sticky_err : num_ev;
 }
 
 /*
@@ -741,7 +749,7 @@  static int port_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)
 {
-	int err = 0, num_ev = 0;
+	int err = 0, sticky_err = 0, num_ev = 0;
 	int dest;
 	struct snd_seq_addr addr;
 
@@ -760,12 +768,16 @@  static int broadcast_event(struct snd_seq_client *client,
 			err = snd_seq_deliver_single_event(NULL, event,
 							   SNDRV_SEQ_FILTER_BROADCAST,
 							   atomic, hop);
-		if (err < 0)
-			break;
+		if (err < 0) {
+			/* save first error that occurs and continue */
+			if (!sticky_err)
+				sticky_err = err;
+			continue;
+		}
 		num_ev += err;
 	}
 	event->dest = addr; /* restore */
-	return (err < 0) ? err : num_ev;
+	return (sticky_err < 0) ? sticky_err : num_ev;
 }