[Spice-devel] Further thoughts on the write polling in the xf86_spice_xql driver

Alon Levy alevy at redhat.com
Sun Jun 3 07:53:32 PDT 2012


On Sat, Jun 02, 2012 at 01:24:29PM -0500, Jeremy White wrote:
> I spent a fair amount of energy trying to understand the issue with EAGAIN
> and the xspice server.  I've sent in one patch which I believe to be
> correct.
> 
> I had some further thoughts I wanted to share as well.
> 
> To reprise, the issue occurs when we send the large ping test packet from the server; if
> the network interface returns EAGAIN, we break out and return to the X server.  We
> add the socket to our list of 'write watches', and hope to retry the write again
> once we detect that the socket is ready for writing.
> 
> However, while the X server main loop has a facility that lets us add fds to it's
> select read mask, it has no such facility to let us add fds to it's *write* select mask.

Right. This is the problem. I think the right solution is to patch the X
server.

> 
> So, without patching X, we have no clean way to add our socket to the list to
> be checked as being writable.  Given that, we have to fall back on polling.  However,
> in our current state, we don't actually do the poll unless there is a readable
> socket.  The patch I've already sent just makes us actually
> perform the polling action.
> 
> I made an argument to myself that another potentially useful design change was to use
> the facility the X server does provide - namely the timeout in the pre block call - to prevent
> the X server from doing a long select on the read sockets.  Essentially, since
> the X server doesn't select on the write fds, we should have it spin loop, while
> we check the write fds.
> 
> A patch that implements that strategy is attached below.
> 
> However, as I played with this patch, I came to feel that, while logically correct,
> it had little practical value, and the resulting spin loop was actually an annoying
> side effect.  It turns out that the X server pretty much always has a timer of some
> sort going, so in my testing, it was never hanging on the select for more than 33 us.
> 
> Given that, I thought to omit this patch, and perhaps instead send a patch
> with a comment touching on this.
> 
> Have I missed anything?

I don't think so. I think fixing BlockHandler in X to include a
pWritemask is the right way (and/or WakeupHandler, not sure).

> 
> Cheers,
> 
> Jeremy
> 
> 
> ---
>  src/spiceqxl_main_loop.c |   21 ++++++++++++++++++---
>  1 file changed, 18 insertions(+), 3 deletions(-)
> 
> diff --git a/src/spiceqxl_main_loop.c b/src/spiceqxl_main_loop.c
> index b06d5eb..3154384 100644
> --- a/src/spiceqxl_main_loop.c
> +++ b/src/spiceqxl_main_loop.c
> @@ -241,12 +241,14 @@ static void channel_event(int event, SpiceChannelEventInfo *info)
>      NOT_IMPLEMENTED
>  }
>  
> -static int set_watch_fds(fd_set *rfds, fd_set *wfds)
> +static int set_watch_fds(fd_set *rfds, fd_set *wfds, int *wcount)
>  {
>      SpiceWatch *watch;
>      RingItem *link;
>      RingItem *next;
>      int max_fd = -1;
> +    if (wcount)
> +        *wcount = 0;
>  
>      RING_FOREACH_SAFE(link, next, &watches) {
>          watch = (SpiceWatch*)link;
> @@ -257,6 +259,8 @@ static int set_watch_fds(fd_set *rfds, fd_set *wfds)
>          if (watch->event_mask & SPICE_WATCH_EVENT_WRITE) {
>              FD_SET(watch->fd, wfds);
>              max_fd = watch->fd > max_fd ? watch->fd : max_fd;
> +            if (wcount)
> +                (*wcount)++;
>          }
>      }
>      return max_fd;
> @@ -267,10 +271,21 @@ static int set_watch_fds(fd_set *rfds, fd_set *wfds)
>   * readmask is just an fdset on linux, but something totally different on windows (etc).
>   * DIX has a comment about it using a special type to hide this (so we break that here)
>   */
> +static struct timeval write_timeout;
>  static void xspice_block_handler(pointer data, OSTimePtr timeout, pointer readmask)
>  {
> +    int wcount;
>      /* set all our fd's */
> -    set_watch_fds((fd_set*)readmask, (fd_set*)readmask);
> +    set_watch_fds((fd_set*)readmask, (fd_set*)readmask, &wcount);
> +
> +    /* If we have any write watches, we have to force the X server into a polling
> +       select loop */
> +    if (wcount > 0)
> +    {
> +        if (*timeout == NULL)
> +            *timeout = &write_timeout;
> +        (*timeout)->tv_sec = (*timeout)->tv_usec = 0;
> +    }
>  }
>  
>  /*
> @@ -292,7 +307,7 @@ static void select_and_check_watches(void)
>  
>      FD_ZERO(&rfds);
>      FD_ZERO(&wfds);
> -    max_fd = set_watch_fds(&rfds, &wfds);
> +    max_fd = set_watch_fds(&rfds, &wfds, NULL);
>      watch = (SpiceWatch*)watches.next;
>      timeout.tv_sec = timeout.tv_usec = 0;
>      retval = select(max_fd + 1, &rfds, &wfds, NULL, &timeout);
> -- 
> 1.7.9.5
> 

> ---
>  src/spiceqxl_main_loop.c |   21 ++++++++++++++++++---
>  1 file changed, 18 insertions(+), 3 deletions(-)
> 
> diff --git a/src/spiceqxl_main_loop.c b/src/spiceqxl_main_loop.c
> index b06d5eb..3154384 100644
> --- a/src/spiceqxl_main_loop.c
> +++ b/src/spiceqxl_main_loop.c
> @@ -241,12 +241,14 @@ static void channel_event(int event, SpiceChannelEventInfo *info)
>      NOT_IMPLEMENTED
>  }
>  
> -static int set_watch_fds(fd_set *rfds, fd_set *wfds)
> +static int set_watch_fds(fd_set *rfds, fd_set *wfds, int *wcount)
>  {
>      SpiceWatch *watch;
>      RingItem *link;
>      RingItem *next;
>      int max_fd = -1;
> +    if (wcount)
> +        *wcount = 0;
>  
>      RING_FOREACH_SAFE(link, next, &watches) {
>          watch = (SpiceWatch*)link;
> @@ -257,6 +259,8 @@ static int set_watch_fds(fd_set *rfds, fd_set *wfds)
>          if (watch->event_mask & SPICE_WATCH_EVENT_WRITE) {
>              FD_SET(watch->fd, wfds);
>              max_fd = watch->fd > max_fd ? watch->fd : max_fd;
> +            if (wcount)
> +                (*wcount)++;
>          }
>      }
>      return max_fd;
> @@ -267,10 +271,21 @@ static int set_watch_fds(fd_set *rfds, fd_set *wfds)
>   * readmask is just an fdset on linux, but something totally different on windows (etc).
>   * DIX has a comment about it using a special type to hide this (so we break that here)
>   */
> +static struct timeval write_timeout;
>  static void xspice_block_handler(pointer data, OSTimePtr timeout, pointer readmask)
>  {
> +    int wcount;
>      /* set all our fd's */
> -    set_watch_fds((fd_set*)readmask, (fd_set*)readmask);
> +    set_watch_fds((fd_set*)readmask, (fd_set*)readmask, &wcount);
> +
> +    /* If we have any write watches, we have to force the X server into a polling
> +       select loop */
> +    if (wcount > 0)
> +    {
> +        if (*timeout == NULL)
> +            *timeout = &write_timeout;
> +        (*timeout)->tv_sec = (*timeout)->tv_usec = 0;
> +    }
>  }
>  
>  /*
> @@ -292,7 +307,7 @@ static void select_and_check_watches(void)
>  
>      FD_ZERO(&rfds);
>      FD_ZERO(&wfds);
> -    max_fd = set_watch_fds(&rfds, &wfds);
> +    max_fd = set_watch_fds(&rfds, &wfds, NULL);
>      watch = (SpiceWatch*)watches.next;
>      timeout.tv_sec = timeout.tv_usec = 0;
>      retval = select(max_fd + 1, &rfds, &wfds, NULL, &timeout);
> -- 
> 1.7.9.5
> 

> _______________________________________________
> Spice-devel mailing list
> Spice-devel at lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/spice-devel



More information about the Spice-devel mailing list