<br><br><div class="gmail_quote"><div dir="ltr">On Wed, Oct 18, 2017, 9:17 AM Frediano Ziglio <<a href="mailto:fziglio@redhat.com">fziglio@redhat.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">><br>
> On Tue, Oct 17, 2017 at 10:09 AM, Frediano Ziglio <<a href="mailto:fziglio@redhat.com" target="_blank">fziglio@redhat.com</a>> wrote:<br>
> > From: Jakub Janků <<a href="mailto:janku.jakub.jj@gmail.com" target="_blank">janku.jakub.jj@gmail.com</a>><br>
> ><br>
> > Replace existing main while-loop with GMainLoop.<br>
> ><br>
> > Use GIOChannel with g_io_add_watch() to manage IO<br>
> > from x11 connections in the main loop.<br>
> ><br>
> > udscs_connect() now internally creates GSources<br>
> > by calling g_io_add_watch().<br>
> > This enables GMainLoop integration,<br>
> > clients no longer need to call<br>
> > udscs_client_fill_fds() and<br>
> > udscs_client_handle_fds() in a loop.<br>
> > Read callback and udscs_write() functions as before.<br>
> ><br>
> > Signals are also handled in the main loop,<br>
> > using g_unix_signal_add().<br>
> > SIGQUIT is currently not supported by GLib.<br>
> ><br>
> > This enables further GTK+ integration in the future.<br>
> ><br>
> > Signed-off-by: Victor Toso <<a href="mailto:victortoso@redhat.com" target="_blank">victortoso@redhat.com</a>><br>
> > ---<br>
> >  src/udscs.c           |  60 +++++++++++++++++++<br>
> >  src/vdagent/vdagent.c | 159<br>
> >  +++++++++++++++++++++++++++-----------------------<br>
> >  2 files changed, 145 insertions(+), 74 deletions(-)<br>
> ><br>
> > Changes since v4:<br>
> > - remove source ids and use g_source_remove_by_user_data to remove<br>
> >   all sources, even signal ones;<br>
> > - register first connection event with g_timeout_add instead<br>
> >   of calling the callback directly.<br>
> ><br>
> > diff --git a/src/udscs.c b/src/udscs.c<br>
> > index 8b16f89..3a1ea44 100644<br>
> > --- a/src/udscs.c<br>
> > +++ b/src/udscs.c<br>
> > @@ -31,6 +31,8 @@<br>
> >  #include <errno.h><br>
> >  #include <sys/socket.h><br>
> >  #include <sys/un.h><br>
> > +#include <glib.h><br>
> > +#include <glib-unix.h><br>
> >  #include "udscs.h"<br>
> ><br>
> >  struct udscs_buf {<br>
> > @@ -66,8 +68,16 @@ struct udscs_connection {<br>
> ><br>
> >      struct udscs_connection *next;<br>
> >      struct udscs_connection *prev;<br>
> > +<br>
> > +    GIOChannel                     *io_channel;<br>
> > +    guint                           write_watch_id;<br>
> > +    guint                           read_watch_id;<br>
> >  };<br>
> ><br>
> > +static gboolean udscs_io_channel_cb(GIOChannel *source,<br>
> > +                                    GIOCondition condition,<br>
> > +                                    gpointer data);<br>
> > +<br>
> >  struct udscs_connection *udscs_connect(const char *socketname,<br>
> >      udscs_read_callback read_callback,<br>
> >      udscs_disconnect_callback disconnect_callback,<br>
> > @@ -103,6 +113,17 @@ struct udscs_connection *udscs_connect(const char<br>
> > *socketname,<br>
> >          return NULL;<br>
> >      }<br>
> ><br>
> > +    conn->io_channel = g_io_channel_unix_new(conn->fd);<br>
> > +    if (!conn->io_channel) {<br>
> > +        udscs_destroy_connection(&conn);<br>
> > +        return NULL;<br>
> > +    }<br>
> > +    conn->read_watch_id =<br>
> > +        g_io_add_watch(conn->io_channel,<br>
> > +                       G_IO_IN | G_IO_ERR | G_IO_NVAL,<br>
> > +                       udscs_io_channel_cb,<br>
> > +                       conn);<br>
> > +<br>
> >      conn->read_callback = read_callback;<br>
> >      conn->disconnect_callback = disconnect_callback;<br>
> ><br>
> > @@ -141,6 +162,12 @@ void udscs_destroy_connection(struct udscs_connection<br>
> > **connp)<br>
> ><br>
> >      close(conn->fd);<br>
> ><br>
> > +    if (conn->write_watch_id != 0)<br>
> > +        g_source_remove(conn->write_watch_id);<br>
> > +    if (conn->read_watch_id != 0)<br>
> > +        g_source_remove(conn->read_watch_id);<br>
> > +    g_clear_pointer(&conn->io_channel, g_io_channel_unref);<br>
> > +<br>
> >      if (conn->debug)<br>
> >          syslog(LOG_DEBUG, "%p disconnected", conn);<br>
> ><br>
> > @@ -198,6 +225,13 @@ int udscs_write(struct udscs_connection *conn,<br>
> > uint32_t type, uint32_t arg1,<br>
> >                     conn, type, arg1, arg2, size);<br>
> >      }<br>
> ><br>
> > +    if (conn->io_channel && conn->write_watch_id == 0)<br>
> > +        conn->write_watch_id =<br>
> > +            g_io_add_watch(conn->io_channel,<br>
> > +                           G_IO_OUT | G_IO_ERR | G_IO_NVAL,<br>
> > +                           udscs_io_channel_cb,<br>
> > +                           conn);<br>
> > +<br>
> >      if (!conn->write_buf) {<br>
> >          conn->write_buf = new_wbuf;<br>
> >          return 0;<br>
> > @@ -353,6 +387,32 @@ int udscs_client_fill_fds(struct udscs_connection<br>
> > *conn, fd_set *readfds,<br>
> >      return conn->fd + 1;<br>
> >  }<br>
> ><br>
> > +static gboolean udscs_io_channel_cb(GIOChannel *source,<br>
> > +                                    GIOCondition condition,<br>
> > +                                    gpointer data)<br>
> > +{<br>
> > +    struct udscs_connection *conn = data;<br>
> > +<br>
> > +    if (condition & G_IO_IN) {<br>
> > +        udscs_do_read(&conn);<br>
> > +        if (conn == NULL)<br>
> > +            return G_SOURCE_REMOVE;<br>
> > +        return G_SOURCE_CONTINUE;<br>
> > +    }<br>
> > +    if (condition & G_IO_OUT) {<br>
> > +        udscs_do_write(&conn);<br>
> > +        if (conn == NULL)<br>
> > +            return G_SOURCE_REMOVE;<br>
> > +        if (conn->write_buf)<br>
> > +            return G_SOURCE_CONTINUE;<br>
> > +        conn->write_watch_id = 0;<br>
> > +        return G_SOURCE_REMOVE;<br>
> > +    }<br>
> > +<br>
> > +    udscs_destroy_connection(&conn);<br>
> > +    return G_SOURCE_REMOVE;<br>
> > +}<br>
> > +<br>
> ><br>
> >  #ifndef UDSCS_NO_SERVER<br>
> ><br>
> > diff --git a/src/vdagent/vdagent.c b/src/vdagent/vdagent.c<br>
> > index 69d8786..ac90634 100644<br>
> > --- a/src/vdagent/vdagent.c<br>
> > +++ b/src/vdagent/vdagent.c<br>
> > @@ -34,8 +34,8 @@<br>
> >  #include <sys/select.h><br>
> >  #include <sys/stat.h><br>
> >  #include <spice/vd_agent.h><br>
> > -#include <glib.h><br>
> >  #include <poll.h><br>
> > +#include <glib-unix.h><br>
> ><br>
> >  #include "udscs.h"<br>
> >  #include "vdagentd-proto.h"<br>
> > @@ -48,6 +48,9 @@ typedef struct VDAgent {<br>
> >      struct vdagent_x11 *x11;<br>
> >      struct vdagent_file_xfers *xfers;<br>
> >      struct udscs_connection *conn;<br>
> > +    GIOChannel *x11_channel;<br>
> > +<br>
> > +    GMainLoop *loop;<br>
> >  } VDAgent;<br>
> ><br>
> >  static int quit = 0;<br>
> > @@ -177,7 +180,7 @@ static void daemon_read_complete(struct<br>
> > udscs_connection **connp,<br>
> >          if (strcmp((char *)data, VERSION) != 0) {<br>
> >              syslog(LOG_INFO, "vdagentd version mismatch: got %s expected<br>
> >              %s",<br>
> >                     data, VERSION);<br>
> > -            udscs_destroy_connection(connp);<br>
> > +            g_main_loop_quit(agent->loop);<br>
> >              version_mismatch = 1;<br>
> >          }<br>
> >          break;<br>
> > @@ -235,25 +238,12 @@ static void daemon_read_complete(struct<br>
> > udscs_connection **connp,<br>
> >      }<br>
> >  }<br>
> ><br>
> > -static struct udscs_connection *client_setup_sync(void)<br>
> > -{<br>
> > -    struct udscs_connection *conn = NULL;<br>
> > -<br>
> > -    while (!quit) {<br>
> > -        conn = udscs_connect(vdagentd_socket, daemon_read_complete, NULL,<br>
> > -                             vdagentd_messages, VDAGENTD_NO_MESSAGES,<br>
> > -                             debug);<br>
> > -        if (conn || !do_daemonize || quit) {<br>
> > -            break;<br>
> > -        }<br>
> > -        sleep(1);<br>
> > -    }<br>
> > -    return conn;<br>
> > -}<br>
> > -<br>
> > -static void quit_handler(int sig)<br>
> > +static void daemon_disconnect_cb(struct udscs_connection *conn)<br>
> >  {<br>
> > -    quit = 1;<br>
> > +    VDAgent *agent = udscs_get_user_data(conn);<br>
> > +    agent->conn = NULL;<br>
> > +    if (agent->loop)<br>
> > +        g_main_loop_quit(agent->loop);<br>
> >  }<br>
> ><br>
> >  /* When we daemonize, it is useful to have the main process<br>
> > @@ -311,10 +301,34 @@ static int file_test(const char *path)<br>
> >      return stat(path, &buffer);<br>
> >  }<br>
> ><br>
> > +static gboolean x11_io_channel_cb(GIOChannel *source,<br>
> > +                                  GIOCondition condition,<br>
> > +                                  gpointer data)<br>
> > +{<br>
> > +    VDAgent *agent = data;<br>
> > +    vdagent_x11_do_read(agent->x11);<br>
> > +<br>
> > +    return G_SOURCE_CONTINUE;<br>
> > +}<br>
> > +<br>
> > +gboolean vdagent_signal_handler(gpointer user_data)<br>
> > +{<br>
> > +    VDAgent *agent = user_data;<br>
> > +    quit = TRUE;<br>
> > +    g_main_loop_quit(agent->loop);<br>
> > +    return G_SOURCE_REMOVE;<br>
> > +}<br>
> > +<br>
> >  static VDAgent *vdagent_new(void)<br>
> >  {<br>
> >      VDAgent *agent = g_new0(VDAgent, 1);<br>
> ><br>
> > +    agent->loop = g_main_loop_new(NULL, FALSE);<br>
> > +<br>
> > +    g_unix_signal_add(SIGINT, vdagent_signal_handler, agent);<br>
> > +    g_unix_signal_add(SIGHUP, vdagent_signal_handler, agent);<br>
> > +    g_unix_signal_add(SIGTERM, vdagent_signal_handler, agent);<br>
> > +<br>
> >      return agent;<br>
> >  }<br>
> ><br>
> > @@ -324,14 +338,59 @@ static void vdagent_destroy(VDAgent *agent)<br>
> >      vdagent_x11_destroy(agent->x11, agent->conn == NULL);<br>
> >      udscs_destroy_connection(&agent->conn);<br>
> ><br>
> > +    while (g_source_remove_by_user_data(agent))<br>
> > +        continue;<br>
> > +<br>
> > +    g_clear_pointer(&agent->x11_channel, g_io_channel_unref);<br>
> > +    g_clear_pointer(&agent->loop, g_main_loop_unref);<br>
> >      g_free(agent);<br>
> >  }<br>
> ><br>
> > +static gboolean vdagent_init_async_cb(gpointer user_data)<br>
> > +{<br>
> > +    VDAgent *agent = user_data;<br>
> > +<br>
> > +    agent->conn = udscs_connect(vdagentd_socket,<br>
> > +                                daemon_read_complete,<br>
> > daemon_disconnect_cb,<br>
> > +                                vdagentd_messages, VDAGENTD_NO_MESSAGES,<br>
> > debug);<br>
> > +    if (agent->conn == NULL) {<br>
> > +        g_timeout_add_seconds(1, vdagent_init_async_cb, agent);<br>
> > +        return G_SOURCE_REMOVE;<br>
> > +    }<br>
> > +    udscs_set_user_data(agent->conn, agent);<br>
> > +<br>
> > +    agent->x11 = vdagent_x11_create(agent->conn, debug, x11_sync);<br>
> > +    if (agent->x11 == NULL)<br>
> > +        goto err_init;<br>
> > +    agent->x11_channel =<br>
> > g_io_channel_unix_new(vdagent_x11_get_fd(agent->x11));<br>
> > +    if (agent->x11_channel == NULL)<br>
> > +        goto err_init;<br>
> > +<br>
> > +    g_io_add_watch(agent->x11_channel,<br>
> > +                   G_IO_IN,<br>
> > +                   x11_io_channel_cb,<br>
> > +                   agent);<br>
> > +<br>
> > +    if (!vdagent_init_file_xfer(agent))<br>
> > +        syslog(LOG_WARNING, "File transfer is disabled");<br>
> > +<br>
> > +    if (parent_socket != -1) {<br>
> > +        if (write(parent_socket, "OK", 2) != 2)<br>
> > +            syslog(LOG_WARNING, "Parent already gone.");<br>
> > +        close(parent_socket);<br>
> > +        parent_socket = -1;<br>
> > +    }<br>
> > +<br>
> > +    return G_SOURCE_REMOVE;<br>
> > +<br>
> > +err_init:<br>
> > +    g_main_loop_quit(agent->loop);<br>
> > +    quit = TRUE;<br>
> > +    return G_SOURCE_REMOVE;<br>
> > +}<br>
> > +<br>
> >  int main(int argc, char *argv[])<br>
> >  {<br>
> > -    fd_set readfds, writefds;<br>
> > -    int n, nfds, x11_fd;<br>
> > -    struct sigaction act;<br>
> >      GOptionContext *context;<br>
> >      GError *error = NULL;<br>
> >      VDAgent *agent;<br>
> > @@ -357,14 +416,6 @@ int main(int argc, char *argv[])<br>
> >      if (vdagentd_socket == NULL)<br>
> >          vdagentd_socket = g_strdup(VDAGENTD_SOCKET);<br>
> ><br>
> > -    memset(&act, 0, sizeof(act));<br>
> > -    act.sa_flags = SA_RESTART;<br>
> > -    act.sa_handler = quit_handler;<br>
> > -    sigaction(SIGINT, &act, NULL);<br>
> > -    sigaction(SIGHUP, &act, NULL);<br>
> > -    sigaction(SIGTERM, &act, NULL);<br>
> > -    sigaction(SIGQUIT, &act, NULL);<br>
> > -<br>
> >      openlog("spice-vdagent", do_daemonize ? LOG_PID : (LOG_PID |<br>
> >      LOG_PERROR),<br>
> >              LOG_USER);<br>
> ><br>
> > @@ -385,53 +436,13 @@ reconnect:<br>
> ><br>
> >      agent = vdagent_new();<br>
> ><br>
> > -    agent->conn = client_setup_sync();<br>
> > -    if (agent->conn == NULL) {<br>
> > -        return 1;<br>
> > -    }<br>
> > -    udscs_set_user_data(agent->conn, agent);<br>
> > +    g_timeout_add(0, vdagent_init_async_cb, agent);<br>
> ><br>
> > -    agent->x11 = vdagent_x11_create(agent->conn, debug, x11_sync);<br>
> > -    if (!agent->x11) {<br>
> > -        udscs_destroy_connection(&agent->conn);<br>
> > -        return 1;<br>
> > -    }<br>
> > -<br>
> > -    if (!vdagent_init_file_xfer(agent))<br>
> > -        syslog(LOG_WARNING, "File transfer is disabled");<br>
> > -<br>
> > -    if (parent_socket != -1) {<br>
> > -        if (write(parent_socket, "OK", 2) != 2)<br>
> > -            syslog(LOG_WARNING, "Parent already gone.");<br>
> > -        close(parent_socket);<br>
> > -        parent_socket = -1;<br>
> > -    }<br>
> > -<br>
> > -    while (agent->conn && !quit) {<br>
> > -        FD_ZERO(&readfds);<br>
> > -        FD_ZERO(&writefds);<br>
> > -<br>
> > -        nfds = udscs_client_fill_fds(agent->conn, &readfds, &writefds);<br>
> > -        x11_fd = vdagent_x11_get_fd(agent->x11);<br>
> > -        FD_SET(x11_fd, &readfds);<br>
> > -        if (x11_fd >= nfds)<br>
> > -            nfds = x11_fd + 1;<br>
> > -<br>
> > -        n = select(nfds, &readfds, &writefds, NULL, NULL);<br>
> > -        if (n == -1) {<br>
> > -            if (errno == EINTR)<br>
> > -                continue;<br>
> > -            syslog(LOG_ERR, "Fatal error select: %s", strerror(errno));<br>
> > -            break;<br>
> > -        }<br>
> > -<br>
> > -        if (FD_ISSET(x11_fd, &readfds))<br>
> > -            vdagent_x11_do_read(agent->x11);<br>
> > -        udscs_client_handle_fds(&agent->conn, &readfds, &writefds);<br>
> > -    }<br>
> > +    g_main_loop_run(agent->loop);<br>
> ><br>
> >      vdagent_destroy(agent);<br>
> >      agent = NULL;<br>
> > +<br>
> >      if (!quit && do_daemonize)<br>
> >          goto reconnect;<br>
> ><br>
> > --<br>
> > 2.13.6<br>
> ><br>
><br>
> Just one more note on this:<br>
> According to GTK docs for g_timeout_add_seconds():<br>
> "Note that the first call of the timer may not be precise for timeouts<br>
> of one second.<br>
> If you need finer precision and have such a timeout,<br>
> you may want to use g_timeout_add() instead."<br>
> (this is basically the same what Christophe pointed out)<br>
><br>
<br>
Yes, first iteration uses (last patch version) g_timeout_add.<br>
<br>
> Since we remove the source in vdagent_init_async_cb() each time,<br>
> the interval between each call might not be regular.<br>
><br>
<br>
>From g_timeout_add_seconds_full documentation:<br>
"Note that timeout functions may be delayed, due to the processing of other<br>
event sources. Thus they should not be relied on for precise timing. After<br>
each call to the timeout function, the time of the next timeout is recalculated<br>
based on the current time and the given interval"<br>
so creating the source every time does not change much on the timing.<br>
<br>
> One solution would be to use g_timeout_add().<br>
><br>
> Another solution:<br>
> - in main():<br>
> replace g_timeout_add(0, vdagent_init_async_cb, agent);<br>
> with g_idle_add(vdagent_init_async_cb, agent);<br>
><br>
> - in vdagent_init_async_cb():<br>
> if (agent->conn == NULL) {<br>
>     if (g_idle_remove_by_data(agent)) {<br>
>         g_timeout_add_seconds(1, vdagent_init_async_cb, agent);<br>
>         return G_SOURCE_REMOVE;<br>
>     }<br>
>     return G_SOURCE_CONTINUE;<br>
> }<br>
> This way, the source doesn't have to be recreated every single time.<br>
><br>
> Jakub<br>
><br>
<br>
1 seconds is quite "human" timing thing, I don't think we need big<br>
precision. If we really need precision I would use g_file_monitor_directory<br>
where available to avoid entirely all the polling (which would be limited<br>
to Unix systems without inotify or other GLib compatible implementations).<br>
This way you can detect more or less precisely when the unix socket is<br>
created/modified in the directory without having to check every second.<br>
<br>
The g_idle solution should work and avoid creating the source every time,<br>
only drawback is that adding another g_idle in a possible future is<br>
tricky and prone to bugs as potentially the g_idle_remove_by_data would<br>
remove the wrong source.<br>
<br>
Frediano<br></blockquote></div><div><br></div><div>That's true, however, I don't think we will add another idle function any time soon.</div><div>It was just a note, I think we could leave it as it is,</div><div>if nobody else complains.</div><div><br></div><div>Thanks,</div><div>Jakub</div><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
</blockquote></div>