[Spice-devel] [PATCH 17/22] worker: use glib main loop
Frediano Ziglio
fziglio at redhat.com
Wed Dec 2 08:20:00 PST 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 | 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 1e40610..0e2da95 100644
--- a/server/Makefile.am
+++ b/server/Makefile.am
@@ -119,8 +119,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 9ba57e4..e31b872 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)
+{
+ 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;
+ }
+
+
+ // 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");
+ 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);
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) {
- 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;
- }
- 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,
@@ -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);
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 */
+}
+
+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);
+
+ /* 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();
}
bool red_worker_run(RedWorker *worker)
diff --git a/server/red_worker.h b/server/red_worker.h
index 57e3c0f..bfbc616 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 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