[Spice-devel] [PATCH 10/15] worker: use glib main loop

Marc-André Lureau mlureau at redhat.com
Fri Dec 4 01:28:51 PST 2015



----- Original Message -----
> On Thu, 2015-12-03 at 16:27 +0000, Frediano Ziglio wrote:
> > From: Marc-André Lureau <marcandre.lureau at gmail.com>
> > 
> > Clean up, more extensible.
> 
> I think this should be a bit less terse... perhaps:
> Use the glib mainloop instead of writing our own. The glib loop is both
> cleaner
> to use and is more extensible. It is also very mature and reduces the
> maintenance burden on the spice server.
> 
> > 
> > Avoid server hanging when no client are connected.
> 
> 
> This sounds like something that might belong in a separate patch?
> 
> 
> > ---
> >  server/Makefile.am         |   2 -
> >  server/red-worker.c        | 395
> >  ++++++++++++++++++++++++++++----------------
> > -
> >  server/red-worker.h        |   1 +
> >  server/spice_timer_queue.c | 273 -------------------------------
> >  server/spice_timer_queue.h |  43 -----
> >  5 files changed, 246 insertions(+), 468 deletions(-)
> >  delete mode 100644 server/spice_timer_queue.c
> >  delete mode 100644 server/spice_timer_queue.h
> > 
> > diff --git a/server/Makefile.am b/server/Makefile.am
> > index d4fc972..88825d8 100644
> > --- a/server/Makefile.am
> > +++ b/server/Makefile.am
> > @@ -121,8 +121,6 @@ libspice_server_la_SOURCES =			\
> >  	spice.h					\
> >  	stat.h					\
> >  	spicevmc.c				\
> > -	spice_timer_queue.c			\
> > -	spice_timer_queue.h			\
> >  	zlib-encoder.c				\
> >  	zlib-encoder.h				\
> >  	image-cache.h			\
> > diff --git a/server/red-worker.c b/server/red-worker.c
> > index aecfcf9..9e8fcbb 100644
> > --- a/server/red-worker.c
> > +++ b/server/red-worker.c
> > @@ -49,31 +49,21 @@
> >  
> >  #include "spice.h"
> >  #include "red-worker.h"
> > -#include "spice_timer_queue.h"
> >  #include "cursor-channel.h"
> >  #include "tree.h"
> >  
> >  #define CMD_RING_POLL_TIMEOUT 10 //milli
> >  #define CMD_RING_POLL_RETRIES 200
> >  
> > -#define MAX_EVENT_SOURCES 20
> > -#define INF_EVENT_WAIT ~0
> > -
> > -struct SpiceWatch {
> > -    struct RedWorker *worker;
> > -    SpiceWatchFunc watch_func;
> > -    void *watch_func_opaque;
> > -};
> > -
> >  struct RedWorker {
> >      pthread_t thread;
> >      clockid_t clockid;
> > +    GMainContext *main_context;
> >      QXLInstance *qxl;
> >      RedDispatcher *red_dispatcher;
> >      int running;
> > -    struct pollfd poll_fds[MAX_EVENT_SOURCES];
> > -    struct SpiceWatch watches[MAX_EVENT_SOURCES];
> > -    unsigned int event_timeout;
> > +
> > +    gint timeout;
> >  
> >      DisplayChannel *display_channel;
> >      uint32_t display_poll_tries;
> > @@ -99,6 +89,13 @@ struct RedWorker {
> >      FILE *record_fd;
> >  };
> >  
> > +GMainContext* red_worker_get_context(RedWorker *worker)
> 
> 
> I'd prefer _get_main_context()

fwiw, glib api uses _get_context() too (task_get_context(), source_get_context() etc)

> 
> 
> > +{
> > +    spice_return_val_if_fail(worker, NULL);
> > +
> > +    return worker->main_context;
> > +}
> > +
> >  QXLInstance* red_worker_get_qxl(RedWorker *worker)
> >  {
> >      spice_return_val_if_fail(worker != NULL, NULL);
> > @@ -182,7 +179,9 @@ static int red_process_cursor(RedWorker *worker,
> > uint32_t
> > max_pipe_size, int *ri
> >              *ring_is_empty = TRUE;
> >              if (worker->cursor_poll_tries < CMD_RING_POLL_RETRIES) {
> >                  worker->cursor_poll_tries++;
> > -                worker->event_timeout = MIN(worker->event_timeout,
> > CMD_RING_POLL_TIMEOUT);
> > +                worker->timeout = worker->timeout == -1 ?
> > +                    CMD_RING_POLL_TIMEOUT :
> > +                    MIN(worker->timeout, CMD_RING_POLL_TIMEOUT);
> >                  return n;
> >              }
> >              if (worker->cursor_poll_tries > CMD_RING_POLL_RETRIES ||
> > @@ -228,7 +227,6 @@ static int red_process_display(RedWorker *worker,
> > uint32_t
> > max_pipe_size, int *r
> >  {
> >      QXLCommandExt ext_cmd;
> >      int n = 0;
> > -    uint64_t start = red_get_monotonic_time();
> >  
> >      if (!worker->running) {
> >          *ring_is_empty = TRUE;
> > @@ -237,14 +235,30 @@ static int red_process_display(RedWorker *worker,
> > uint32_t max_pipe_size, int *r
> >  
> >      worker->process_display_generation++;
> >      *ring_is_empty = FALSE;
> > -    while (!display_is_connected(worker) ||
> > -           // TODO: change to average pipe size?
> > -           red_channel_min_pipe_size(RED_CHANNEL(worker->display_channel))
> > <=
> > max_pipe_size) {
> > +    for (;;) {
> > +
> > +        if (display_is_connected(worker)) {
> > +
> > +            if (red_channel_all_blocked(RED_CHANNEL(worker
> > ->display_channel))) {
> > +                spice_info("all display clients are blocking");
> > +                return n;
> > +            }
> 
> This is a change of behavior. The previous code checked for all_blocked()
> after
> calling to qif->get_command() and incrementing n. (Could this be related to
> the
> "avoid server hanging" comment from the commit log?)

(I don't remember, sorry. I should try to review my own patch, with probably some modifications due to the rebase)

> 
> 
> > +
> > +
> > +            // TODO: change to average pipe size?
> > +            if (red_channel_min_pipe_size(RED_CHANNEL(worker
> > ->display_channel)) > max_pipe_size) {
> > +                spice_info("too much item in the display clients pipe
> > already");
> 
> "too much item" is not proper. Change to "Too many items". Also, "clients" ->
> "client's"
> 
> 
> > +                return n;
> > +            }
> > +        }
> > +
> >          if (!worker->qxl->st->qif->get_command(worker->qxl, &ext_cmd)) {
> >              *ring_is_empty = TRUE;;
> >              if (worker->display_poll_tries < CMD_RING_POLL_RETRIES) {
> >                  worker->display_poll_tries++;
> > -                worker->event_timeout = MIN(worker->event_timeout,
> > CMD_RING_POLL_TIMEOUT);
> > +                worker->timeout = worker->timeout == -1 ?
> > +                    CMD_RING_POLL_TIMEOUT :
> > +                    MIN(worker->timeout, CMD_RING_POLL_TIMEOUT);
> 
> 
> I find this a bit awkward. I'd almost consider reverting to the previous
> approach (where the timeout was an unsigned in initially set to ~0) or adding
> a
> simple inline function such as red_worker_update_timeout() to hide this
> logic.
> 
> 
> >                  return n;
> >              }
> >              if (worker->display_poll_tries > CMD_RING_POLL_RETRIES ||
> > @@ -329,13 +343,8 @@ static int red_process_display(RedWorker *worker,
> > uint32_t max_pipe_size, int *r
> >              spice_error("bad command type");
> >          }
> >          n++;
> > -        if ((worker->display_channel &&
> > -
> > red_channel_all_blocked(&worker->display_channel->common.base))
> > -            || red_get_monotonic_time() - start > 10 * 1000 * 1000) {
> 
> this all_blocked() check was moved up (as mentioned above), but elapsed time
> check was simply dropped with no explanation...
> 
> 
> > -            worker->event_timeout = 0;
> > -            return n;
> > -        }
> >      }
> > +
> >      return n;
> >  }
> >  
> > @@ -511,81 +520,159 @@ static int
> > common_channel_config_socket(RedChannelClient *rcc)
> >      return TRUE;
> >  }
> >  
> > -static void worker_watch_update_mask(SpiceWatch *watch, int event_mask)
> > +typedef struct SpiceTimer {
> > +    SpiceTimerFunc func;
> > +    void *opaque;
> > +    guint source_id;
> > +} SpiceTimer;
> > +
> > +static SpiceTimer* worker_timer_add(SpiceTimerFunc func, void *opaque)
> >  {
> > -    struct RedWorker *worker;
> > -    int i;
> > +    SpiceTimer *timer = g_new0(SpiceTimer, 1);
> > +
> > +    timer->func = func;
> > +    timer->opaque = opaque;
> > +
> > +    return timer;
> > +}
> > +
> > +static gboolean worker_timer_func(gpointer user_data)
> > +{
> > +    SpiceTimer *timer = user_data;
> > +
> > +    timer->source_id = 0;
> > +    timer->func(timer->opaque);
> > +    /* timer might be free after func(), don't touch */
> > +
> > +    return FALSE;
> > +}
> >  
> > -    if (!watch) {
> > +static void worker_timer_cancel(SpiceTimer *timer)
> > +{
> > +    if (timer->source_id == 0)
> >          return;
> 
> use g_return_if_fail() here? It seems to me that canceling a timer without a
> source_id may indicate programmer error...
> 
> 
> > -    }
> >  
> > -    worker = watch->worker;
> > -    i = watch - worker->watches;
> > +    g_source_remove(timer->source_id);
> > +    timer->source_id = 0;
> > +}
> >  
> > -    worker->poll_fds[i].events = 0;
> > -    if (event_mask & SPICE_WATCH_EVENT_READ) {
> > -        worker->poll_fds[i].events |= POLLIN;
> > -    }
> > -    if (event_mask & SPICE_WATCH_EVENT_WRITE) {
> > -        worker->poll_fds[i].events |= POLLOUT;
> > +static void worker_timer_start(SpiceTimer *timer, uint32_t ms)
> > +{
> > +    worker_timer_cancel(timer);
> > +
> > +    timer->source_id = g_timeout_add(ms, worker_timer_func, timer);
> > +}
> > +
> > +static void worker_timer_remove(SpiceTimer *timer)
> > +{
> > +    worker_timer_cancel(timer);
> > +    g_free(timer);
> > +}
> > +
> > +static GIOCondition spice_event_to_giocondition(int event_mask)
> > +{
> > +    GIOCondition condition = 0;
> > +
> > +    if (event_mask & SPICE_WATCH_EVENT_READ)
> > +        condition |= G_IO_IN;
> > +    if (event_mask & SPICE_WATCH_EVENT_WRITE)
> > +        condition |= G_IO_OUT;
> > +
> > +    return condition;
> > +}
> > +
> > +static int giocondition_to_spice_event(GIOCondition condition)
> > +{
> > +    int event = 0;
> > +
> > +    if (condition & G_IO_IN)
> > +        event |= SPICE_WATCH_EVENT_READ;
> > +    if (condition & G_IO_OUT)
> > +        event |= SPICE_WATCH_EVENT_WRITE;
> > +
> > +    return event;
> > +}
> > +
> > +struct SpiceWatch {
> > +    GIOChannel *channel;
> > +    GSource *source;
> > +    RedChannelClient *rcc;
> > +    SpiceWatchFunc func;
> > +};
> > +
> > +static gboolean watch_func(GIOChannel *source, GIOCondition condition,
> > +                           gpointer data)
> > +{
> > +    SpiceWatch *watch = data;
> > +    int fd = g_io_channel_unix_get_fd(source);
> > +
> > +    watch->func(fd, giocondition_to_spice_event(condition), watch->rcc);
> > +
> > +    return TRUE;
> > +}
> > +
> > +static void worker_watch_update_mask(SpiceWatch *watch, int events)
> > +{
> > +    RedWorker *worker;
> > +
> > +    spice_return_if_fail(watch != NULL);
> > +    worker = SPICE_CONTAINEROF(watch->rcc->channel, CommonChannel, base)
> > ->worker;
> > +
> > +    if (watch->source) {
> > +        g_source_destroy(watch->source);
> > +        watch->source = NULL;
> >      }
> > +
> > +    if (!events)
> > +        return;
> > +
> > +    watch->source = g_io_create_watch(watch->channel,
> > spice_event_to_giocondition(events));
> > +    g_source_set_callback(watch->source, (GSourceFunc)watch_func, watch,
> > NULL);
> > +    g_source_attach(watch->source, worker->main_context);
> >  }
> >  
> > -static SpiceWatch *worker_watch_add(int fd, int event_mask, SpiceWatchFunc
> > func, void *opaque)
> > +static SpiceWatch* worker_watch_add(int fd, int events, SpiceWatchFunc
> > func,
> > void *opaque)
> >  {
> > -    /* Since we are a channel core implementation, we always get called
> > from
> > -       red_channel_client_create(), so opaque always is our rcc */
> 
> I think I would leave this comment in. It feels a bit fragile to make this
> assumption in the first place, so I'd at least like a comment explaining why
> we
> can assume this.
> 
> 
> >      RedChannelClient *rcc = opaque;
> > -    struct RedWorker *worker;
> > -    int i;
> > +    RedWorker *worker;
> > +    SpiceWatch *watch;
> > +
> > +    spice_return_val_if_fail(rcc != NULL, NULL);
> > +    spice_return_val_if_fail(fd != -1, NULL);
> > +    spice_return_val_if_fail(func != NULL, NULL);
> >  
> >      /* Since we are called from red_channel_client_create()
> >         CommonChannelClient->worker has not been set yet! */
> >      worker = SPICE_CONTAINEROF(rcc->channel, CommonChannel, base)->worker;
> > +    spice_return_val_if_fail(worker != NULL, NULL);
> > +    spice_return_val_if_fail(worker->main_context != NULL, NULL);
> >  
> > -    /* Search for a free slot in our poll_fds & watches arrays */
> > -    for (i = 0; i < MAX_EVENT_SOURCES; i++) {
> > -        if (worker->poll_fds[i].fd == -1) {
> > -            break;
> > -        }
> > -    }
> > -    if (i == MAX_EVENT_SOURCES) {
> > -        spice_warning("could not add a watch for channel type %u id %u",
> > -                      rcc->channel->type, rcc->channel->id);
> > -        return NULL;
> > -    }
> > +    watch = g_new0(SpiceWatch, 1);
> > +    watch->channel = g_io_channel_unix_new(fd);
> > +    watch->rcc = rcc;
> > +    watch->func = func;
> >  
> > -    worker->poll_fds[i].fd = fd;
> > -    worker->watches[i].worker = worker;
> > -    worker->watches[i].watch_func = func;
> > -    worker->watches[i].watch_func_opaque = opaque;
> > -    worker_watch_update_mask(&worker->watches[i], event_mask);
> > +    worker_watch_update_mask(watch, events);
> >  
> > -    return &worker->watches[i];
> > +    return watch;
> >  }
> >  
> >  static void worker_watch_remove(SpiceWatch *watch)
> >  {
> > -    if (!watch) {
> > -        return;
> > -    }
> > +    spice_return_if_fail(watch != NULL);
> >  
> > -    /* Note we don't touch the poll_fd here, to avoid the
> > -       poll_fds/watches table entry getting re-used in the same
> > -       red_worker_main loop over the fds as it is removed.
> > +    if (watch->source)
> > +        g_source_destroy(watch->source);
> >  
> > -       This is done because re-using it while events were pending on
> > -       the fd previously occupying the slot would lead to incorrectly
> > -       calling the watch_func for the new fd. */
> > -    memset(watch, 0, sizeof(SpiceWatch));
> > +    g_io_channel_unref(watch->channel);
> > +    g_free(watch);
> >  }
> >  
> > -SpiceCoreInterface worker_core = {
> > -    .timer_add = spice_timer_queue_add,
> > -    .timer_start = spice_timer_set,
> > -    .timer_cancel = spice_timer_cancel,
> > -    .timer_remove = spice_timer_remove,
> > +static const SpiceCoreInterface worker_core = {
> > +    .timer_add = worker_timer_add,
> > +    .timer_start = worker_timer_start,
> > +    .timer_cancel = worker_timer_cancel,
> > +    .timer_remove = worker_timer_remove,
> >  
> >      .watch_update_mask = worker_watch_update_mask,
> >      .watch_add = worker_watch_add,
> > @@ -1528,24 +1615,87 @@ static void register_callbacks(Dispatcher
> > *dispatcher)
> >  
> >  
> >  
> > -static void handle_dev_input(int fd, int event, void *opaque)
> > +static gboolean worker_dispatcher_cb(GIOChannel *source, GIOCondition
> > condition,
> > +                                     gpointer data)
> >  {
> > -    RedWorker *worker = opaque;
> > +    RedWorker *worker = data;
> >  
> > +    spice_debug(NULL);
> 
> I don't think this is necessary.
> 
> >      dispatcher_handle_recv_read(red_dispatcher_get_dispatcher(worker
> > ->red_dispatcher));
> > +
> > +    return TRUE;
> > +}
> > +
> > +typedef struct RedWorkerSource {
> > +    GSource source;
> > +    RedWorker *worker;
> > +} RedWorkerSource;
> > +
> > +static gboolean worker_source_prepare(GSource *source, gint *timeout)
> > +{
> > +    RedWorkerSource *wsource = (RedWorkerSource *)source;
> > +    RedWorker *worker = wsource->worker;
> > +
> > +    *timeout = worker->timeout;
> > +    *timeout = MIN(worker->timeout,
> > +                   display_channel_get_streams_timeout(worker
> > ->display_channel));
> > +
> > +    return FALSE; /* do no timeout poll */
> 
> This comment is confusing. it implies that returning FALSE means that we
> don't
> poll? In fact, returning TRUE from the prepare function indicates that the
> source is already ready and we don't need to poll...
> 
> > +}
> > +
> > +static gboolean worker_source_check(GSource *source)
> > +{
> > +    RedWorkerSource *wsource = (RedWorkerSource *)source;
> > +    RedWorker *worker = wsource->worker;
> > +
> > +    return worker->running /* TODO && worker->pending_process */;
> >  }
> >  
> > +static gboolean worker_source_dispatch(GSource *source, GSourceFunc
> > callback,
> > +                                       gpointer user_data)
> > +{
> > +    RedWorkerSource *wsource = (RedWorkerSource *)source;
> > +    RedWorker *worker = wsource->worker;
> > +    DisplayChannel *display = worker->display_channel;
> > +    int ring_is_empty;
> > +
> > +    /* during migration, in the dest, the display channel can be
> > initialized
> > +       while the global lz data not since migrate data msg hasn't been
> > +       received yet */
> > +    /* FIXME: why is this here, and not in display_channel_create */
> > +    display_channel_free_glz_drawables_to_free(display);
> 
> In the previous version, this call was inside of a 'if (worker
> ->display_channel)' branch. Here that is not checked.
> 
> > +
> > +    /* FIXME: could use its own source */
> > +    stream_timeout(display);
> > +
> > +    worker->timeout = -1;
> > +    red_process_cursor(worker, MAX_PIPE_SIZE, &ring_is_empty);
> > +    red_process_display(worker, MAX_PIPE_SIZE, &ring_is_empty);
> > +
> > +    /* FIXME: remove me? that should be handled by watch out condition */
> > +    red_push(worker);
> > +
> > +    return TRUE;
> > +}
> > +
> > +/* cannot be const */
> > +static GSourceFuncs worker_source_funcs = {
> > +    .prepare = worker_source_prepare,
> > +    .check = worker_source_check,
> > +    .dispatch = worker_source_dispatch,
> > +};
> > +
> >  RedWorker* red_worker_new(QXLInstance *qxl, RedDispatcher *red_dispatcher)
> >  {
> >      QXLDevInitInfo init_info;
> >      RedWorker *worker;
> >      Dispatcher *dispatcher;
> > -    int i;
> >      const char *record_filename;
> >  
> >      qxl->st->qif->get_init_info(qxl, &init_info);
> >  
> >      worker = spice_new0(RedWorker, 1);
> > +    worker->main_context = g_main_context_new();
> >  
> >      record_filename = getenv("SPICE_WORKER_RECORD_FILENAME");
> >      if (record_filename) {
> > @@ -1579,15 +1729,18 @@ RedWorker* red_worker_new(QXLInstance *qxl,
> > RedDispatcher *red_dispatcher)
> >      worker->wakeup_counter = stat_add_counter(worker->stat, "wakeups",
> >      TRUE);
> >      worker->command_counter = stat_add_counter(worker->stat, "commands",
> > TRUE);
> >  #endif
> > -    for (i = 0; i < MAX_EVENT_SOURCES; i++) {
> > -        worker->poll_fds[i].fd = -1;
> > -    }
> >  
> > -    worker->poll_fds[0].fd = dispatcher_get_recv_fd(dispatcher);
> > -    worker->poll_fds[0].events = POLLIN;
> > -    worker->watches[0].worker = worker;
> > -    worker->watches[0].watch_func = handle_dev_input;
> > -    worker->watches[0].watch_func_opaque = worker;
> > +    GIOChannel *channel =
> > g_io_channel_unix_new(dispatcher_get_recv_fd(dispatcher));
> > +    GSource *source = g_io_create_watch(channel, G_IO_IN);
> > +    g_source_set_callback(source, (GSourceFunc)worker_dispatcher_cb,
> > worker,
> > NULL);
> > +    g_source_attach(source, worker->main_context);
> > +    g_source_unref(source);
> > +
> > +    source = g_source_new(&worker_source_funcs, sizeof(RedWorkerSource));
> > +    RedWorkerSource *wsource = (RedWorkerSource *)source;
> > +    wsource->worker = worker;
> > +    g_source_attach(source, worker->main_context);
> > +    g_source_unref(source);
> >  
> >      memslot_info_init(&worker->mem_slots,
> >                        init_info.num_memslots_groups,
> > @@ -1598,7 +1751,7 @@ RedWorker* red_worker_new(QXLInstance *qxl,
> > RedDispatcher *red_dispatcher)
> >  
> >      spice_warn_if(init_info.n_surfaces > NUM_SURFACES);
> >  
> > -    worker->event_timeout = INF_EVENT_WAIT;
> > +    worker->timeout = -1;
> >  
> >      worker->cursor_channel = cursor_channel_new(worker);
> >      // TODO: handle seemless migration. Temp, setting migrate to FALSE
> > @@ -1616,10 +1769,6 @@ SPICE_GNUC_NORETURN static void
> > *red_worker_main(void
> > *arg)
> >      spice_assert(MAX_PIPE_SIZE > WIDE_CLIENT_ACK_WINDOW &&
> >             MAX_PIPE_SIZE > NARROW_CLIENT_ACK_WINDOW); //ensure wakeup by
> >             ack
> > message
> >  
> > -    if (!spice_timer_queue_create()) {
> > -        spice_error("failed to create timer queue");
> > -    }
> > -
> >      if (pthread_getcpuclockid(pthread_self(), &worker->clockid)) {
> >          spice_warning("getcpuclockid failed");
> >      }
> > @@ -1627,66 +1776,12 @@ SPICE_GNUC_NORETURN static void
> > *red_worker_main(void
> > *arg)
> >      RED_CHANNEL(worker->cursor_channel)->thread_id = pthread_self();
> >      RED_CHANNEL(worker->display_channel)->thread_id = pthread_self();
> >  
> > -    for (;;) {
> > -        int i, num_events;
> > -        unsigned int timeout;
> > -
> > -        timeout = spice_timer_queue_get_timeout_ms();
> > -        worker->event_timeout = MIN(timeout, worker->event_timeout);
> > -        timeout = display_channel_get_streams_timeout(worker
> > ->display_channel);
> > -        worker->event_timeout = MIN(timeout, worker->event_timeout);
> > -        num_events = poll(worker->poll_fds, MAX_EVENT_SOURCES, worker
> > ->event_timeout);
> > -        stream_timeout(worker->display_channel);
> > -        spice_timer_queue_cb();
> > -
> > -        if (worker->display_channel) {
> > -            /* during migration, in the dest, the display channel can be
> > initialized
> > -               while the global lz data not since migrate data msg hasn't
> > been
> > -               received yet */
> > -            display_channel_free_glz_drawables_to_free(worker
> > ->display_channel);
> > -        }
> > -
> > -        worker->event_timeout = INF_EVENT_WAIT;
> > -        if (num_events == -1) {
> > -            if (errno != EINTR) {
> > -                spice_error("poll failed, %s", strerror(errno));
> > -            }
> > -        }
> > -
> > -        for (i = 0; i < MAX_EVENT_SOURCES; i++) {
> > -            /* The watch may have been removed by the watch-func from
> > -               another fd (ie a disconnect through the dispatcher),
> > -               in this case watch_func is NULL. */
> > -            if (worker->poll_fds[i].revents &&
> > worker->watches[i].watch_func)
> > {
> > -                int events = 0;
> > -                if (worker->poll_fds[i].revents & POLLIN) {
> > -                    events |= SPICE_WATCH_EVENT_READ;
> > -                }
> > -                if (worker->poll_fds[i].revents & POLLOUT) {
> > -                    events |= SPICE_WATCH_EVENT_WRITE;
> > -                }
> > -                worker->watches[i].watch_func(worker->poll_fds[i].fd,
> > events,
> > -                                        worker
> > ->watches[i].watch_func_opaque);
> > -            }
> > -        }
> > -
> > -        /* Clear the poll_fd for any removed watches, see the comment in
> > -           watch_remove for why we don't do this there. */
> > -        for (i = 0; i < MAX_EVENT_SOURCES; i++) {
> > -            if (!worker->watches[i].watch_func) {
> > -                worker->poll_fds[i].fd = -1;
> > -            }
> > -        }
> > -
> > -        if (worker->running) {
> > -            int ring_is_empty;
> > -            red_process_cursor(worker, MAX_PIPE_SIZE, &ring_is_empty);
> > -            red_process_display(worker, MAX_PIPE_SIZE, &ring_is_empty);
> > -        }
> > -        red_push(worker);
> > -    }
> > +    GMainLoop *loop = g_main_loop_new(worker->main_context, FALSE);
> > +    g_main_loop_run(loop);
> > +    g_main_loop_unref(loop);
> >  
> > -    spice_warn_if_reached();
> > +    /* FIXME: free worker, and join threads */
> > +    abort();
> 
> why abort?
> 
> >  }
> >  
> >  bool red_worker_run(RedWorker *worker)
> > diff --git a/server/red-worker.h b/server/red-worker.h
> > index 44f35f7..b55a45c 100644
> > --- a/server/red-worker.h
> > +++ b/server/red-worker.h
> > @@ -97,6 +97,7 @@ bool       red_worker_run(RedWorker *worker);
> >  QXLInstance* red_worker_get_qxl(RedWorker *worker);
> >  RedChannel* red_worker_get_cursor_channel(RedWorker *worker);
> >  RedChannel* red_worker_get_display_channel(RedWorker *worker);
> > +GMainContext* red_worker_get_context(RedWorker *worker);
> >  clockid_t red_worker_get_clockid(RedWorker *worker);
> >  RedMemSlotInfo* red_worker_get_memslot(RedWorker *worker);
> >  
> > diff --git a/server/spice_timer_queue.c b/server/spice_timer_queue.c
> > deleted file mode 100644
> > index 60017cc..0000000
> > --- a/server/spice_timer_queue.c
> > +++ /dev/null
> > @@ -1,273 +0,0 @@
> > -/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
> > -/*
> > -   Copyright (C) 2013 Red Hat, Inc.
> > -
> > -   This library is free software; you can redistribute it and/or
> > -   modify it under the terms of the GNU Lesser General Public
> > -   License as published by the Free Software Foundation; either
> > -   version 2.1 of the License, or (at your option) any later version.
> > -
> > -   This library is distributed in the hope that it will be useful,
> > -   but WITHOUT ANY WARRANTY; without even the implied warranty of
> > -   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> > -   Lesser General Public License for more details.
> > -
> > -   You should have received a copy of the GNU Lesser General Public
> > -   License along with this library; if not, see
> > <http://www.gnu.org/licenses/
> > >.
> > -*/
> > -#include <config.h>
> > -#include <pthread.h>
> > -#include "red-common.h"
> > -#include "spice_timer_queue.h"
> > -#include "common/ring.h"
> > -
> > -static Ring timer_queue_list;
> > -static int queue_count = 0;
> > -static pthread_mutex_t queue_list_lock = PTHREAD_MUTEX_INITIALIZER;
> > -
> > -static void spice_timer_queue_init(void)
> > -{
> > -    ring_init(&timer_queue_list);
> > -}
> > -
> > -struct SpiceTimer {
> > -    RingItem link;
> > -    RingItem active_link;
> > -
> > -    SpiceTimerFunc func;
> > -    void *opaque;
> > -
> > -    SpiceTimerQueue *queue;
> > -
> > -    int is_active;
> > -    uint32_t ms;
> > -    uint64_t expiry_time;
> > -};
> > -
> > -struct SpiceTimerQueue {
> > -    RingItem link;
> > -    pthread_t thread;
> > -    Ring timers;
> > -    Ring active_timers;
> > -};
> > -
> > -static SpiceTimerQueue *spice_timer_queue_find(void)
> > -{
> > -    pthread_t self = pthread_self();
> > -    RingItem *queue_item;
> > -
> > -    RING_FOREACH(queue_item, &timer_queue_list) {
> > -         SpiceTimerQueue *queue = SPICE_CONTAINEROF(queue_item,
> > SpiceTimerQueue, link);
> > -
> > -         if (pthread_equal(self, queue->thread) != 0) {
> > -            return queue;
> > -         }
> > -    }
> > -
> > -    return NULL;
> > -}
> > -
> > -static SpiceTimerQueue *spice_timer_queue_find_with_lock(void)
> > -{
> > -    SpiceTimerQueue *queue;
> > -
> > -    pthread_mutex_lock(&queue_list_lock);
> > -    queue = spice_timer_queue_find();
> > -    pthread_mutex_unlock(&queue_list_lock);
> > -    return queue;
> > -}
> > -
> > -int spice_timer_queue_create(void)
> > -{
> > -    SpiceTimerQueue *queue;
> > -
> > -    pthread_mutex_lock(&queue_list_lock);
> > -    if (queue_count == 0) {
> > -        spice_timer_queue_init();
> > -    }
> > -
> > -    if (spice_timer_queue_find() != NULL) {
> > -        spice_printerr("timer queue was already created for the thread");
> > -        return FALSE;
> > -    }
> > -
> > -    queue = spice_new0(SpiceTimerQueue, 1);
> > -    queue->thread = pthread_self();
> > -    ring_init(&queue->timers);
> > -    ring_init(&queue->active_timers);
> > -
> > -    ring_add(&timer_queue_list, &queue->link);
> > -    queue_count++;
> > -
> > -    pthread_mutex_unlock(&queue_list_lock);
> > -
> > -    return TRUE;
> > -}
> > -
> > -void spice_timer_queue_destroy(void)
> > -{
> > -    RingItem *item;
> > -    SpiceTimerQueue *queue;
> > -
> > -    pthread_mutex_lock(&queue_list_lock);
> > -    queue = spice_timer_queue_find();
> > -
> > -    spice_assert(queue != NULL);
> > -
> > -    while ((item = ring_get_head(&queue->timers))) {
> > -        SpiceTimer *timer;
> > -
> > -        timer = SPICE_CONTAINEROF(item, SpiceTimer, link);
> > -        spice_timer_remove(timer);
> > -    }
> > -
> > -    ring_remove(&queue->link);
> > -    free(queue);
> > -    queue_count--;
> > -
> > -    pthread_mutex_unlock(&queue_list_lock);
> > -}
> > -
> > -SpiceTimer *spice_timer_queue_add(SpiceTimerFunc func, void *opaque)
> > -{
> > -    SpiceTimer *timer = spice_new0(SpiceTimer, 1);
> > -    SpiceTimerQueue *queue = spice_timer_queue_find_with_lock();
> > -
> > -    spice_assert(queue != NULL);
> > -
> > -    ring_item_init(&timer->link);
> > -    ring_item_init(&timer->active_link);
> > -
> > -    timer->opaque = opaque;
> > -    timer->func = func;
> > -    timer->queue = queue;
> > -
> > -    ring_add(&queue->timers, &timer->link);
> > -
> > -    return timer;
> > -}
> > -
> > -static void _spice_timer_set(SpiceTimer *timer, uint32_t ms, uint64_t now)
> > -{
> > -    RingItem *next_item;
> > -    SpiceTimerQueue *queue;
> > -
> > -    if (timer->is_active) {
> > -        spice_timer_cancel(timer);
> > -    }
> > -
> > -    queue = timer->queue;
> > -    timer->expiry_time = now + ms;
> > -    timer->ms = ms;
> > -
> > -    RING_FOREACH(next_item, &queue->active_timers) {
> > -        SpiceTimer *next_timer = SPICE_CONTAINEROF(next_item, SpiceTimer,
> > active_link);
> > -
> > -        if (timer->expiry_time <= next_timer->expiry_time) {
> > -            break;
> > -        }
> > -    }
> > -
> > -    if (next_item) {
> > -        ring_add_before(&timer->active_link, next_item);
> > -    } else {
> > -        ring_add_before(&timer->active_link, &queue->active_timers);
> > -    }
> > -    timer->is_active = TRUE;
> > -}
> > -
> > -void spice_timer_set(SpiceTimer *timer, uint32_t ms)
> > -{
> > -    struct timespec now;
> > -
> > -    spice_assert(pthread_equal(timer->queue->thread, pthread_self()) !=
> > 0);
> > -
> > -    clock_gettime(CLOCK_MONOTONIC, &now);
> > -    _spice_timer_set(timer, ms,
> > -                     (uint64_t)now.tv_sec * 1000 + (now.tv_nsec / 1000 /
> > 1000));
> > -}
> > -
> > -void spice_timer_cancel(SpiceTimer *timer)
> > -{
> > -    spice_assert(pthread_equal(timer->queue->thread, pthread_self()) !=
> > 0);
> > -
> > -    if (!ring_item_is_linked(&timer->active_link)) {
> > -        spice_assert(!timer->is_active);
> > -        return;
> > -    }
> > -
> > -    spice_assert(timer->is_active);
> > -    ring_remove(&timer->active_link);
> > -    timer->is_active = FALSE;
> > -}
> > -
> > -void spice_timer_remove(SpiceTimer *timer)
> > -{
> > -    spice_assert(timer->queue);
> > -    spice_assert(ring_item_is_linked(&timer->link));
> > -    spice_assert(pthread_equal(timer->queue->thread, pthread_self()) !=
> > 0);
> > -
> > -    if (timer->is_active) {
> > -        spice_assert(ring_item_is_linked(&timer->active_link));
> > -        ring_remove(&timer->active_link);
> > -    }
> > -    ring_remove(&timer->link);
> > -    free(timer);
> > -}
> > -
> > -unsigned int spice_timer_queue_get_timeout_ms(void)
> > -{
> > -    struct timespec now;
> > -    int64_t now_ms;
> > -    RingItem *head;
> > -    SpiceTimer *head_timer;
> > -    SpiceTimerQueue *queue = spice_timer_queue_find_with_lock();
> > -
> > -    spice_assert(queue != NULL);
> > -
> > -    if (ring_is_empty(&queue->active_timers)) {
> > -        return -1;
> > -    }
> > -
> > -    head = ring_get_head(&queue->active_timers);
> > -    head_timer = SPICE_CONTAINEROF(head, SpiceTimer, active_link);
> > -
> > -    clock_gettime(CLOCK_MONOTONIC, &now);
> > -    now_ms = ((int64_t)now.tv_sec * 1000) + (now.tv_nsec / 1000 / 1000);
> > -
> > -    return MAX(0, ((int64_t)head_timer->expiry_time - now_ms));
> > -}
> > -
> > -
> > -void spice_timer_queue_cb(void)
> > -{
> > -    struct timespec now;
> > -    uint64_t now_ms;
> > -    RingItem *head;
> > -    SpiceTimerQueue *queue = spice_timer_queue_find_with_lock();
> > -
> > -    spice_assert(queue != NULL);
> > -
> > -    if (ring_is_empty(&queue->active_timers)) {
> > -        return;
> > -    }
> > -
> > -    clock_gettime(CLOCK_MONOTONIC, &now);
> > -    now_ms = ((uint64_t)now.tv_sec * 1000) + (now.tv_nsec / 1000 / 1000);
> > -
> > -    while ((head = ring_get_head(&queue->active_timers))) {
> > -        SpiceTimer *timer = SPICE_CONTAINEROF(head, SpiceTimer,
> > active_link);
> > -
> > -        if (timer->expiry_time > now_ms) {
> > -            break;
> > -        } else {
> > -            /* Remove active timer before calling the timer function.
> > -             * Timer function could delete the timer making the timer
> > -             * pointer point to freed data.
> > -             */
> > -            spice_timer_cancel(timer);
> > -            timer->func(timer->opaque);
> > -            /* timer could now be invalid ! */
> > -        }
> > -    }
> > -}
> > diff --git a/server/spice_timer_queue.h b/server/spice_timer_queue.h
> > deleted file mode 100644
> > index a84f6cd..0000000
> > --- a/server/spice_timer_queue.h
> > +++ /dev/null
> > @@ -1,43 +0,0 @@
> > -/*
> > -   Copyright (C) 2013 Red Hat, Inc.
> > -
> > -   This library is free software; you can redistribute it and/or
> > -   modify it under the terms of the GNU Lesser General Public
> > -   License as published by the Free Software Foundation; either
> > -   version 2.1 of the License, or (at your option) any later version.
> > -
> > -   This library is distributed in the hope that it will be useful,
> > -   but WITHOUT ANY WARRANTY; without even the implied warranty of
> > -   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> > -   Lesser General Public License for more details.
> > -
> > -   You should have received a copy of the GNU Lesser General Public
> > -   License along with this library; if not, see
> > <http://www.gnu.org/licenses/
> > >.
> > -*/
> > -
> > -#ifndef _H_SPICE_TIMER_QUEUE
> > -#define _H_SPICE_TIMER_QUEUE
> > -
> > -#include  <stdint.h>
> > -#include "spice.h"
> > -
> > -typedef struct SpiceTimerQueue SpiceTimerQueue;
> > -
> > -/* create/destroy a timer queue for the current thread.
> > - * In order to execute the timers functions, spice_timer_queue_cb should
> > be
> > called
> > - * periodically, according to spice_timer_queue_get_timeout_ms */
> > -int spice_timer_queue_create(void);
> > -void spice_timer_queue_destroy(void);
> > -
> > -SpiceTimer *spice_timer_queue_add(SpiceTimerFunc func, void *opaque);
> > -void spice_timer_set(SpiceTimer *timer, uint32_t ms);
> > -void spice_timer_cancel(SpiceTimer *timer);
> > -void spice_timer_remove(SpiceTimer *timer);
> > -
> > -/* returns the time left till the earliest timer in the queue expires.
> > - * returns (unsigned)-1 if there are no active timers */
> > -unsigned int spice_timer_queue_get_timeout_ms(void);
> > -/* call the timeout callbacks of all the expired timers */
> > -void spice_timer_queue_cb(void);
> > -
> > -#endif
> 
> 
> Reviewed-by: Jonathon Jongsma <jjongsma at redhat.com>
> _______________________________________________
> 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