From: Andre Noll Date: Sun, 27 Oct 2013 08:37:44 +0000 (+0100) Subject: Merge branch 't/buffer_tree_improvements' X-Git-Tag: v0.5.1~9 X-Git-Url: http://git.tuebingen.mpg.de/?p=paraslash.git;a=commitdiff_plain;h=b3644a3f5de245609dc608a947ed71e2f75b2fd5;hp=bcea04fa816d0c0eb5469af6117b1014de8f87ca Merge branch 't/buffer_tree_improvements' Was cooking for about a month. bcea04 btr: Simplify btr_node_status(). 072391 buffer_tree: Improve btr_splice_out_node(). --- diff --git a/NEWS b/NEWS index 7cd6838e..fc6ccffb 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,13 @@ NEWS ==== +---------------------------------------------- +0.5.1 (to be announced) "temporary implication" +---------------------------------------------- + + - audiod improvements and fixes. + - buffer tree robustness improvements. + ---------------------------------------- 0.5.0 (2013-08-23) "invertible validity" ---------------------------------------- diff --git a/audiod.c b/audiod.c index 611e72d7..5ef57781 100644 --- a/audiod.c +++ b/audiod.c @@ -520,12 +520,13 @@ static void open_writers(struct slot_info *s) assert(s->wns == NULL); s->wns = para_calloc(PARA_MAX(1U, a->num_writers) * sizeof(struct writer_node)); - PARA_INFO_LOG("opening %s writers\n", audio_formats[s->format]); for (i = 0; i < a->num_writers; i++) { wn = s->wns + i; wn->conf = a->writer_conf[i]; wn->writer_num = a->writer_nums[i]; register_writer_node(wn, parent, &sched); + PARA_NOTICE_LOG("%s writer started in slot %d\n", + writer_names[a->writer_nums[i]], (int)(s - slot)); } } @@ -914,7 +915,7 @@ out: static int parse_filter_args(void) { - int i, j, ret, af_mask; + int i, j, ret, af_mask, num_matches; for (i = 0; i < conf.filter_given; i++) { char *arg; @@ -922,13 +923,18 @@ static int parse_filter_args(void) if (ret < 0) goto out; af_mask = ret; + num_matches = 0; FOR_EACH_AUDIO_FORMAT(j) { if ((af_mask & (1 << j)) == 0) /* no match */ continue; ret = add_filter(j, arg); if (ret < 0) goto out; + num_matches++; } + if (num_matches == 0) + PARA_WARNING_LOG("ignoring filter spec: %s\n", + conf.filter_arg[i]); } ret = init_default_filters(); /* use default values for the rest */ out: @@ -1001,7 +1007,7 @@ static int signal_post_select(struct sched *s, __a_unused struct task *t) case SIGINT: case SIGTERM: case SIGHUP: - PARA_EMERG_LOG("terminating on signal %d\n", signum); + PARA_NOTICE_LOG("received signal %d\n", signum); clean_exit(EXIT_FAILURE, "caught deadly signal"); } return 0; @@ -1025,19 +1031,35 @@ static int command_post_select(struct sched *s, struct task *t) int ret; struct command_task *ct = container_of(t, struct command_task, task); static struct timeval last_status_dump; - struct timeval tmp, delay = {0, 500 * 1000}; - - tv_add(&last_status_dump, &delay, &tmp); - if (tv_diff(&tmp, now, NULL) < 0) { - audiod_status_dump(); - last_status_dump = *now; - } + struct timeval tmp, delay; + bool force = true; ret = handle_connect(ct->fd, &s->rfds); if (ret < 0) PARA_ERROR_LOG("%s\n", para_strerror(-ret)); - audiod_status_dump(); - return 0; + else if (ret > 0) + goto dump; + + /* if last status dump was less than 500ms ago, do nothing */ + delay.tv_sec = 0; + delay.tv_usec = 500 * 1000; + tv_add(&last_status_dump, &delay, &tmp); + if (tv_diff(now, &tmp, NULL) < 0) + return 0; + + /* + * If last status dump was more than 5s ago, force update. Otherwise, + * update only those items that have changed. + */ + delay.tv_sec = 5; + delay.tv_usec = 0; + tv_add(&last_status_dump, &delay, &tmp); + if (tv_diff(now, &tmp, NULL) < 0) + force = false; +dump: + audiod_status_dump(force); + last_status_dump = *now; + return 1; } static void init_command_task(struct command_task *ct) @@ -1060,28 +1082,7 @@ static void close_stat_pipe(void) stat_task->offset_seconds = 0; stat_task->vss_status = 0; stat_task->current_audio_format_num = -1; - audiod_status_dump(); -} - -/** - * close the connection to para_server and exit - * - * \param status the exit status which is passed to exit(3) - * \param msg the log message - * - * Log \a msg with loglevel \p EMERG, close the connection to para_server if - * open, and call \p exit(status). \a status should be either EXIT_SUCCESS or - * EXIT_FAILURE. - * - * \sa exit(3) - */ -void __noreturn clean_exit(int status, const char *msg) -{ - PARA_EMERG_LOG("%s\n", msg); - if (socket_name) - unlink(socket_name); - close_stat_pipe(); - exit(status); + audiod_status_dump(true); } /* avoid busy loop if server is down */ @@ -1115,20 +1116,51 @@ static bool must_close_slot(int slot_num) return true; } +static void close_slot(int slot_num) +{ + struct slot_info *s = slot + slot_num; + + PARA_INFO_LOG("closing slot %d\n", slot_num); + close_writers(s); + close_filters(s); + close_receiver(slot_num); + clear_slot(slot_num); +} + static void close_unused_slots(void) { int i; - FOR_EACH_SLOT(i) { - struct slot_info *s = slot + i; - if (!must_close_slot(i)) - continue; - PARA_INFO_LOG("closing slot %d\n", i); - close_writers(s); - close_filters(s); - close_receiver(i); - clear_slot(i); - } + FOR_EACH_SLOT(i) + if (must_close_slot(i)) + close_slot(i); +} + +/** + * Close the connection to para_server and exit. + * + * \param status The exit status which is passed to exit(3). + * \param msg The log message + * + * Log \a msg with loglevel \p EMERG, close the connection to para_server and + * all slots, and call \p exit(status). \a status should be either EXIT_SUCCESS + * or EXIT_FAILURE. + * + * \sa exit(3). + */ +void __noreturn clean_exit(int status, const char *msg) +{ + int i; + + if (socket_name) + unlink(socket_name); + close_stat_pipe(); + FOR_EACH_SLOT(i) + close_slot(i); + audiod_cmdline_parser_free(&conf); + close_stat_clients(); + PARA_EMERG_LOG("%s\n", msg); + exit(status); } /* @@ -1212,25 +1244,26 @@ static int status_post_select(struct sched *s, struct task *t) char *buf; size_t sz; int ret; - if (st->ct->task.error < 0) { + + ret = btr_node_status(st->btrn, st->min_iqs, BTR_NT_LEAF); + if (ret < 0) { close_stat_pipe(); goto out; } if (st->ct->status != CL_EXECUTING) goto out; - ret = btr_node_status(st->btrn, st->min_iqs, BTR_NT_LEAF); - if (ret <= 0) { + if (ret == 0) { struct timeval diff; tv_diff(now, &st->last_status_read, &diff); if (diff.tv_sec > 61) - task_notify(&st->ct->task, E_AUDIOD_OFF); + task_notify(&st->ct->task, E_STATUS_TIMEOUT); goto out; } btr_merge(st->btrn, st->min_iqs); sz = btr_next_buffer(st->btrn, &buf); ret = for_each_stat_item(buf, sz, update_item); if (ret < 0) { - task_notify(&st->ct->task, E_AUDIOD_OFF); + task_notify(&st->ct->task, -ret); goto out; } if (sz != ret) { diff --git a/audiod.h b/audiod.h index e54a8576..5485b424 100644 --- a/audiod.h +++ b/audiod.h @@ -68,12 +68,13 @@ extern int audiod_status; void __noreturn clean_exit(int status, const char *msg); int handle_connect(int accept_fd, fd_set *rfds); -void audiod_status_dump(void); +void audiod_status_dump(bool force); char *get_time_string(int slot_num); struct btr_node *audiod_get_btr_root(void); void stat_client_write_item(int item_num); void clear_and_dump_items(void); +void close_stat_clients(void); /** iterate over all slots */ #define FOR_EACH_SLOT(_slot) for (_slot = 0; _slot < MAX_STREAM_SLOTS; _slot++) diff --git a/audiod_command.c b/audiod_command.c index b49d659e..07b2c81c 100644 --- a/audiod_command.c +++ b/audiod_command.c @@ -30,7 +30,7 @@ extern struct sched sched; extern char *stat_item_values[NUM_STAT_ITEMS]; -struct audiod_command audiod_cmds[] = {DEFINE_AUDIOD_CMD_ARRAY}; +static struct audiod_command audiod_cmds[] = {DEFINE_AUDIOD_CMD_ARRAY}; /** Iterate over the array of all audiod commands. */ #define FOR_EACH_COMMAND(c) for (c = 0; audiod_cmds[c].name; c++) @@ -109,6 +109,31 @@ static int stat_client_add(int fd, uint64_t mask, int parser_friendly) num_clients++; return 1; } + +static void close_stat_client(struct stat_client *sc) +{ + PARA_INFO_LOG("closing client fd %d\n", sc->fd); + close(sc->fd); + list_del(&sc->node); + free(sc); + num_clients--; +} + +/** + * Empty the status clients list. + * + * This iterates over the list of connected status clients, closes each client + * file descriptor and frees the resources. + */ +void close_stat_clients(void) +{ + struct stat_client *sc, *tmp; + + list_for_each_entry_safe(sc, tmp, &client_list, node) + close_stat_client(sc); + assert(num_clients == 0); +} + /** * Write a message to all connected status clients. * @@ -127,7 +152,7 @@ void stat_client_write_item(int item_num) struct para_buffer *b; list_for_each_entry_safe(sc, tmp, &client_list, node) { - int fd = sc->fd, ret; + int ret; if (!((one << item_num) & sc->item_mask)) continue; @@ -135,15 +160,11 @@ void stat_client_write_item(int item_num) if (!b->buf) (void)WRITE_STATUS_ITEM(b, item_num, "%s\n", msg? msg : ""); - ret = write(fd, b->buf, b->offset); + ret = write(sc->fd, b->buf, b->offset); if (ret == b->offset) continue; /* write error or short write */ - close(fd); - num_clients--; - PARA_INFO_LOG("deleting client on fd %d\n", fd); - list_del(&sc->node); - free(sc); + close_stat_client(sc); dump_stat_client_list(); } free(pb.buf); @@ -320,7 +341,6 @@ static int com_stat(int fd, int argc, char **argv) if (!strncmp(arg, "-p", 2)) { parser_friendly = 1; b.flags = PBF_SIZE_PREFIX; - continue; } } if (i >= argc) @@ -466,8 +486,10 @@ out: /** * Send the current audiod status to all connected stat clients. + * + * \param force Whether to write unchanged items. */ -void audiod_status_dump(void) +void audiod_status_dump(bool force) { int slot_num = get_play_time_slot_num(); char *old, *new; @@ -475,7 +497,7 @@ void audiod_status_dump(void) old = stat_item_values[SI_PLAY_TIME]; new = get_time_string(slot_num); if (new) { - if (!old || strcmp(old, new)) { + if (force || !old || strcmp(old, new)) { free(old); stat_item_values[SI_PLAY_TIME] = new; stat_client_write_item(SI_PLAY_TIME); @@ -485,7 +507,7 @@ void audiod_status_dump(void) new = get_server_uptime_str(now); old = stat_item_values[SI_AUDIOD_UPTIME]; - if (!old || strcmp(old, new)) { + if (force || !old || strcmp(old, new)) { free(old); stat_item_values[SI_AUDIOD_UPTIME] = new; stat_client_write_item(SI_AUDIOD_UPTIME); @@ -494,7 +516,7 @@ void audiod_status_dump(void) old = stat_item_values[SI_AUDIOD_STATUS]; new = audiod_status_string(); - if (!old || strcmp(old, new)) { + if (force || !old || strcmp(old, new)) { free(old); stat_item_values[SI_AUDIOD_STATUS] = new; stat_client_write_item(SI_AUDIOD_STATUS); @@ -503,7 +525,7 @@ void audiod_status_dump(void) old = stat_item_values[SI_DECODER_FLAGS]; new = decoder_flags(); - if (!old || strcmp(old, new)) { + if (force || !old || strcmp(old, new)) { free(old); stat_item_values[SI_DECODER_FLAGS] = new; stat_client_write_item(SI_DECODER_FLAGS); diff --git a/error.h b/error.h index b0133dee..82df5ca9 100644 --- a/error.h +++ b/error.h @@ -338,6 +338,7 @@ extern const char **para_errlist[]; PARA_ERROR(UNSUPPORTED_AUDIO_FORMAT, "given audio format not supported"), \ PARA_ERROR(NOT_PLAYING, "not playing"), \ PARA_ERROR(AUDIOD_OFF, "audiod switched off"), \ + PARA_ERROR(STATUS_TIMEOUT, "status item timeout"), \ #define AUDIOD_COMMAND_ERRORS \ diff --git a/server.c b/server.c index 36af088e..70d9137e 100644 --- a/server.c +++ b/server.c @@ -388,6 +388,9 @@ static int command_post_select(struct sched *s, struct task *t) goto out; } if (child_pid) { + /* avoid problems with non-fork-safe PRNGs */ + unsigned char buf[16]; + get_random_bytes_or_die(buf, sizeof(buf)); close(new_fd); /* parent keeps accepting connections */ return 0;