On Tue, Feb 26, 2013 at 01:03:57PM -0500, Yonit Halperin wrote: > Each thread can create a spice_timer_queue, for managing its > own timers. ACK. nitpick: why ms and not nano? > --- > server/Makefile.am | 2 + > server/spice_timer_queue.c | 268 +++++++++++++++++++++++++++++++++++++++++++++ > server/spice_timer_queue.h | 43 ++++++++ > 3 files changed, 313 insertions(+) > create mode 100644 server/spice_timer_queue.c > create mode 100644 server/spice_timer_queue.h > > diff --git a/server/Makefile.am b/server/Makefile.am > index 8b380fc..7a52b17 100644 > --- a/server/Makefile.am > +++ b/server/Makefile.am > @@ -93,6 +93,8 @@ libspice_server_la_SOURCES = \ > spice.h \ > stat.h \ > spicevmc.c \ > + spice_timer_queue.c \ > + spice_timer_queue.h \ > zlib_encoder.c \ > zlib_encoder.h \ > $(NULL) > diff --git a/server/spice_timer_queue.c b/server/spice_timer_queue.c > new file mode 100644 > index 0000000..690ab83 > --- /dev/null > +++ b/server/spice_timer_queue.c > @@ -0,0 +1,268 @@ > +/* -*- 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 <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, uint32_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, 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; > + int 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 = (now.tv_sec * 1000) - (now.tv_nsec / 1000 / 1000); > + > + return MAX(0, ((int)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 = (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 { > + timer->func(timer->opaque); > + if (timer->is_active) { > + _spice_timer_set(timer, timer->ms, now_ms); > + } > + } > + } > +} > diff --git a/server/spice_timer_queue.h b/server/spice_timer_queue.h > new file mode 100644 > index 0000000..a84f6cd > --- /dev/null > +++ b/server/spice_timer_queue.h > @@ -0,0 +1,43 @@ > +/* > + 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 > -- > 1.8.1 > > _______________________________________________ > Spice-devel mailing list > Spice-devel@xxxxxxxxxxxxxxxxxxxxx > http://lists.freedesktop.org/mailman/listinfo/spice-devel _______________________________________________ Spice-devel mailing list Spice-devel@xxxxxxxxxxxxxxxxxxxxx http://lists.freedesktop.org/mailman/listinfo/spice-devel