[Spice-devel] [PATCH spice 05/11] server: spice_timer_queue

Christophe Fergeau cfergeau at redhat.com
Tue Apr 10 04:55:18 PDT 2012


On Sun, Apr 08, 2012 at 06:43:14PM +0300, Yonit Halperin wrote:
> Each thread can create a spice_timer_queue, for managing its
> own timers.
> 
> Signed-off-by: Yonit Halperin <yhalperi at redhat.com>
> ---
>  server/Makefile.am         |    2 +
>  server/spice_timer_queue.c |  245 ++++++++++++++++++++++++++++++++++++++++++++
>  server/spice_timer_queue.h |   41 ++++++++
>  3 files changed, 288 insertions(+), 0 deletions(-)
>  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 97e7dfe..d7d769f 100644
> --- a/server/Makefile.am
> +++ b/server/Makefile.am
> @@ -87,6 +87,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..9748186
> --- /dev/null
> +++ b/server/spice_timer_queue.c
> @@ -0,0 +1,245 @@
> +/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
> +/*
> +   Copyright (C) 2009 Red Hat, Inc.

2012 ?

> +
> +   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"
> +
> +Ring timer_queue_list;
> +static int queue_count = 0;
> +
> +void spice_timer_queue_init()
> +{
> +    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()
> +{
> +    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;
> +}

This function isn't thread safe as far as I can tell (timer_queue_list use
isn't protected), while it will accessed from different thread. The whole
file would need auditing to make sure we're not accessing this structure
from different threads. thread local storage may come in handy here, but
I'm not sure we have support for this yet in spice though... It would be
much easier if we reused an existing mainloop which would give us support
for this for free :)

Christophe

> +
> +int spice_timer_queue_create()
> +{
> +    SpiceTimerQueue *queue;
> +
> +    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++;
> +    return TRUE;
> +}
> +
> +void spice_timer_queue_destroy()
> +{
> +    RingItem *item;
> +    SpiceTimerQueue *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--;
> +}
> +
> +SpiceTimer *spice_timer_queue_add(SpiceTimerFunc func, void *opaque)
> +{
> +    SpiceTimer *timer = spice_new0(SpiceTimer, 1);
> +    SpiceTimerQueue *queue = spice_timer_queue_find();
> +
> +    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_tail(&queue->active_timers, &timer->active_link);
> +    }
> +    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);
> +}
> +
> +int spice_timer_queue_get_timeout_ms()
> +{
> +    struct timespec now;
> +    int now_ms;
> +    RingItem *head;
> +    SpiceTimer *head_timer;
> +    SpiceTimerQueue *queue = spice_timer_queue_find();
> +
> +    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, (head_timer->expiry_time - now_ms));
> +}
> +
> +
> +void spice_timer_queue_cb()
> +{
> +    struct timespec now;
> +    int now_ms;
> +    RingItem *cur, *next;
> +    SpiceTimerQueue *queue = spice_timer_queue_find();
> +
> +    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);
> +
> +    RING_FOREACH_SAFE(cur, next, &queue->active_timers) {
> +        SpiceTimer *timer = SPICE_CONTAINEROF(cur, SpiceTimer, active_link);
> +
> +        if (timer->expiry_time > now_ms) {
> +            return;
> +        } else {
> +            timer->func(timer->opaque);
> +            _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..bcd5796
> --- /dev/null
> +++ b/server/spice_timer_queue.h
> @@ -0,0 +1,41 @@
> +/*
> +   Copyright (C) 2009 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;
> +
> +/* creates timers queue for the current thread */
> +int spice_timer_queue_create();
> +void spice_timer_queue_destroy(); // destroy the queue of the current thread
> +
> +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.
> + * return -1 if there are no active timers */
> +int spice_timer_queue_get_timeout_ms();
> +/* calls the timeout callbacks of all the expired timers */
> +void spice_timer_queue_cb();
> +
> +#endif
> -- 
> 1.7.7.6
> 
> _______________________________________________
> Spice-devel mailing list
> Spice-devel at lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/spice-devel
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 198 bytes
Desc: not available
URL: <http://lists.freedesktop.org/archives/spice-devel/attachments/20120410/68e56f07/attachment-0001.pgp>


More information about the Spice-devel mailing list