src/irc_queue.h
author Tero Marttila <terom@fixme.fi>
Wed, 27 May 2009 23:57:48 +0300
branchnew-lib-errors
changeset 217 7728d6ec3abf
parent 155 c59d3eaff0fb
child 219 cefec18b8268
permissions -rw-r--r--
nexus.c compiles
#ifndef IRC_QUEUE_H
#define IRC_QUEUE_H

/**
 * @file
 *
 * Ratelimited queue of outgoing irc_line's for use with irc_conn.
 *
 * This implements the basic flood control algorithm as described in RFC1459 section 8.10.
 */
#include "irc_line.h"
#include "line_proto.h"
#include "error.h"

#include <sys/queue.h>
#include <event2/event.h>

/**
 * Number of seconds of penalty applied for each message
 */
#define IRC_QUEUE_PENALTY 2

/**
 * Maximum allowed burst, in seconds
 */
#define IRC_QUEUE_WINDOW 10

/**
 * An enqueued irc_line for later delivery
 */
struct irc_queue_entry {
    /** The formatted irc_line data, including terminating CRLF */
    char line_buf[IRC_LINE_MAX + 2];

    /** Our entry in the irc_queue list */
    TAILQ_ENTRY(irc_queue_entry) queue_list;
};

/**
 * The queue state and timing algorithm, as described in the RFC:
 *  * A line will be sent directly if the current timer is less than IRC_QUEUE_WINDOW seconds into the future, and
 *    IRC_QUEUE_PENALTY seconds are added on to the timer.
 *  * Otherwise, the line is stored as a irc_queue_entry and enqueued for later transmission, once the timer value
 *    drops below IRC_QUEUE_WINDOW seconds into the future.
 *  * The above is repeated for each dequeued line.
 *
 * Additionally, if sending a line fails due to the line_proto socket buffer being full, this also handles this case
 * by queueing the line for later sending.
 *
 * Note that the timing behaviour of this is rather unexact - the socket layer may introduce its own delays/jitter which
 * we can't measure or control here, but at least we make an effort. This should work OK as long as the outgoing lines
 * don't accumulate in the socket's write buffer too much. XXX: maybe tweak socket params to try and "disable" 
 * buffering on our end to improve accuracy?
 */
struct irc_queue {
    /** The line_proto that we can send the messages out on */
    struct line_proto *lp;

    /** Our timeout used for delaying messages */
    struct event *ev;

    /** Current "message timer" value, may be up to IRC_QUEUE_WINDOW seconds in the future */
    time_t timer;

    /** The actual queue of messages */
    TAILQ_HEAD(irc_queue_entry_list, irc_queue_entry) list;
};

/**
 * Create a new irc_queue for use with the given line_proto
 */
err_t irc_queue_create (struct irc_queue **queue_ptr, struct event_base *ev_base, struct line_proto *lp, error_t *err);

/**
 * Process a line, either sending it directly, or enqueueing it, based on the timer state.
 */
err_t irc_queue_process (struct irc_queue *queue, const struct irc_line *line);

/**
 * Destroy the irc_queue, releasing all queued lines
 */
void irc_queue_destroy (struct irc_queue *queue);

#endif /* IRC_QUEUE_H */