btr: Introduce btr_add_output_dont_free().
authorAndre Noll <maan@systemlinux.org>
Tue, 23 Aug 2011 19:04:02 +0000 (21:04 +0200)
committerAndre Noll <maan@systemlinux.org>
Sun, 8 Jul 2012 17:06:39 +0000 (17:06 +0000)
At the moment, all buffers which are fed to a buffer tree must be
allocated on the heap since the buffer tree code automatically frees
the buffer once its refcount dropped to zero.

The new afh receiver, however, mmaps the audio file and likes to feed
chunks of this memory map into the buffer tree. This is currently
impossible because such buffers must not be freed.

This patch adds the new public function btr_add_output_dont_free()
which works like btr_add_output() but sets the new dont_free bit
which prevents the buffer from being deallocated.

Also btr_inplace_ok() is changed to return "false" whenever there
exists a buffer in the input queue with the dont_free bit set.

buffer_tree.c
buffer_tree.h

index 5c88470..c62cbf1 100644 (file)
@@ -30,6 +30,8 @@ struct btr_buffer {
        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 {
@@ -319,7 +321,7 @@ static void dealloc_buffer(struct btr_buffer *btrb)
 {
        if (btrb->pool)
                btr_pool_deallocate(btrb->pool, btrb->size);
-       else
+       else if (!btrb->dont_free)
                free(btrb->buf);
 }
 
@@ -392,6 +394,34 @@ void btr_add_output(char *buf, size_t size, struct btr_node *btrn)
        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.
  *
@@ -544,9 +574,15 @@ bool btr_no_parent(struct btr_node *btrn)
  */
 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)
index 91106a1..8e8d341 100644 (file)
@@ -184,6 +184,7 @@ void btr_copy(const void *src, size_t n, struct btr_pool *btrp,
 struct btr_node *btr_new_node(struct btr_node_description *bnd);
 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);