7 changed files with 150 additions and 140 deletions
@ -0,0 +1,43 @@
|
||||
#ifndef _LIGHTTPD_WAITQUEUE_H_ |
||||
#define _LIGHTTPD_WAITQUEUE_H_ |
||||
|
||||
#ifndef _LIGHTTPD_BASE_H_ |
||||
#error Please include <lighttpd/base.h> instead of this file |
||||
#endif |
||||
|
||||
struct waitqueue_elem { |
||||
gboolean queued; |
||||
ev_tstamp ts; |
||||
waitqueue_elem *prev; |
||||
waitqueue_elem *next; |
||||
gpointer data; |
||||
}; |
||||
|
||||
struct waitqueue { |
||||
waitqueue_elem *head; |
||||
waitqueue_elem *tail; |
||||
ev_timer timer; |
||||
struct ev_loop *loop; |
||||
gdouble delay; |
||||
}; |
||||
|
||||
/*
|
||||
* waitqueues are queues used to implement delays for certain tasks in a lightweight, non-blocking way |
||||
* they are used for io timeouts or throttling for example |
||||
* waitqueue_push, waitqueue_pop and waitqueue_remove have O(1) complexity |
||||
*/ |
||||
|
||||
/* initializes a waitqueue by creating and starting the ev_timer. precision is sub-seconds */ |
||||
LI_API void waitqueue_init(waitqueue *queue, struct ev_loop *loop, waitqueue_cb callback, gdouble delay, gpointer data); |
||||
/* stops the waitqueue. to restart it, simply call waitqueue_update */ |
||||
LI_API void waitqueue_stop(waitqueue *queue); |
||||
/* updates the timeout of the waitqueue, you should allways call this at the end of your callback */ |
||||
LI_API void waitqueue_update(waitqueue *queue); |
||||
/* moves the element to the end of the queue if already queued, appends it to the end otherwise */ |
||||
LI_API void waitqueue_push(waitqueue *queue, waitqueue_elem *elem); |
||||
/* pops the first ready! element from the queue or NULL if none ready yet. this should be called in your callback */ |
||||
LI_API waitqueue_elem *waitqueue_pop(waitqueue *queue); |
||||
/* removes an element from the queue */ |
||||
LI_API void waitqueue_remove(waitqueue *queue, waitqueue_elem *elem); |
||||
|
||||
#endif |
@ -0,0 +1,103 @@
|
||||
|
||||
#include <lighttpd/base.h> |
||||
|
||||
void waitqueue_init(waitqueue *queue, struct ev_loop *loop, waitqueue_cb callback, gdouble delay, gpointer data) { |
||||
ev_timer_init(&queue->timer, callback, delay, delay); |
||||
ev_timer_start(loop, &queue->timer); |
||||
|
||||
queue->timer.data = data; |
||||
queue->head = queue->tail = NULL; |
||||
queue->loop = loop; |
||||
queue->delay = delay; |
||||
} |
||||
|
||||
void waitqueue_stop(waitqueue *queue) { |
||||
ev_timer_stop(queue->loop, &queue->timer); |
||||
} |
||||
|
||||
void waitqueue_update(waitqueue *queue) { |
||||
ev_tstamp repeat; |
||||
|
||||
if (queue->head) { |
||||
repeat = queue->head->ts + queue->delay - ev_now(queue->loop); |
||||
} else |
||||
repeat = queue->delay; |
||||
|
||||
if (queue->timer.repeat != repeat) |
||||
{ |
||||
queue->timer.repeat = repeat; |
||||
ev_timer_again(queue->loop, &queue->timer); |
||||
} |
||||
} |
||||
|
||||
void waitqueue_push(waitqueue *queue, waitqueue_elem *elem) { |
||||
elem->ts = ev_now(queue->loop); |
||||
|
||||
if (!elem->queued) { |
||||
elem->queued = TRUE; |
||||
/* not in the queue yet, insert at the end */ |
||||
if (!queue->head) { |
||||
/* queue is empty */ |
||||
queue->head = elem; |
||||
queue->tail = elem; |
||||
elem->prev = NULL; |
||||
elem->next = NULL; |
||||
} else { |
||||
/* queue not empty */ |
||||
elem->prev = queue->tail; |
||||
elem->next = NULL; |
||||
queue->tail->next = elem; |
||||
queue->tail = elem; |
||||
} |
||||
} else { |
||||
/* already queued, move to end */ |
||||
if (elem == queue->tail) |
||||
return; |
||||
|
||||
if (elem == queue->head) { |
||||
queue->head = elem->next; |
||||
if (elem->next) |
||||
elem->next->prev = NULL; |
||||
} |
||||
|
||||
elem->prev = queue->tail; |
||||
elem->next = NULL; |
||||
queue->tail->next = elem; |
||||
queue->tail = elem; |
||||
} |
||||
} |
||||
|
||||
waitqueue_elem *waitqueue_pop(waitqueue *queue) { |
||||
waitqueue_elem *elem = queue->head; |
||||
ev_tstamp now = ev_now(queue->loop); |
||||
|
||||
if (!elem || (elem->ts + queue->delay) >= now) { |
||||
return NULL; |
||||
} |
||||
|
||||
if (elem != queue->tail) |
||||
elem->next->prev = NULL; |
||||
|
||||
queue->head = elem->next; |
||||
|
||||
elem->queued = FALSE; |
||||
|
||||
return elem; |
||||
} |
||||
|
||||
void waitqueue_remove(waitqueue *queue, waitqueue_elem *elem) { |
||||
if (!elem->queued) |
||||
return; |
||||
|
||||
if (elem == queue->head) |
||||
queue->head = elem->next; |
||||
else |
||||
elem->prev->next = elem->next; |
||||
|
||||
if (elem == queue->tail) |
||||
queue->tail = elem->prev; |
||||
else |
||||
elem->next->prev = elem->prev; |
||||
|
||||
elem->queued = FALSE; |
||||
} |
Loading…
Reference in new issue