[Spice-devel] [PATCH 8/9] worker: use glib main loop

Frediano Ziglio fziglio at redhat.com
Wed Oct 21 05:00:37 PDT 2015


From: Marc-André Lureau <marcandre.lureau at gmail.com>

Clean up, more extensible.

Avoid server hanging when no client are connected.
---
 server/Makefile.am         |   2 -
 server/red_worker.c        | 438 +++++++++++++++++++++++++--------------------
 server/spice_timer_queue.c | 273 ----------------------------
 server/spice_timer_queue.h |  43 -----
 4 files changed, 240 insertions(+), 516 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 cd4686a..67e5d65 100644
--- a/server/Makefile.am
+++ b/server/Makefile.am
@@ -117,8 +117,6 @@ libspice_server_la_SOURCES =			\
 	snd_worker.h				\
 	stat.h					\
 	spicevmc.c				\
-	spice_timer_queue.c			\
-	spice_timer_queue.h			\
 	zlib_encoder.c				\
 	zlib_encoder.h				\
 	spice_bitmap_utils.h		\
diff --git a/server/red_worker.c b/server/red_worker.c
index 7f73409..6c68819 100644
--- a/server/red_worker.c
+++ b/server/red_worker.c
@@ -86,7 +86,6 @@
 #include "dispatcher.h"
 #include "main_channel.h"
 #include "migration_protocol.h"
-#include "spice_timer_queue.h"
 #include "main_dispatcher.h"
 #include "spice_server_utils.h"
 #include "red_time.h"
@@ -259,13 +258,6 @@ static inline double stat_byte_to_mega(uint64_t size)
 #endif
 
 #define MAX_EVENT_SOURCES 20
-#define INF_EVENT_WAIT ~0
-
-struct SpiceWatch {
-    struct RedWorker *worker;
-    SpiceWatchFunc watch_func;
-    void *watch_func_opaque;
-};
 
 enum {
     BUF_TYPE_RAW = 1,
@@ -861,6 +853,7 @@ typedef struct ItemTrace {
 typedef struct RedWorker {
     pthread_t thread;
     clockid_t clockid;
+    GMainContext *main_context;
     DisplayChannel *display_channel;
     CursorChannel *cursor_channel;
     QXLInstance *qxl;
@@ -869,9 +862,6 @@ typedef struct RedWorker {
     int channel;
     int id;
     int running;
-    struct pollfd poll_fds[MAX_EVENT_SOURCES];
-    struct SpiceWatch watches[MAX_EVENT_SOURCES];
-    unsigned int event_timeout;
 
     gint timeout;
     uint32_t repoll_cmd_ring;
@@ -2711,48 +2701,6 @@ static void red_streams_update_visible_region(RedWorker *worker, Drawable *drawa
     }
 }
 
-static inline unsigned int red_get_streams_timout(RedWorker *worker)
-{
-    unsigned int timout = -1;
-    Ring *ring = &worker->streams;
-    RingItem *item = ring;
-    struct timespec time;
-
-    clock_gettime(CLOCK_MONOTONIC, &time);
-    red_time_t now = timespec_to_red_time(&time);
-    while ((item = ring_next(ring, item))) {
-        Stream *stream;
-
-        stream = SPICE_CONTAINEROF(item, Stream, link);
-        red_time_t delta = (stream->last_time + RED_STREAM_TIMOUT) - now;
-
-        if (delta < 1000 * 1000) {
-            return 0;
-        }
-        timout = MIN(timout, (unsigned int)(delta / (1000 * 1000)));
-    }
-    return timout;
-}
-
-static inline void red_handle_streams_timout(RedWorker *worker)
-{
-    Ring *ring = &worker->streams;
-    struct timespec time;
-    RingItem *item;
-
-    clock_gettime(CLOCK_MONOTONIC, &time);
-    red_time_t now = timespec_to_red_time(&time);
-    item = ring_get_head(ring);
-    while (item) {
-        Stream *stream = SPICE_CONTAINEROF(item, Stream, link);
-        item = ring_next(ring, item);
-        if (now >= (stream->last_time + RED_STREAM_TIMOUT)) {
-            red_detach_stream_gracefully(worker, stream, NULL);
-            red_stop_stream(worker, stream);
-        }
-    }
-}
-
 static void red_display_release_stream(RedWorker *worker, StreamAgent *agent)
 {
     spice_assert(agent->stream);
@@ -4665,7 +4613,9 @@ static int red_process_cursor(RedWorker *worker, uint32_t max_pipe_size, int *ri
             *ring_is_empty = TRUE;
             if (worker->repoll_cursor_ring < CMD_RING_POLL_RETRIES) {
                 worker->repoll_cursor_ring++;
-                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->repoll_cursor_ring > CMD_RING_POLL_RETRIES ||
@@ -4711,7 +4661,6 @@ static int red_process_commands(RedWorker *worker, uint32_t max_pipe_size, int *
 {
     QXLCommandExt ext_cmd;
     int n = 0;
-    uint64_t start = red_now();
 
     if (!worker->running) {
         *ring_is_empty = TRUE;
@@ -4720,14 +4669,30 @@ static int red_process_commands(RedWorker *worker, uint32_t max_pipe_size, int *
 
     worker->process_commands_generation++;
     *ring_is_empty = FALSE;
-    while (!display_is_connected(worker) ||
-           // TODO: change to average pipe size?
-           red_channel_min_pipe_size(&worker->display_channel->common.base) <= max_pipe_size) {
+    for (;;) {
+
+        if (display_is_connected(worker)) {
+
+            if (red_channel_all_blocked(&worker->display_channel->common.base)) {
+                spice_info("all display clients are blocking");
+                return n;
+            }
+
+
+            // TODO: change to average pipe size?
+            if (red_channel_min_pipe_size(&worker->display_channel->common.base) > max_pipe_size) {
+                spice_info("too much item in the display clients pipe already");
+                return n;
+            }
+        }
+
         if (!worker->qxl->st->qif->get_command(worker->qxl, &ext_cmd)) {
             *ring_is_empty = TRUE;;
             if (worker->repoll_cmd_ring < CMD_RING_POLL_RETRIES) {
                 worker->repoll_cmd_ring++;
-                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->repoll_cmd_ring > CMD_RING_POLL_RETRIES ||
@@ -4809,13 +4774,8 @@ static int red_process_commands(RedWorker *worker, uint32_t max_pipe_size, int *
             spice_error("bad command type");
         }
         n++;
-        if ((worker->display_channel &&
-             red_channel_all_blocked(&worker->display_channel->common.base))
-            || red_now() - start > 10 * 1000 * 1000) {
-            worker->event_timeout = 0;
-            return n;
-        }
     }
+
     return n;
 }
 
@@ -10103,81 +10063,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;
 
-    if (!watch) {
+    timer->source_id = 0;
+    timer->func(timer->opaque);
+    /* timer might be free after func(), don't touch */
+
+    return FALSE;
+}
+
+static void worker_timer_cancel(SpiceTimer *timer)
+{
+    if (timer->source_id == 0)
         return;
-    }
 
-    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 */
     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,
@@ -11846,19 +11884,85 @@ 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);
     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;
+
+    return FALSE; /* do no timeout poll */
+}
+
+static gboolean worker_source_check(GSource *source)
+{
+    RedWorkerSource *wsource = (RedWorkerSource *)source;
+    RedWorker *worker = wsource->worker;
+
+    return worker->running /* TODO && worker->pending_process */;
+}
+
+static void red_display_cc_free_glz_drawables(RedChannelClient *rcc)
+{
+    DisplayChannelClient *dcc = RCC_TO_DCC(rcc);
+
+    red_display_handle_glz_drawables_to_free(dcc);
 }
 
+static gboolean worker_source_dispatch(GSource *source, GSourceFunc callback,
+                                       gpointer user_data)
+{
+    RedWorkerSource *wsource = (RedWorkerSource *)source;
+    RedWorker *worker = wsource->worker;
+    int ring_is_empty;
+
+    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 */
+        /* FIXME: why is this here, and not in display_channel_create */
+        red_channel_apply_clients(&worker->display_channel->common.base,
+                                  red_display_cc_free_glz_drawables);
+    }
+
+    worker->timeout = -1;
+    red_process_cursor(worker, MAX_PIPE_SIZE, &ring_is_empty);
+    red_process_commands(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;
 
     spice_assert(sizeof(CursorItem) <= QXL_CURSUR_DEVICE_DATA_SIZE);
@@ -11866,6 +11970,7 @@ RedWorker* red_worker_new(QXLInstance *qxl, RedDispatcher *red_dispatcher)
     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) {
@@ -11917,15 +12022,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 = worker->channel;
-    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);
 
     red_memslot_info_init(&worker->mem_slots,
                           init_info.num_memslots_groups,
@@ -11937,10 +12045,6 @@ RedWorker* red_worker_new(QXLInstance *qxl, RedDispatcher *red_dispatcher)
     spice_warn_if(init_info.n_surfaces > NUM_SURFACES);
     worker->n_surfaces = init_info.n_surfaces;
 
-    if (!spice_timer_queue_create()) {
-        spice_error("failed to create timer queue");
-    }
-
     red_init_quic(worker);
     red_init_lz(worker);
     red_init_jpeg(worker);
@@ -11948,18 +12052,11 @@ RedWorker* red_worker_new(QXLInstance *qxl, RedDispatcher *red_dispatcher)
     red_init_lz4(worker);
 #endif
     red_init_zlib(worker);
-    worker->event_timeout = INF_EVENT_WAIT;
+    worker->timeout = -1;
 
     return worker;
 }
 
-static void red_display_cc_free_glz_drawables(RedChannelClient *rcc)
-{
-    DisplayChannelClient *dcc = RCC_TO_DCC(rcc);
-
-    red_display_handle_glz_drawables_to_free(dcc);
-}
-
 SPICE_GNUC_NORETURN static void *red_worker_main(void *arg)
 {
     RedWorker *worker = arg;
@@ -11972,67 +12069,12 @@ SPICE_GNUC_NORETURN static void *red_worker_main(void *arg)
         spice_warning("getcpuclockid failed");
     }
 
-    for (;;) {
-        int i, num_events;
-        unsigned int timeout;
-
-        timeout = spice_timer_queue_get_timeout_ms();
-        worker->event_timeout = MIN(timeout, worker->event_timeout);
-        timeout = red_get_streams_timout(worker);
-        worker->event_timeout = MIN(timeout, worker->event_timeout);
-        num_events = poll(worker->poll_fds, MAX_EVENT_SOURCES, worker->event_timeout);
-        red_handle_streams_timout(worker);
-        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 */
-            red_channel_apply_clients(&worker->display_channel->common.base,
-                                      red_display_cc_free_glz_drawables);
-        }
-
-        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_commands(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();
 }
 
 bool red_worker_run(RedWorker *worker)
diff --git a/server/spice_timer_queue.c b/server/spice_timer_queue.c
deleted file mode 100644
index c4f2f6e..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
-- 
2.4.3



More information about the Spice-devel mailing list