[Spice-devel] [PATCH spice-server 11/28] server: spice_timer_queue
Yonit Halperin
yhalperi at redhat.com
Tue Feb 26 10:03:57 PST 2013
Each thread can create a spice_timer_queue, for managing its
own timers.
---
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
More information about the Spice-devel
mailing list