> > From: Marc-André Lureau <marcandre.lureau@xxxxxxxxx> > > Clean up, more extensible. > > Avoid server hanging when no client are connected. > --- > server/Makefile.am | 2 - > server/red_worker.c | 439 > +++++++++++++++++++++++++-------------------- > server/spice_timer_queue.c | 273 ---------------------------- > server/spice_timer_queue.h | 43 ----- > 4 files changed, 242 insertions(+), 515 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 fad1cbc..78ccac9 100644 > --- a/server/Makefile.am > +++ b/server/Makefile.am > @@ -119,8 +119,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 c96c60a..6cc5a35 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" > @@ -270,13 +269,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, > @@ -917,6 +909,7 @@ typedef struct ItemTrace { > #define NUM_CURSORS 100 > > typedef struct RedWorker { > + GMainContext *main_context; > DisplayChannel *display_channel; > CursorChannel *cursor_channel; > QXLInstance *qxl; > @@ -926,9 +919,7 @@ typedef struct RedWorker { > int id; > int running; > uint32_t *pending; > - struct pollfd poll_fds[MAX_EVENT_SOURCES]; > - struct SpiceWatch watches[MAX_EVENT_SOURCES]; > - unsigned int event_timeout; > + gint timeout; > uint32_t repoll_cmd_ring; > uint32_t repoll_cursor_ring; > uint32_t num_renderers; > @@ -2766,48 +2757,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); > @@ -4722,7 +4671,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 || Perhaps could be better to initialize the timeout to a dummy huge (like INT_MAX) value instead of adding the check here. This would remove this change. > @@ -4768,7 +4719,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; > @@ -4777,14 +4727,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; > + } > + } > + Does this change is necessary just for the main loop? > 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); See above > return n; > } > if (worker->repoll_cmd_ring > CMD_RING_POLL_RETRIES || > @@ -4866,13 +4832,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; > } > > @@ -10088,81 +10049,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, > @@ -11831,23 +11870,91 @@ 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); Why this debug ? > 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 */; > +} > + Here is slightly different, in the old code the dispatch part was always executed no matter is running or not. So could be this function should return TRUE always. > +static void red_display_cc_free_glz_drawables(RedChannelClient *rcc) > +{ > + DisplayChannelClient *dcc = RCC_TO_DCC(rcc); > + > + red_display_handle_glz_drawables_to_free(dcc); > } > Why moving this function? A declaration was not enough? > +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); > + } > + This part was always executed, now only if running is true. > + worker->timeout = -1; > + red_process_cursor(worker, MAX_PIPE_SIZE, &ring_is_empty); > + red_process_commands(worker, MAX_PIPE_SIZE, &ring_is_empty); > + These two lines were executed only if running > + /* FIXME: remove me? that should be handled by watch out condition */ > + red_push(worker); > + I don't understand the FIXME. > + return TRUE; > +} > + > +/* cannot be const */ > +static GSourceFuncs worker_source_funcs = { > + .prepare = worker_source_prepare, > + .check = worker_source_check, > + .dispatch = worker_source_dispatch, > +}; > + > static RedWorker* red_worker_new(WorkerInitData *init_data) > { > RedWorker *worker = spice_new0(RedWorker, 1); > RedWorkerMessage message; > Dispatcher *dispatcher; > - int i; > const char *record_filename; > > spice_assert(sizeof(CursorItem) <= QXL_CURSUR_DEVICE_DATA_SIZE); > > + worker->main_context = g_main_context_new(); > + > record_filename = getenv("SPICE_WORKER_RECORD_FILENAME"); > if (record_filename) { > static const char header[] = "SPICE_REPLAY 1\n"; > @@ -11898,15 +12005,18 @@ static RedWorker* red_worker_new(WorkerInitData > *init_data) > 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); Cast here should not be needed, function should be declared with correct types. Style: declaration inside code. > + 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; usually we don't use declarations inside code, could be replaced with ((RedWorkerSource *) source)->worker = worker; > + g_source_attach(source, worker->main_context); > + g_source_unref(source); > > red_memslot_info_init(&worker->mem_slots, > init_data->num_memslots_groups, > @@ -11918,9 +12028,6 @@ static RedWorker* red_worker_new(WorkerInitData > *init_data) > spice_warn_if(init_data->n_surfaces > NUM_SURFACES); > worker->n_surfaces = init_data->n_surfaces; > > - if (!spice_timer_queue_create()) { > - spice_error("failed to create timer queue"); > - } > message = RED_WORKER_MESSAGE_READY; > write_message(worker->channel, &message); > > @@ -11931,18 +12038,11 @@ static RedWorker* red_worker_new(WorkerInitData > *init_data) > red_init_lz4(worker); > #endif > red_init_zlib(worker); > - worker->event_timeout = INF_EVENT_WAIT; > + worker->timeout = -1; Use "infinite" value and check on callback? See comment above. > > 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); > -} > - Just required as function was moved. > SPICE_GNUC_NORETURN void *red_worker_main(void *arg) > { > RedWorker *worker = red_worker_new(arg); > @@ -11955,65 +12055,10 @@ SPICE_GNUC_NORETURN void *red_worker_main(void > *arg) > spice_error("pthread_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(); why replacing a spice_warn_if_reached with an abort? > } > 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 Frediano _______________________________________________ Spice-devel mailing list Spice-devel@xxxxxxxxxxxxxxxxxxxxx http://lists.freedesktop.org/mailman/listinfo/spice-devel