From 2ecabb05dbeb0948e514cdad1bb811d9edf97e2e Mon Sep 17 00:00:00 2001 From: Andre Noll Date: Sun, 12 Aug 2007 19:44:33 +0200 Subject: [PATCH] Move chunk_queue stuff to own files chunk_queue.[ch]. This completes the isolation of the chunk queueing code. In this form it may be used also by other senders. --- chunk_queue.c | 171 ++++++++++++++++++++++++++++++++++++++++++++++++++ chunk_queue.h | 18 ++++++ configure.ac | 2 +- error.h | 8 ++- http_send.c | 114 +-------------------------------- 5 files changed, 200 insertions(+), 113 deletions(-) create mode 100644 chunk_queue.c create mode 100644 chunk_queue.h diff --git a/chunk_queue.c b/chunk_queue.c new file mode 100644 index 00000000..0b1e3000 --- /dev/null +++ b/chunk_queue.c @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2007 Andre Noll + * + * Licensed under the GPL v2. For licencing details see COPYING. + */ + +/** \file chunk_queue.c Queuing functions for paraslash senders. */ + +#include "para.h" +#include "list.h" +#include "vss.h" +#include "string.h" +#include "error.h" + +/** + * Senders may use the chunk queue facility to deal with laggy connections. It + * allows them to enqueue chunks if they can not be sent out immediately. + * + * Chunk queues are "cheap" in the sense that only reference to the audio file + * data is stored, but not the data itsself. + */ +struct chunk_queue { + /** The list of pending chunks for this client. */ + struct list_head q; + /** The number of pending bytes for this client. */ + unsigned long num_pending; + /** Enqueueing more than that many bytes is an error. */ + unsigned long max_pending; +}; + +/** Describes one queued chunk in a chunk queue. */ +struct queued_chunk { + /** The number of the queued chunk, -1U means header. */ + unsigned chunk_num; + /** The number of bytes already sent. */ + unsigned sent; + /** Position of the chunk in the chunk queue. */ + struct list_head node; +}; + +/** + * Add a chunk to the given queue. + * + * \param cq the queue to add the chunk to. + * \param chunk_num The number of the chunk to be queued. + * \param sent The number of bytes of this chunk that the sender was able to + * send. + * + * \return Positive on success, negative on errors. + */ +int cq_enqueue(struct chunk_queue *cq, long unsigned chunk_num, + size_t sent) +{ + struct queued_chunk *qc; + char *buf; + size_t len; + int ret; + + if (chunk_num != -1U) { + ret = vss_get_chunk(chunk_num, &buf, &len); + if (ret < 0) + return ret; + } else + buf = vss_get_header(&len); + if (cq->num_pending + len > cq->max_pending) + return -E_QUEUE; + qc = para_malloc(sizeof(struct queued_chunk)); + cq->num_pending += len; + qc->chunk_num = chunk_num; + qc->sent = sent; + list_add_tail(&qc->node, &cq->q); + PARA_DEBUG_LOG("%lu bytes queued for %p\n", cq->num_pending, &cq->q); + return 1; +} + +/** + * Lookup the next chunk in the queue. + * + * \param cq The chunk queue. + * + * \return The next queued chunk, or \p NULL if there is no chunk awailable. + */ +struct queued_chunk *cq_peek(struct chunk_queue *cq) +{ + if (list_empty(&cq->q)) + return NULL; + return list_entry(cq->q.next, struct queued_chunk, node); +} + +/** + * Remove the current chunk from the queue. + * + * \param cq The chunk to remove. + */ +void cq_dequeue(struct chunk_queue *cq) +{ + struct queued_chunk *qc = cq_peek(cq); + assert(qc); + list_del(&qc->node); + free(qc); +} + +/** + * Change the number of bytes send for the current queued chunk. + * + * \param cq The chunk queue. + * \param sent Number of bytes successfully sent. + */ +void cq_update(struct chunk_queue *cq, size_t sent) +{ + struct queued_chunk *qc = cq_peek(cq); + assert(qc); + qc->sent += sent; + cq->num_pending -= sent; +} + +/** + * Get a pointer to the given queued chunk. + * + * \param qc The queued chunk. + * \param buf Result pointer. + * \param len Number of bytes of \a buf. + * + * \return Positive on success, negative on errors. + */ +int cq_get(struct queued_chunk *qc, char **buf, size_t *len) +{ + int ret; + + if (qc->chunk_num != -1U) { + ret = vss_get_chunk(qc->chunk_num, buf, len); + if (ret < 0) + return ret; + } else + *buf = vss_get_header(len); + assert(*len > qc->sent); + *buf += qc->sent; + *len -= qc->sent; + return 1; +} + +/** + * Allocate and initialize a chunk queue. + * + * \param max_pending Maximal number of bytes that will be queued. + * + * \return A pointer to the new queue. + */ +struct chunk_queue *cq_new(size_t max_pending) +{ + struct chunk_queue *cq = para_malloc(sizeof(*cq)); + INIT_LIST_HEAD(&cq->q); + cq->max_pending = max_pending; + cq->num_pending = 0; + return cq; +} + +/** + * Deallocate all resources of this queue. + * + * \param cq The chunk queue. + */ +void cq_destroy(struct chunk_queue *cq) +{ + struct queued_chunk *qc, *tmp; + list_for_each_entry_safe(qc, tmp, &cq->q, node) { + list_del(&qc->node); + free(qc); + } + free(cq); +} diff --git a/chunk_queue.h b/chunk_queue.h new file mode 100644 index 00000000..6d981a31 --- /dev/null +++ b/chunk_queue.h @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2007 Andre Noll + * + * Licensed under the GPL v2. For licencing details see COPYING. + */ + +/** \file chunk_queue.h Exported symbols from chunk_queue.c. */ + +struct chunk_queue; +struct queued_chunk; + +int cq_enqueue(struct chunk_queue *cq, long unsigned chunk_num, size_t sent); +struct queued_chunk *cq_peek(struct chunk_queue *cq); +void cq_dequeue(struct chunk_queue *cq); +void cq_update(struct chunk_queue *cq, size_t sent); +int cq_get(struct queued_chunk *qc, char **buf, size_t *len); +struct chunk_queue *cq_new(size_t max_pending); +void cq_destroy(struct chunk_queue *cq); diff --git a/configure.ac b/configure.ac index 9d31a821..19244f99 100644 --- a/configure.ac +++ b/configure.ac @@ -109,7 +109,7 @@ server_cmdline_objs="server.cmdline server_command_list random_selector_command_ playlist_selector_command_list" server_errlist_objs="server mp3_afh vss command net string signal random_selector time daemon stat crypt http_send afs close_on_fork playlist_selector - ipc dccp dccp_send fd user_list" + ipc dccp dccp_send fd user_list chunk_queue" server_ldflags="" server_audio_formats=" mp3" diff --git a/error.h b/error.h index ffb3eedf..c0888aa0 100644 --- a/error.h +++ b/error.h @@ -63,6 +63,7 @@ enum para_subsystem { SS_FILE_WRITE, SS_OSX_WRITE, SS_USER_LIST, + SS_CHUNK_QUEUE, NUM_SS }; @@ -295,8 +296,8 @@ extern const char **para_errlist[]; #define HTTP_SEND_ERRORS \ - PARA_ERROR(QUEUE, "packet queue overrun"), \ PARA_ERROR(WRITE_OK, "can not check whether fd is writable"), \ + PARA_ERROR(SEND_QUEUED_CHUNK, "failed to send queued chunk"), \ #define RANDOM_SELECTOR_ERRORS \ @@ -421,6 +422,10 @@ extern const char **para_errlist[]; PARA_ERROR(AAC_OVERRUN, "aac output buffer overrun"), \ +#define CHUNK_QUEUE_ERRORS \ + PARA_ERROR(QUEUE, "packet queue overrun"), \ + + /** * the subsystem shift * @@ -549,6 +554,7 @@ SS_ENUM(CLIENT); SS_ENUM(CLIENT_COMMON); SS_ENUM(AUDIOC); SS_ENUM(USER_LIST); +SS_ENUM(CHUNK_QUEUE); /** \endcond */ #undef PARA_ERROR /* rest of the world only sees the error text */ diff --git a/http_send.c b/http_send.c index 1f28f722..bbf63410 100644 --- a/http_send.c +++ b/http_send.c @@ -18,6 +18,7 @@ #include "net.h" #include "string.h" #include "fd.h" +#include "chunk_queue.h" /** \cond convert sock_addr_in to ascii */ #define CLIENT_ADDR(hc) inet_ntoa((hc)->addr.sin_addr) @@ -49,14 +50,6 @@ static struct list_head clients; /** The whitelist/blacklist. */ static struct list_head access_perm_list; -struct chunk_queue{ - /** The list of pending chunks for this client. */ - struct list_head q; - /** The number of pending bytes for this client. */ - unsigned long num_pending; - unsigned long max_pending; -}; - /** Describes one client that connected the tcp port of the http sender. */ struct http_client { /** The file descriptor of the client. */ @@ -75,22 +68,6 @@ struct http_client { struct chunk_queue *cq; }; -/** - * Describes one queued chunk of the chunk queue. - * - * The send function of the http sender checks each client fd for writing. If a - * client fd is not ready, it tries to queue that chunk for this client until - * the number of queued bytes exceeds \p MAX_BACKLOG. - */ -struct queued_chunk { - /** The number of the queued chunk, -1U means header. */ - unsigned chunk_num; - /** The number of bytes already sent. */ - unsigned sent; - /** Position of the chunk in the chunk queue. */ - struct list_head node; -}; - /** * Describes one entry in the blacklist/whitelist of the http sender. */ @@ -107,90 +84,6 @@ static int server_fd = -1, numclients; static struct sender *self; -static int cq_enqueue(struct chunk_queue *cq, long unsigned chunk_num, - size_t sent) -{ - struct queued_chunk *qc; - char *buf; - size_t len; - int ret; - - if (chunk_num != -1U) { - ret = vss_get_chunk(chunk_num, &buf, &len); - if (ret < 0) - return ret; - } else - buf = vss_get_header(&len); - if (cq->num_pending + len > cq->max_pending) - return -E_QUEUE; - qc = para_malloc(sizeof(struct queued_chunk)); - cq->num_pending += len; - qc->chunk_num = chunk_num; - qc->sent = sent; - list_add_tail(&qc->node, &cq->q); - PARA_DEBUG_LOG("%lu bytes queued for %p\n", cq->num_pending, &cq->q); - return 1; -} - -static struct queued_chunk *cq_peek(struct chunk_queue *cq) -{ - if (list_empty(&cq->q)) - return NULL; - return list_entry(cq->q.next, struct queued_chunk, node); -} - -int cq_dequeue(struct chunk_queue *cq) -{ - struct queued_chunk *qc = cq_peek(cq); - assert(qc); - list_del(&qc->node); - free(qc); - return 1; -} - -void cq_update(struct chunk_queue *cq, size_t sent) -{ - struct queued_chunk *qc = cq_peek(cq); - assert(qc); - qc->sent += sent; - cq->num_pending -= sent; -} - -int cq_get(struct queued_chunk *qc, char **buf, size_t *len) -{ - int ret; - - if (qc->chunk_num != -1U) { - ret = vss_get_chunk(qc->chunk_num, buf, len); - if (ret < 0) - return ret; - } else - *buf = vss_get_header(len); - assert(*len > qc->sent); - *buf += qc->sent; - *len -= qc->sent; - return 1; -} - -struct chunk_queue *cq_init(size_t max_pending) -{ - struct chunk_queue *cq = para_malloc(sizeof(*cq)); - INIT_LIST_HEAD(&cq->q); - cq->max_pending = max_pending; - cq->num_pending = 0; - return cq; -} - -void cq_destroy(struct chunk_queue *cq) -{ - struct queued_chunk *qc, *tmp; - list_for_each_entry_safe(qc, tmp, &cq->q, node) { - list_del(&qc->node); - free(qc); - } - free(cq); -} - static void http_shutdown_client(struct http_client *hc, const char *msg) { PARA_INFO_LOG("shutting down %s on fd %d (%s)\n", CLIENT_ADDR(hc), @@ -231,7 +124,6 @@ static int http_send_err_msg(struct http_client *hc) return http_send_msg(hc, HTTP_ERR_MSG); } - static int send_queued_chunks(struct http_client *hc) { struct queued_chunk *qc; @@ -244,7 +136,7 @@ static int send_queued_chunks(struct http_client *hc) cq_get(qc, &buf, &len); ret = write(hc->fd, buf, len); if (ret < 0) - return -1; /* FIXME */ + return -E_SEND_QUEUED_CHUNK; cq_update(hc->cq, ret); if (ret != len) return 1; @@ -383,7 +275,7 @@ static void http_post_select(fd_set *rfds, fd_set *wfds) goto err_out; } hc->status = HTTP_CONNECTED; - hc->cq = cq_init(MAX_BACKLOG); + hc->cq = cq_new(MAX_BACKLOG); PARA_INFO_LOG("accepted client #%d: %s (fd %d)\n", numclients, CLIENT_ADDR(hc), hc->fd); numclients++; -- 2.39.5