mirror of
https://gitlab.uni-freiburg.de/opensourcevdi/spice
synced 2025-12-26 14:41:25 +00:00
server: spice_timer_queue
Each thread can create a spice_timer_queue, for managing its own timers.
This commit is contained in:
parent
622d7159c2
commit
e3bc219570
@ -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)
|
||||
|
||||
268
server/spice_timer_queue.c
Normal file
268
server/spice_timer_queue.c
Normal file
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
43
server/spice_timer_queue.h
Normal file
43
server/spice_timer_queue.h
Normal file
@ -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
|
||||
Loading…
Reference in New Issue
Block a user