146316 btr_exec_up(): Also ask given node.
6d9c35 btr: Introduce btr_add_output_dont_free().
4ac313 btr: Remove btr_free_node().
Has been cooking in next for a week.
0.4.11 (to be announced) "mutual diversity"
-------------------------------------------
+The major feature in this release is the new sideband API for
+client-server communication. This API will be used exclusively starting
+with 0.5.0, which breaks backward compatibility but allows to get rid
+of quite some compatibility code. Other noteworthy changes include
+decoder latency improvements and a long-standing bug fix for the
+ALSA writer.
+
- Sideband connections: If both para_server and para_client
support this feature, data is sent as a multiplexed stream.
- The --no_default_filters option of para_filter has been
removed.
- Several fixes and latency improvements to various decoders.
- - The alsa writer now limits the prebuffer time to 500ms.
+ - The ALSA writer now limits the prebuffer time to 500ms.
- Documentation improvements.
- Overhaul of the command_util.sh script.
- Fixes for some minor problems found by the clang analyzer.
- Compiles (almost) without warnings on gcc-3.
+ - Robustness improvements of the buffer tree code.
------------------------------------------
0.4.10 (2012-03-30) "heterogeneous vacuum"
err:
assert(ret < 0);
t->error = ret;
- btr_remove_node(btrn);
+ btr_remove_node(&fn->btrn);
}
/**
ret = -E_ALSA;
err:
assert(ret < 0);
- btr_remove_node(btrn);
+ btr_remove_node(&wn->btrn);
t->error = ret;
}
err:
assert(ret < 0);
t->error = ret;
- btr_remove_node(btrn);
+ btr_remove_node(&fn->btrn);
}
static void amp_free_config(void *conf)
struct task *t)
{
struct writer_node *wn = container_of(t, struct writer_node, task);
- struct btr_node *btrn = wn->btrn;
struct private_aow_data *pawd = wn->private_data;
int ret;
int32_t rate, ch, format;
struct btr_node_description bnd;
- ret = btr_node_status(btrn, wn->min_iqs, BTR_NT_LEAF);
+ ret = btr_node_status(wn->btrn, wn->min_iqs, BTR_NT_LEAF);
if (ret < 0)
goto remove_btrn;
if (ret == 0)
return;
- get_btr_sample_rate(btrn, &rate);
- get_btr_channels(btrn, &ch);
- get_btr_sample_format(btrn, &format);
+ get_btr_sample_rate(wn->btrn, &rate);
+ get_btr_channels(wn->btrn, &ch);
+ get_btr_sample_format(wn->btrn, &format);
ret = aow_init(wn, rate, ch, format);
if (ret < 0)
goto remove_btrn;
/* set up thread btr node */
bnd.name = "ao_thread_btrn";
- bnd.parent = btrn;
+ bnd.parent = wn->btrn;
bnd.child = NULL;
bnd.handler = NULL;
bnd.context = pawd;
return;
}
pthread_mutex_lock(&pawd->mutex);
- ret = btr_node_status(btrn, wn->min_iqs, BTR_NT_LEAF);
+ ret = btr_node_status(wn->btrn, wn->min_iqs, BTR_NT_LEAF);
if (ret > 0) {
- btr_pushdown(btrn);
+ btr_pushdown(wn->btrn);
pthread_cond_signal(&pawd->data_available);
}
pthread_mutex_unlock(&pawd->mutex);
if (ret >= 0)
goto out;
pthread_mutex_lock(&pawd->mutex);
- btr_remove_node(btrn);
- btrn = NULL;
+ btr_remove_node(&wn->btrn);
PARA_INFO_LOG("waiting for thread to terminate\n");
pthread_cond_signal(&pawd->data_available);
pthread_mutex_unlock(&pawd->mutex);
pthread_join(pawd->thread, NULL);
remove_thread_btrn:
- btr_remove_node(pawd->thread_btrn);
- btr_free_node(pawd->thread_btrn);
+ btr_remove_node(&pawd->thread_btrn);
remove_btrn:
- if (btrn)
- btr_remove_node(btrn);
+ btr_remove_node(&wn->btrn);
out:
t->error = ret;
}
out:
if (ret < 0) {
free(buf);
- btr_remove_node(at->btrn);
- btr_free_node(at->btrn);
- at->btrn = NULL;
+ btr_remove_node(&at->btrn);
close(at->fd);
}
t->error = ret;
PARA_NOTICE_LOG("closing %s receiver in slot %d\n",
audio_formats[s->format], slot_num);
a->receiver->close(s->receiver_node);
- btr_free_node(s->receiver_node->btrn);
+ btr_remove_node(&s->receiver_node->btrn);
free(s->receiver_node);
s->receiver_node = NULL;
tv_add(now, &(struct timeval)EMBRACE(0, 200 * 1000),
w = writers + wn->writer_num;
PARA_INFO_LOG("closing %s\n", writer_names[wn->writer_num]);
w->close(wn);
- btr_free_node(wn->btrn);
+ btr_remove_node(&wn->btrn);
}
static void close_writers(struct slot_info *s)
f = filters + fn->filter_num;
if (f->close)
f->close(fn);
- btr_free_node(fn->btrn);
+ btr_remove_node(&fn->btrn);
}
free(s->fns);
s->fns = NULL;
* task. Note that the scheduler checks t->error also _before_ each pre/post
* select call, so the victim will never be scheduled again.
*/
-static void kill_btrn(struct btr_node *btrn, struct task *t, int error)
+static void kill_btrn(struct btr_node **btrnp, struct task *t, int error)
{
if (t->error < 0)
return;
t->error = error;
- btr_remove_node(btrn);
+ btr_remove_node(btrnp);
}
static void kill_all_decoders(int error)
continue;
if (!s->receiver_node)
continue;
- kill_btrn(s->receiver_node->btrn, &s->receiver_node->task,
+ kill_btrn(&s->receiver_node->btrn, &s->receiver_node->task,
error);
}
}
EMBRACE(.name = r->name, .context = rn));
ret = r->open(rn);
if (ret < 0) {
- btr_free_node(rn->btrn);
+ btr_remove_node(&rn->btrn);
free(rn);
return ret;
}
if (!st->ct)
goto out;
if (st->ct->task.error >= 0) {
- kill_btrn(st->ct->btrn, &st->ct->task, -E_AUDIOD_OFF);
+ kill_btrn(&st->ct->btrn, &st->ct->task, -E_AUDIOD_OFF);
goto out;
}
close_stat_pipe();
struct timeval diff;
tv_diff(now, &st->last_status_read, &diff);
if (diff.tv_sec > 61)
- kill_btrn(st->ct->btrn, &st->ct->task,
+ kill_btrn(&st->ct->btrn, &st->ct->task,
-E_STATUS_TIMEOUT);
goto out;
}
sz = btr_next_buffer(st->btrn, &buf);
ret = for_each_stat_item(buf, sz, update_item);
if (ret < 0) {
- kill_btrn(st->ct->btrn, &st->ct->task, ret);
+ kill_btrn(&st->ct->btrn, &st->ct->task, ret);
goto out;
}
if (sz != ret) {
int refcount;
/* NULL means no buffer pool but a malloced buffer. */
struct btr_pool *pool;
+ /* Only relevant if pool is NULL. */
+ bool dont_free;
};
struct btr_buffer_reference {
{
if (btrb->pool)
btr_pool_deallocate(btrb->pool, btrb->size);
- else
+ else if (!btrb->dont_free)
free(btrb->buf);
}
add_btrb_to_children(btrb, btrn, 0);
}
+/**
+ * Insert a buffer into the buffer tree, non-freeing variant.
+ *
+ * \param buf See \ref btr_add_output().
+ * \param size See \ref btr_add_output().
+ * \param btrn See \ref btr_add_output().
+ *
+ * This is similar to btr_add_output() but additionally sets the \p dont_free
+ * flag on \a buf. If the refcount for the buffer drops to zero, \a buf will
+ * not be deallocated if this flag is set.
+ *
+ * The \p dont_free bit also prevents the children of \a btrn from modifying
+ * the buffer contents inplace. Specifically, \ref btr_inplace_ok() returns
+ * false if there is any buffer in the input queue with the \p dont_free bit
+ * set.
+ */
+void btr_add_output_dont_free(const char *buf, size_t size, struct btr_node *btrn)
+{
+ struct btr_buffer *btrb;
+
+ assert(size != 0);
+ if (list_empty(&btrn->children))
+ return;
+ btrb = new_btrb((char *)buf, size);
+ btrb->dont_free = true;
+ add_btrb_to_children(btrb, btrn, 0);
+}
+
/**
* Feed data to child nodes of a buffer tree node.
*
*/
bool btr_inplace_ok(struct btr_node *btrn)
{
- if (!btrn->parent)
- return true;
- return list_is_singular(&btrn->parent->children);
+ struct btr_buffer_reference *br;
+ FOR_EACH_BUFFER_REF(br, btrn) {
+ struct btr_buffer *btrb = br->btrb;
+ if (btrb->refcount > 1)
+ return false;
+ if (btrb->dont_free == true)
+ return false;
+ }
+ return true;
}
static inline size_t br_available_bytes(struct btr_buffer_reference *br)
btr_drop_buffer_reference(br);
}
-/**
- * Free all resources allocated by btr_new_node().
- *
- * \param btrn Pointer to a btr node obtained by \ref btr_new_node().
- *
- * Like free(3), it is OK to call this with a \p NULL pointer argument.
- */
-void btr_free_node(struct btr_node *btrn)
-{
- if (!btrn)
- return;
- free(btrn->name);
- free(btrn);
-}
-
/**
* Remove a node from a buffer tree.
*
- * \param btrn The node to remove.
+ * \param btrnp Determines the node to remove.
*
- * This makes all child nodes of \a btrn orphans and removes \a btrn from the
- * list of children of its parent. Moreover, the input queue of \a btrn is
- * flushed if it is not empty.
+ * This orphans all children of the node given by \a btrnp and removes this
+ * node from the child list of its parent. Moreover, the input queue is flushed
+ * and the node pointer given by \a btrp is set to \p NULL.
*
* \sa \ref btr_splice_out_node.
*/
-void btr_remove_node(struct btr_node *btrn)
+void btr_remove_node(struct btr_node **btrnp)
{
struct btr_node *ch;
+ struct btr_node *btrn;
- if (!btrn)
+ if (!btrnp)
return;
+ btrn = *btrnp;
+ if (!btrn)
+ goto out;
PARA_INFO_LOG("removing btr node %s from buffer tree\n", btrn->name);
FOR_EACH_CHILD(ch, btrn)
ch->parent = NULL;
btr_drain(btrn);
if (btrn->parent)
list_del(&btrn->node);
+ free(btrn->name);
+ free(btrn);
+out:
+ *btrnp = NULL;
}
/**
}
/**
- * Execute a inter-node command on a parent node.
+ * Execute a inter-node command on the given node or on a parent node.
*
* \param btrn The node to start looking.
* \param command The command to execute.
* \param value_result Additional arguments and result value.
*
- * This function traverses the buffer tree upwards and looks for parent nodes
- * of \a btrn that understands \a command. On the first such node the command
- * is executed, and the result is stored in \a value_result.
+ * This function traverses the buffer tree from \a btrn upwards and looks for
+ * the first node that understands \a command. On this node \a command is
+ * executed, and the result is stored in \a value_result.
*
* \return \p -ENOTSUP if no parent node of \a btrn understands \a command.
* Otherwise the return value of the command handler is returned.
+ *
+ * \sa \ref receiver::execute, filter::execute, writer::execute.
*/
int btr_exec_up(struct btr_node *btrn, const char *command, char **value_result)
{
int ret;
for (; btrn; btrn = btrn->parent) {
- struct btr_node *parent = btrn->parent;
- if (!parent)
- return -ERRNO_TO_PARA_ERROR(ENOTSUP);
- if (!parent->execute)
+ if (!btrn->execute)
continue;
- PARA_INFO_LOG("parent: %s, cmd: %s\n", parent->name, command);
- ret = parent->execute(parent, command, value_result);
+ PARA_INFO_LOG("executing %s on %s\n", command, btrn->name);
+ ret = btrn->execute(btrn, command, value_result);
if (ret == -ERRNO_TO_PARA_ERROR(ENOTSUP))
continue;
if (ret < 0)
return ret;
if (value_result && *value_result)
- PARA_INFO_LOG("%s(%s): %s\n", command, parent->name,
+ PARA_INFO_LOG("%s(%s): %s\n", command, btrn->name,
*value_result);
return 1;
}
struct btr_node *btrn);
struct btr_node *btr_new_node(struct btr_node_description *bnd);
-void btr_remove_node(struct btr_node *btrn);
-void btr_free_node(struct btr_node *btrn);
+void btr_remove_node(struct btr_node **btrnp);
void btr_add_output(char *buf, size_t size, struct btr_node *btrn);
+void btr_add_output_dont_free(const char *buf, size_t size, struct btr_node *btrn);
size_t btr_get_input_queue_size(struct btr_node *btrn);
size_t btr_get_output_queue_size(struct btr_node *btrn);
bool btr_no_parent(struct btr_node *btrn);
goto out;
schedule(&command_sched);
*result = exec_task.result_buf;
- btr_remove_node(exec_task.btrn);
+ btr_remove_node(&exec_task.btrn);
client_disconnect(ct);
ret = 1;
out:
- btr_free_node(exec_task.btrn);
+ btr_remove_node(&exec_task.btrn);
if (ret < 0)
free(exec_task.result_buf);
return ret;
if (ret < 0)
PARA_ERROR_LOG("%s\n", para_strerror(-ret));
client_close(ct);
- btr_free_node(sit.btrn);
- btr_free_node(sot.btrn);
+ btr_remove_node(&sit.btrn);
+ btr_remove_node(&sot.btrn);
return ret < 0? EXIT_FAILURE : EXIT_SUCCESS;
}
ct->scc.recv = NULL;
sc_free(ct->scc.send);
ct->scc.send = NULL;
- btr_free_node(ct->btrn);
- ct->btrn = NULL;
+ btr_remove_node(&ct->btrn);
}
/**
if (!ct->use_sideband && ret != -E_SERVER_EOF &&
ret != -E_BTR_EOF && ret != -E_EOF)
PARA_ERROR_LOG("%s\n", para_strerror(-t->error));
- btr_remove_node(btrn);
+ btr_remove_node(&ct->btrn);
}
}
err:
assert(ret < 0);
t->error = ret;
- btr_remove_node(btrn);
+ btr_remove_node(&fn->btrn);
}
/** TODO: Add sanity checks */
out:
if (ret >= 0)
return;
- btr_remove_node(rn->btrn);
+ btr_remove_node(&rn->btrn);
t->error = ret;
}
out:
t->error = ret;
if (ret < 0)
- btr_remove_node(btrn);
+ btr_remove_node(&fn->btrn);
}
static void fecdec_open(struct filter_node *fn)
btr_consume(btrn, ret);
out:
if (ret < 0)
- btr_remove_node(btrn);
+ btr_remove_node(&wn->btrn);
t->error = ret;
}
f = filters + fn->filter_num;
if (f->close)
f->close(fn);
- btr_free_node(fn->btrn);
+ btr_remove_node(&fn->btrn);
free(fn->conf);
free(fn);
}
free(fns);
- btr_free_node(sit->btrn);
- btr_free_node(sot->btrn);
+ btr_remove_node(&sit->btrn);
+ btr_remove_node(&sot->btrn);
out:
if (ret < 0)
PARA_EMERG_LOG("%s\n", para_strerror(-ret));
out:
t->error = ret;
if (ret < 0)
- btr_remove_node(btrn);
+ btr_remove_node(&fn->btrn);
}
static void flacdec_close(struct filter_node *fn)
static int gc_close(struct grab_client *gc, int err)
{
- btr_remove_node(gc->btrn);
- btr_free_node(gc->btrn);
- gc->btrn = NULL;
+ btr_remove_node(&gc->btrn);
PARA_INFO_LOG("closing gc: %s\n", para_strerror(-err));
list_move(&gc->node, &inactive_grab_client_list);
if (err == -E_GC_WRITE || (gc->flags & GF_ONE_SHOT)) {
out:
if (ret >= 0)
return;
- btr_remove_node(rn->btrn);
+ btr_remove_node(&rn->btrn);
t->error = ret;
}
btr_consume(btrn, ret);
goto out;
rm_btrn:
- btr_remove_node(btrn);
- btr_free_node(btrn);
- i9ep->stdout_btrn = NULL;
+ btr_remove_node(&i9ep->stdout_btrn);
rl_set_prompt(i9ep->ici->prompt);
rl_forced_update_display();
out:
err:
assert(ret < 0);
t->error = ret;
- btr_remove_node(btrn);
+ btr_remove_node(&fn->btrn);
}
static void mp3dec_open(struct filter_node *fn)
out:
t->error = ret;
if (ret < 0)
- btr_remove_node(btrn);
+ btr_remove_node(&fn->btrn);
}
/**
out:
t->error = ret;
if (ret < 0)
- btr_remove_node(btrn);
+ btr_remove_node(&wn->btrn);
}
__malloc static void *oss_parse_config_or_die(const char *options)
AudioOutputUnitStop(powd->audio_unit);
AudioUnitUninitialize(powd->audio_unit);
CloseComponent(powd->audio_unit);
- btr_remove_node(powd->callback_btrn);
- btr_free_node(powd->callback_btrn);
+ btr_remove_node(&powd->callback_btrn);
remove_btrn:
- btr_remove_node(btrn);
+ btr_remove_node(&wn->btrn);
PARA_NOTICE_LOG("%s\n", para_strerror(-ret));
out:
t->error = ret;
out:
if (r_opened)
r->close(&rn);
- btr_free_node(rn.btrn);
- btr_free_node(sot.btrn);
+ btr_remove_node(&rn.btrn);
+ btr_remove_node(&sot.btrn);
if (rn.conf)
r->free_config(rn.conf);
+
if (ret < 0)
PARA_ERROR_LOG("%s\n", para_strerror(-ret));
return ret < 0? EXIT_FAILURE : EXIT_SUCCESS;
fail:
if (ret < 0) {
t->error = ret;
- btr_remove_node(btrn);
+ btr_remove_node(&fn->btrn);
}
}
if (ret >= 0)
return;
err:
- btr_remove_node(sit->btrn);
+ btr_remove_node(&sit->btrn);
//btr_pool_free(sit->btrp);
t->error = ret;
}
}
out:
if (ret < 0)
- btr_remove_node(btrn);
+ btr_remove_node(&sot->btrn);
t->error = ret;
}
/**
out:
if (ret >= 0)
return;
- btr_remove_node(btrn);
+ btr_remove_node(&rn->btrn);
t->error = ret;
close(rn->fd);
rn->fd = -1;
if (ret == -E_WAV_SUCCESS)
btr_splice_out_node(btrn);
else {
- btr_remove_node(btrn);
+ btr_remove_node(&fn->btrn);
PARA_ERROR_LOG("%s\n", para_strerror(-ret));
}
}
err:
assert(ret < 0);
t->error = ret;
- btr_remove_node(btrn);
+ btr_remove_node(&fn->btrn);
}
static void wmadec_open(struct filter_node *fn)
out:
t->error = ret;
if (ret < 0)
- btr_remove_node(btrn);
+ btr_remove_node(&cwt->btrn);
}
static int loglevel;
struct writer *w = writers + wn->writer_num;
w->close(wn);
- btr_free_node(wn->btrn);
+ btr_remove_node(&wn->btrn);
w->free_config(wn->conf);
free(wn->conf);
}
free(wns);
- btr_free_node(cwt->btrn);
+ btr_remove_node(&cwt->btrn);
return ret;
}