put everything in tests/
authorAndy Wingo <wingo@pobox.com>
Sat, 5 Jan 2002 01:25:28 +0000 (01:25 +0000)
committerAndy Wingo <wingo@pobox.com>
Sat, 5 Jan 2002 01:25:28 +0000 (01:25 +0000)
Original commit message from CVS:
put everything in tests/

15 files changed:
tests/Makefile.am
tests/README
tests/bufspeed/Makefile.am [new file with mode: 0644]
tests/bufspeed/README [new file with mode: 0644]
tests/bufspeed/gstbuffer.c [new file with mode: 0644]
tests/bufspeed/gstbuffer.h [new file with mode: 0644]
tests/bufspeed/gstmempool.c [new file with mode: 0644]
tests/bufspeed/gstmempool.h [new file with mode: 0644]
tests/bufspeed/test1.c [new file with mode: 0644]
tests/bufspeed/test2.c [new file with mode: 0644]
tests/memchunk/Makefile.am [new file with mode: 0644]
tests/memchunk/gmemchunktest.c [new file with mode: 0644]
tests/memchunk/gstmemchunk.c [new file with mode: 0644]
tests/memchunk/gstmemchunk.h [new file with mode: 0644]
tests/memchunk/gstmemchunktest.c [new file with mode: 0644]

index df08733..e9a58ec 100644 (file)
@@ -1,18 +1,8 @@
-SUBDIRS = sched eos nego muxing
+SUBDIRS = bufspeed memchunk muxing sched
 
-if GST_DISABLE_LOADSAVE
-GST_LOADSAVE_PROG =
-else
-GST_LOADSAVE_PROG = caps registry autoplug props tee autoplug2 capsconnect \
-                   padfactory autoplug4
-endif
+noinst_PROGRAMS = lat
 
-noinst_PROGRAMS = $(GST_LOADSAVE_PROG) init loadall simplefake states queue \
-paranoia rip mp3encode case4 markup load autoplug3 \
-incsched reaping threadlock mp1vid reconnect \
-faketest events timecache
-
-LDADD = $(GST_LIBS)
-AM_CFLAGS = $(GST_CFLAGS)
+lat_CFLAGS = $(GST_CFLAGS)
+lat_LDFLAGS = $(GST_LIBS)
 
 EXTRA_DIST = README
index 274b291..d0a0f60 100644 (file)
@@ -1,2 +1,3 @@
-This directory contains various tests designed to verify GStreamer
-behavior.  If any of them exit with a non-zero value, something's wrong.
+This directory contains various tests designed to test GStreamer's
+behavior. When these tests work, they should be recast in the form
+of regression tests and moved to ../testsuite.
diff --git a/tests/bufspeed/Makefile.am b/tests/bufspeed/Makefile.am
new file mode 100644 (file)
index 0000000..e86b7a7
--- /dev/null
@@ -0,0 +1,6 @@
+noinst_PROGRAMS = test1 test2
+
+test1_SOURCES = test1.c gstbuffer.c gstmempool.c
+
+#LIBS += $(GST_LIBS)
+CFLAGS += $(GST_CFLAGS) 
diff --git a/tests/bufspeed/README b/tests/bufspeed/README
new file mode 100644 (file)
index 0000000..8bb6600
--- /dev/null
@@ -0,0 +1,6 @@
+benchmark of 5000000 gst_buffer_new/free on 0.2.1 code
+------------------------------------------------------
+gstmemchunk, no lock:   real    0m1.309s user   0m1.220s sys    0m0.070s
+gmemchunk, no lock:     real    0m3.872s user   0m3.740s sys    0m0.090s
+gstmemchunk, lock:      real    0m5.306s user   0m5.160s sys    0m0.100s
+gmemchunk, lock:        real    0m8.001s user   0m7.890s sys    0m0.080s
diff --git a/tests/bufspeed/gstbuffer.c b/tests/bufspeed/gstbuffer.c
new file mode 100644 (file)
index 0000000..35a92ee
--- /dev/null
@@ -0,0 +1,495 @@
+/* GStreamer
+ * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
+ *                    2000 Wim Taymans <wtay@chello.be>
+ *
+ * gstbuffer.c: Buffer operations
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/* this file makes too much noise for most debugging sessions */
+#define GST_DEBUG_FORCE_DISABLE
+#include "gst/gst_private.h"
+
+#include "gstbuffer.h"
+#include "gstmempool.h"
+
+GType _gst_buffer_type;
+
+static GstMemPool *_gst_buffer_pool;
+
+static void
+gst_buffer_alloc_func (GstMemPool *pool, gpointer data)
+{
+  GstBuffer *buffer = GST_BUFFER (data);
+
+  GST_DATA_TYPE(buffer) = _gst_buffer_type;
+  buffer->lock = g_mutex_new ();
+}
+
+static void
+gst_buffer_free_func (GstMemPool *pool, gpointer data)
+{
+  GstBuffer *buffer = GST_BUFFER (data);
+
+  g_mutex_free (buffer->lock);
+}
+
+void 
+_gst_buffer_initialize (void) 
+{
+  int buffersize = sizeof(GstBuffer);
+  static const GTypeInfo buffer_info = {
+    0, // sizeof(class),
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    0, // sizeof(object),
+    0,
+    NULL,
+  };
+
+  // round up to the nearest 32 bytes for cache-line and other efficiencies
+  buffersize = (((buffersize-1) / 32) + 1) * 32;
+
+  _gst_buffer_pool = gst_mem_pool_new ("GstBuffer", buffersize,
+    buffersize * 32, G_ALLOC_AND_FREE, gst_buffer_alloc_func, gst_buffer_free_func);
+
+  _gst_buffer_type = g_type_register_static (G_TYPE_INT, "GstBuffer", &buffer_info, 0);
+}
+
+/**
+ * gst_buffer_new:
+ *
+ * Create a new buffer.
+ *
+ * Returns: new buffer
+ */
+GstBuffer*
+gst_buffer_new (void)
+{
+  GstBuffer *buffer;
+
+  buffer = gst_mem_pool_alloc (_gst_buffer_pool);
+
+  GST_INFO (GST_CAT_BUFFER,"creating new buffer %p",buffer);
+
+#ifdef HAVE_ATOMIC_H
+  atomic_set (&buffer->refcount, 1);
+#else
+  buffer->refcount = 1;
+#endif
+  buffer->offset = -1;
+  buffer->flags = 0;
+  buffer->data = NULL;
+  buffer->size = 0;
+  buffer->maxsize = 0;
+  buffer->timestamp = 0;
+  buffer->parent = NULL;
+  buffer->pool = NULL;
+  buffer->pool_private = NULL;
+  buffer->free = NULL;
+  buffer->copy = NULL;
+
+  return buffer;
+}
+
+/**
+ * gst_buffer_new_from_pool:
+ * @pool: the buffer pool to use
+ *
+ * Create a new buffer using the specified bufferpool.
+ *
+ * Returns: new buffer
+ */
+GstBuffer*
+gst_buffer_new_from_pool (GstBufferPool *pool, guint32 offset, guint32 size)
+{
+  GstBuffer *buffer;
+
+  g_return_val_if_fail (pool != NULL, NULL);
+  g_return_val_if_fail (pool->buffer_new != NULL, NULL);
+  
+  buffer = pool->buffer_new (pool, offset, size, pool->user_data);
+  buffer->pool = pool;
+  buffer->free = pool->buffer_free;
+  buffer->copy = pool->buffer_copy;
+  
+  GST_INFO (GST_CAT_BUFFER,"creating new buffer %p from pool %p (size %x, offset %x)", 
+                 buffer, pool, size, offset);
+
+  return buffer;
+}
+
+/**
+ * gst_buffer_create_sub:
+ * @parent: parent buffer
+ * @offset: offset into parent buffer
+ * @size: size of new subbuffer
+ *
+ * Creates a sub-buffer from the parent at a given offset.
+ *
+ * Returns: new buffer
+ */
+GstBuffer*
+gst_buffer_create_sub (GstBuffer *parent,
+                      guint32 offset,
+                      guint32 size) 
+{
+  GstBuffer *buffer;
+
+  g_return_val_if_fail (parent != NULL, NULL);
+  g_return_val_if_fail (GST_BUFFER_REFCOUNT(parent) > 0, NULL);
+  g_return_val_if_fail (size > 0, NULL);
+  g_return_val_if_fail ((offset+size) <= parent->size, NULL);
+
+  buffer = gst_mem_pool_alloc (_gst_buffer_pool);
+  GST_DATA_TYPE(buffer) = _gst_buffer_type;
+
+  GST_INFO (GST_CAT_BUFFER,"creating new subbuffer %p from parent %p (size %u, offset %u)", 
+                 buffer, parent, size, offset);
+
+#ifdef HAVE_ATOMIC_H
+  atomic_set (&buffer->refcount, 1);
+#else
+  buffer->refcount = 1;
+#endif
+
+  // copy flags and type from parent, for lack of better
+  buffer->flags = parent->flags;
+
+  // set the data pointer, size, offset, and maxsize
+  buffer->data = parent->data + offset;
+  buffer->size = size;
+  buffer->maxsize = parent->size - offset;
+
+  // deal with bogus/unknown offsets
+  if (parent->offset != -1)
+    buffer->offset = parent->offset + offset;
+  else
+    buffer->offset = -1;
+
+  // again, for lack of better, copy parent's timestamp
+  buffer->timestamp = parent->timestamp;
+  buffer->maxage = parent->maxage;
+
+  // if the parent buffer is a subbuffer itself, use its parent, a real buffer
+  if (parent->parent != NULL)
+    parent = parent->parent;
+
+  // set parentage and reference the parent
+  buffer->parent = parent;
+  gst_buffer_ref (parent);
+
+  buffer->pool = NULL;
+
+  return buffer;
+}
+
+
+// FIXME FIXME: how does this overlap with the newly-added gst_buffer_span() ???
+/**
+ * gst_buffer_append:
+ * @buffer: a buffer
+ * @append: the buffer to append
+ *
+ * Creates a new buffer by appending the data of append to the
+ * existing data of buffer.
+ *
+ * Returns: new buffer
+ */
+GstBuffer*
+gst_buffer_append (GstBuffer *buffer, 
+                  GstBuffer *append) 
+{
+  guint size;
+  GstBuffer *newbuf;
+
+  g_return_val_if_fail (buffer != NULL, NULL);
+  g_return_val_if_fail (append != NULL, NULL);
+  g_return_val_if_fail (buffer->pool == NULL, NULL);
+  g_return_val_if_fail (GST_BUFFER_REFCOUNT(buffer) > 0, NULL);
+  g_return_val_if_fail (GST_BUFFER_REFCOUNT(append) > 0, NULL);
+
+  GST_INFO (GST_CAT_BUFFER,"appending buffers %p and %p",buffer,append);
+
+  GST_BUFFER_LOCK (buffer);
+  // the buffer is not used by anyone else
+  if (GST_BUFFER_REFCOUNT (buffer) == 1 && buffer->parent == NULL 
+         && !GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_DONTFREE)) {
+    // save the old size
+    size = buffer->size;
+    buffer->size += append->size;
+    buffer->data = g_realloc (buffer->data, buffer->size);
+    memcpy(buffer->data + size, append->data, append->size);
+    GST_BUFFER_UNLOCK (buffer);
+  }
+  // the buffer is used, create a new one 
+  else {
+    newbuf = gst_buffer_new ();
+    newbuf->size = buffer->size+append->size;
+    newbuf->data = g_malloc (newbuf->size);
+    memcpy (newbuf->data, buffer->data, buffer->size);
+    memcpy (newbuf->data+buffer->size, append->data, append->size);
+    GST_BUFFER_UNLOCK (buffer);
+    gst_buffer_unref (buffer);
+    buffer = newbuf;
+  }
+  return buffer;
+}
+
+/**
+ * gst_buffer_destroy:
+ * @buffer: the GstBuffer to destroy
+ *
+ * destroy the buffer
+ */
+void 
+gst_buffer_destroy (GstBuffer *buffer) 
+{
+
+  g_return_if_fail (buffer != NULL);
+  
+  GST_INFO (GST_CAT_BUFFER, "freeing %sbuffer %p",
+           (buffer->parent?"sub":""),
+           buffer);
+  
+  // free the data only if there is some, DONTFREE isn't set, and not sub
+  if (GST_BUFFER_DATA (buffer) &&
+      !GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_DONTFREE) &&
+      (buffer->parent == NULL)) {
+    // if there's a free function, use it
+    if (buffer->free != NULL) {
+      (buffer->free)(buffer);
+    } else {
+      g_free (GST_BUFFER_DATA (buffer));
+    }
+  }
+
+  // unreference the parent if there is one
+  if (buffer->parent != NULL)
+    gst_buffer_unref (buffer->parent);
+
+  // remove it entirely from memory
+  gst_mem_pool_free (_gst_buffer_pool,buffer);
+}
+
+/**
+ * gst_buffer_ref:
+ * @buffer: the GstBuffer to reference
+ *
+ * Increment the refcount of this buffer.
+ */
+void 
+gst_buffer_ref (GstBuffer *buffer) 
+{
+  g_return_if_fail (buffer != NULL);
+  g_return_if_fail (GST_BUFFER_REFCOUNT(buffer) > 0);
+
+  GST_INFO (GST_CAT_BUFFER, "ref buffer %p\n", buffer);
+
+#ifdef HAVE_ATOMIC_H
+  atomic_inc (&(buffer->refcount));
+#else
+  GST_BUFFER_LOCK (buffer);
+  buffer->refcount++;
+  GST_BUFFER_UNLOCK (buffer);
+#endif
+}
+
+/**
+ * gst_buffer_unref:
+ * @buffer: the GstBuffer to unref
+ *
+ * Decrement the refcount of this buffer. If the refcount is
+ * zero, the buffer will be destroyed.
+ */
+void 
+gst_buffer_unref (GstBuffer *buffer) 
+{
+  gint zero;
+
+  g_return_if_fail (buffer != NULL);
+  g_return_if_fail (GST_BUFFER_REFCOUNT(buffer) > 0);
+
+  GST_INFO (GST_CAT_BUFFER, "unref buffer %p\n", buffer);
+
+#ifdef HAVE_ATOMIC_H
+  zero = atomic_dec_and_test (&(buffer->refcount));
+#else
+  GST_BUFFER_LOCK (buffer);
+  buffer->refcount--;
+  zero = (buffer->refcount == 0);
+  GST_BUFFER_UNLOCK (buffer);
+#endif
+
+  /* if we ended up with the refcount at zero, destroy the buffer */
+  if (zero) {
+    gst_buffer_destroy (buffer);
+  }
+}
+
+/**
+ * gst_buffer_copy:
+ * @buffer: the orignal GstBuffer to make a copy of
+ *
+ * Make a full copy of the give buffer, data and all.
+ *
+ * Returns: new buffer
+ */
+GstBuffer *
+gst_buffer_copy (GstBuffer *buffer)
+{
+  GstBuffer *newbuf;
+
+  g_return_val_if_fail (GST_BUFFER_REFCOUNT(buffer) > 0, NULL);
+
+  // if a copy function exists, use it, else copy the bytes
+  if (buffer->copy != NULL) {
+    newbuf = (buffer->copy)(buffer);
+  } else {
+    // allocate a new buffer
+    newbuf = gst_buffer_new();
+
+    // copy the absolute size
+    newbuf->size = buffer->size;
+    // allocate space for the copy
+    newbuf->data = (guchar *)g_malloc (buffer->size);
+    // copy the data straight across
+    memcpy(newbuf->data,buffer->data,buffer->size);
+    // the new maxsize is the same as the size, since we just malloc'd it
+    newbuf->maxsize = newbuf->size;
+  }
+  newbuf->offset = buffer->offset;
+  newbuf->timestamp = buffer->timestamp;
+  newbuf->maxage = buffer->maxage;
+
+  // since we just created a new buffer, so we have no ties to old stuff
+  newbuf->parent = NULL;
+  newbuf->pool = NULL;
+
+  return newbuf;
+}
+
+/*
+ * gst_buffer_is_span_fast
+ * @buf1: first source buffer
+ * @buf2: second source buffer
+ *
+ * Determines whether a gst_buffer_span is free, or requires a memcpy. 
+ *
+ * Returns: TRUE if the buffers are contiguous, FALSE if a copy would be required.
+ */
+gboolean
+gst_buffer_is_span_fast (GstBuffer *buf1, GstBuffer *buf2)
+{
+  g_return_val_if_fail (GST_BUFFER_REFCOUNT(buf1) > 0, FALSE);
+  g_return_val_if_fail (GST_BUFFER_REFCOUNT(buf2) > 0, FALSE);
+
+  return (buf1->parent && buf2->parent && 
+         (buf1->parent == buf2->parent) &&
+          ((buf1->data + buf1->size) == buf2->data));
+}
+
+
+/**
+ * gst_buffer_span:
+ * @buf1: first source buffer to merge
+ * @offset: offset in first buffer to start new buffer
+ * @buf2: second source buffer to merge
+ * @len: length of new buffer
+ *
+ * Create a new buffer that consists of part of buf1 and buf2.
+ * Logically, buf1 and buf2 are concatenated into a single larger
+ * buffer, and a new buffer is created at the given offset inside
+ * this space, with a given length.
+ *
+ * If the two source buffers are children of the same larger buffer,
+ * and are contiguous, the new buffer will be a child of the shared
+ * parent, and thus no copying is necessary.
+ *
+ * Returns: new buffer that spans the two source buffers
+ */
+// FIXME need to think about CoW and such...
+GstBuffer *
+gst_buffer_span (GstBuffer *buf1, guint32 offset, GstBuffer *buf2, guint32 len)
+{
+  GstBuffer *newbuf;
+
+  g_return_val_if_fail (GST_BUFFER_REFCOUNT(buf1) > 0, NULL);
+  g_return_val_if_fail (GST_BUFFER_REFCOUNT(buf2) > 0, NULL);
+
+  // make sure buf1 has a lower address than buf2
+  if (buf1->data > buf2->data) {
+    GstBuffer *tmp = buf1;
+    g_print ("swapping buffers\n");
+    buf1 = buf2;
+    buf2 = tmp;
+  }
+
+  // if the two buffers have the same parent and are adjacent
+  if (gst_buffer_is_span_fast(buf1,buf2)) {
+    // we simply create a subbuffer of the common parent
+    newbuf = gst_buffer_create_sub (buf1->parent, buf1->data - (buf1->parent->data) + offset, len);
+  }
+  else {
+    g_print ("slow path taken in buffer_span\n");
+    // otherwise we simply have to brute-force copy the buffers
+    newbuf = gst_buffer_new ();
+
+    // put in new size
+    newbuf->size = len;
+    // allocate space for the copy
+    newbuf->data = (guchar *)g_malloc(len);
+    // copy the first buffer's data across
+    memcpy(newbuf->data, buf1->data + offset, buf1->size - offset);
+    // copy the second buffer's data across
+    memcpy(newbuf->data + (buf1->size - offset), buf2->data, len - (buf1->size - offset));
+
+    if (newbuf->offset != -1)
+      newbuf->offset = buf1->offset + offset;
+    newbuf->timestamp = buf1->timestamp;
+    if (buf2->maxage > buf1->maxage) newbuf->maxage = buf2->maxage;
+    else newbuf->maxage = buf1->maxage;
+
+  }
+
+  return newbuf;
+}
+
+
+/**
+ * gst_buffer_merge:
+ * @buf1: first source buffer to merge
+ * @buf2: second source buffer to merge
+ *
+ * Create a new buffer that is the concatenation of the two source
+ * buffers.  The original source buffers will not be modified or
+ * unref'd.
+ *
+ * Internally is nothing more than a specialized gst_buffer_span,
+ * so the same optimizations can occur.
+ *
+ * Returns: new buffer that's the concatenation of the source buffers
+ */
+GstBuffer *
+gst_buffer_merge (GstBuffer *buf1, GstBuffer *buf2)
+{
+  // we're just a specific case of the more general gst_buffer_span()
+  return gst_buffer_span (buf1, 0, buf2, buf1->size + buf2->size);
+}
diff --git a/tests/bufspeed/gstbuffer.h b/tests/bufspeed/gstbuffer.h
new file mode 100644 (file)
index 0000000..4c78d78
--- /dev/null
@@ -0,0 +1,171 @@
+/* GStreamer
+ * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
+ *                    2000 Wim Taymans <wtay@chello.be>
+ *
+ * gstbuffer.h: Header for GstBuffer object
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+#ifndef __GST_BUFFER_H__
+#define __GST_BUFFER_H__
+
+//
+// Define this to add file:line info to each GstBuffer showing
+// the location in the source code where the buffer was created.
+// 
+// #define GST_BUFFER_WHERE
+//
+// Then in gdb, you can `call gst_buffer_print_live()' to get a list
+// of allocated GstBuffers and also the file:line where they were
+// allocated.
+//
+
+#include <gst/gstdata.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_ATOMIC_H
+#include <asm/atomic.h>
+#endif
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+extern GType _gst_buffer_type;
+
+#define GST_TYPE_BUFFER                (_gst_buffer_type)
+#define GST_BUFFER(buf)        ((GstBuffer *)(buf))
+#define GST_IS_BUFFER(buf)     (GST_DATA_TYPE(buf) == GST_TYPE_BUFFER)
+
+#define GST_BUFFER_FLAGS(buf)                  (GST_BUFFER(buf)->flags)
+#define GST_BUFFER_FLAG_IS_SET(buf,flag)       (GST_BUFFER_FLAGS(buf) & (1<<(flag)))
+#define GST_BUFFER_FLAG_SET(buf,flag)          G_STMT_START{ (GST_BUFFER_FLAGS(buf) |= (1<<(flag))); }G_STMT_END
+#define GST_BUFFER_FLAG_UNSET(buf,flag)        G_STMT_START{ (GST_BUFFER_FLAGS(buf) &= ~(1<<(flag))); }G_STMT_END
+
+
+#define GST_BUFFER_DATA(buf)           (GST_BUFFER(buf)->data)
+#define GST_BUFFER_SIZE(buf)           (GST_BUFFER(buf)->size)
+#define GST_BUFFER_OFFSET(buf)         (GST_BUFFER(buf)->offset)
+#define GST_BUFFER_MAXSIZE(buf)                (GST_BUFFER(buf)->maxsize)
+#define GST_BUFFER_TIMESTAMP(buf)      (GST_BUFFER(buf)->timestamp)
+#define GST_BUFFER_MAXAGE(buf)         (GST_BUFFER(buf)->maxage)
+#define GST_BUFFER_BUFFERPOOL(buf)     (GST_BUFFER(buf)->pool)
+#define GST_BUFFER_PARENT(buf)         (GST_BUFFER(buf)->parent)
+#define GST_BUFFER_POOL_PRIVATE(buf)   (GST_BUFFER(buf)->pool_private)
+#define GST_BUFFER_COPY_FUNC(buf)      (GST_BUFFER(buf)->copy)
+#define GST_BUFFER_FREE_FUNC(buf)      (GST_BUFFER(buf)->free)
+
+
+#define GST_BUFFER_LOCK(buf)   (g_mutex_lock(GST_BUFFER(buf)->lock))
+#define GST_BUFFER_TRYLOCK(buf)        (g_mutex_trylock(GST_BUFFER(buf)->lock))
+#define GST_BUFFER_UNLOCK(buf) (g_mutex_unlock(GST_BUFFER(buf)->lock))
+
+
+typedef enum {
+  GST_BUFFER_READONLY,
+  GST_BUFFER_ORIGINAL,
+  GST_BUFFER_DONTFREE,
+
+} GstBufferFlags;
+
+
+typedef struct _GstBuffer GstBuffer;
+
+
+typedef void       (*GstBufferFreeFunc)        (GstBuffer *buf);
+typedef GstBuffer *(*GstBufferCopyFunc)        (GstBuffer *srcbuf);
+
+
+#include <gst/gstbufferpool.h>
+
+struct _GstBuffer {
+  GstData              data_type;
+
+  /* locking */
+  GMutex               *lock;
+
+  /* refcounting */
+#ifdef HAVE_ATOMIC_H
+  atomic_t             refcount;
+#define GST_BUFFER_REFCOUNT(buf)       (atomic_read(&(GST_BUFFER((buf))->refcount)))
+#else
+  int                  refcount;
+#define GST_BUFFER_REFCOUNT(buf)       (GST_BUFFER(buf)->refcount)
+#endif
+
+  /* flags */
+  guint16              flags;
+
+  /* pointer to data, its size, and offset in original source if known */
+  guchar               *data;
+  guint32              size;
+  guint32              maxsize;
+  guint32              offset;
+
+  /* timestamp */
+  gint64               timestamp;
+  gint64               maxage;
+
+  /* subbuffer support, who's my parent? */
+  GstBuffer            *parent;
+
+  /* this is a pointer to the buffer pool (if any) */
+  GstBufferPool        *pool;
+  gpointer             pool_private;
+
+  /* utility function pointers */
+  GstBufferFreeFunc    free;           // free the data associated with the buffer
+  GstBufferCopyFunc    copy;           // copy the data from one buffer to another
+};
+
+/* initialisation */
+void           _gst_buffer_initialize          (void);
+/* creating a new buffer from scratch */
+GstBuffer*     gst_buffer_new                  (void);
+GstBuffer*     gst_buffer_new_from_pool        (GstBufferPool *pool, guint32 offset, guint32 size);
+
+/* creating a subbuffer */
+GstBuffer*     gst_buffer_create_sub           (GstBuffer *parent, guint32 offset, guint32 size);
+
+/* refcounting */
+void           gst_buffer_ref                  (GstBuffer *buffer);
+void           gst_buffer_unref                (GstBuffer *buffer);
+
+/* destroying the buffer */
+void           gst_buffer_destroy              (GstBuffer *buffer);
+
+/* copy buffer */
+GstBuffer*     gst_buffer_copy                 (GstBuffer *buffer);
+
+/* merge, span, or append two buffers, intelligently */
+GstBuffer*     gst_buffer_merge                (GstBuffer *buf1, GstBuffer *buf2);
+GstBuffer*     gst_buffer_span                 (GstBuffer *buf1,guint32 offset,GstBuffer *buf2,guint32 len);
+GstBuffer*     gst_buffer_append               (GstBuffer *buf, GstBuffer *buf2);
+
+gboolean       gst_buffer_is_span_fast         (GstBuffer *buf1, GstBuffer *buf2);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+
+#endif /* __GST_BUFFER_H__ */
diff --git a/tests/bufspeed/gstmempool.c b/tests/bufspeed/gstmempool.c
new file mode 100644 (file)
index 0000000..c9f956e
--- /dev/null
@@ -0,0 +1,191 @@
+#include "gstmempool.h"
+
+#ifdef __SMP__
+#define POOL_LOCK "lock ; "
+#else
+#define POOL_LOCK ""
+#endif
+
+#define GST_MEM_POOL_AREA(pool)        (((GstMemPoolElement*)(pool))->area)
+#define GST_MEM_POOL_DATA(pool)        ((gpointer)(((GstMemPoolElement*)(pool)) + 1))
+#define GST_MEM_POOL_LINK(mem)                 ((GstMemPoolElement*)((guint8*)(mem) - sizeof (GstMemPoolElement)))
+
+#define USE_ASM
+
+/*******************************************************
+ *         area size
+ * +-----------------------------------------+
+ *   pool size
+ * +------------+
+ *
+ * !next!data... !next!data.... !next!data...
+ *  !             ^ !            ^ !
+ *  +-------------+ +------------+ +---> NULL
+ *
+ */
+static gboolean
+populate (GstMemPool *mem_pool) 
+{
+  guint8 *area;
+  gint i;
+
+  if (mem_pool->cleanup)
+    return FALSE;
+
+  area = (guint8 *) g_malloc (mem_pool->area_size);
+
+  for (i=0; i < mem_pool->area_size; i += mem_pool->pool_size) { 
+    guint8 *areap = area + i;
+
+    GST_MEM_POOL_AREA (areap) = (GstMemPoolElement *)area;
+
+    if (mem_pool->alloc_func) {
+      mem_pool->alloc_func (mem_pool, GST_MEM_POOL_DATA (areap));
+    }
+
+    gst_mem_pool_free (mem_pool, GST_MEM_POOL_DATA (areap));
+  }
+
+  return TRUE;
+}
+
+
+GstMemPool*
+gst_mem_pool_new (gchar* name, gint atom_size, gulong area_size, gint type, 
+                 GstMemPoolAllocFunc alloc_func,
+                 GstMemPoolFreeFunc free_func)
+{
+  GstMemPool *mem_pool;
+
+  g_return_val_if_fail (atom_size > 0, NULL);
+  g_return_val_if_fail (area_size >= atom_size, NULL);
+
+  mem_pool = g_malloc (sizeof (GstMemPool));
+
+  mem_pool->pool_size = atom_size + sizeof (GstMemPoolElement);
+  area_size = (area_size/atom_size) * mem_pool->pool_size;
+
+  mem_pool->name = g_strdup (name);
+  mem_pool->free = NULL;
+  mem_pool->cnt = 0;
+  mem_pool->atom_size = atom_size;
+  mem_pool->area_size = area_size;
+  mem_pool->cleanup = FALSE;
+  mem_pool->alloc_func = alloc_func;
+  mem_pool->free_func = free_func;
+  mem_pool->chunk_lock = g_mutex_new ();
+
+  populate (mem_pool);
+
+  return mem_pool;
+}
+
+static gboolean
+free_area (gpointer key, gpointer value, gpointer user_data)
+{
+  g_print ("free %p\n", key);
+  g_free (key);
+
+  return TRUE;
+}
+
+void
+gst_mem_pool_destroy (GstMemPool *mem_pool) 
+{
+  GHashTable *elements = g_hash_table_new (NULL, NULL);
+  gpointer data;
+
+  mem_pool->cleanup = TRUE;
+
+  data = gst_mem_pool_alloc (mem_pool);
+  while (data) {
+    GstMemPoolElement *elem = GST_MEM_POOL_LINK (data);
+
+    g_hash_table_insert (elements, GST_MEM_POOL_AREA (elem), NULL); 
+    
+    data = gst_mem_pool_alloc (mem_pool);
+  } 
+  g_hash_table_foreach_remove (elements, free_area, NULL);
+
+  g_hash_table_destroy (elements);
+  g_free (mem_pool->name);
+  g_free (mem_pool);
+}
+
+gpointer
+gst_mem_pool_alloc (GstMemPool *mem_pool)
+{
+  GstMemPoolElement *pool = NULL;
+  
+  g_return_val_if_fail (mem_pool != NULL, NULL);
+
+again:
+#ifdef USE_ASM
+  __asm__ __volatile__ ("  testl %%eax, %%eax          \n\t"
+                       "  jz 20f                       \n"
+                       "10:                            \t"
+                       "  movl (%%eax), %%ebx          \n\t"
+                       "  movl %%edx, %%ecx            \n\t"
+                       "  incl %%ecx                   \n\t"
+                       POOL_LOCK "cmpxchg8b %1         \n\t"
+                       "  jz 20f                       \n\t"
+                       "  testl %%eax, %%eax           \n\t"
+                       "  jnz 10b                      \n"
+                       "20:\t"
+                       :"=a" (pool)
+                       :"m" (*mem_pool), "a" (mem_pool->free), "d" (mem_pool->cnt)
+                       :"ecx", "ebx");
+#else
+  g_mutex_lock (mem_pool->chunk_lock);
+  if (mem_pool->free) {
+    pool = mem_pool->free;
+    mem_pool->free = pool->link;
+  }
+  g_mutex_unlock (mem_pool->chunk_lock);
+#endif
+
+  if (!pool) {
+    //g_print ("extending\n");
+    if (populate (mem_pool))
+      goto again;
+    else 
+      return NULL;
+  }
+  return GST_MEM_POOL_DATA (pool);
+}
+
+gpointer
+gst_mem_pool_alloc0 (GstMemPool *mem_pool)
+{
+  gpointer mem = gst_mem_pool_alloc (mem_pool);
+
+  if (mem)
+    memset (mem, 0, mem_pool->atom_size);
+  
+  return mem;
+}
+
+void
+gst_mem_pool_free (GstMemPool *mem_pool, gpointer mem)
+{
+  GstMemPoolElement *pool;
+  
+  g_return_if_fail (mem_pool != NULL);
+  g_return_if_fail (mem != NULL);
+
+  pool = GST_MEM_POOL_LINK (mem);
+
+#ifdef USE_ASM
+  __asm__ __volatile__ ( "1:                           \t"
+                       "  movl %2, (%1)                \n"
+                       POOL_LOCK "cmpxchg %1, %0       \n\t"
+                       "  jnz 1b                       \n\t"
+                       :
+                       :"m" (*mem_pool), "r" (pool), "a" (mem_pool->free));
+#else
+  g_mutex_lock (mem_pool->chunk_lock);
+  pool->link = mem_pool->free;
+  mem_pool->free = pool;
+  g_mutex_unlock (mem_pool->chunk_lock);
+#endif
+}
diff --git a/tests/bufspeed/gstmempool.h b/tests/bufspeed/gstmempool.h
new file mode 100644 (file)
index 0000000..307b768
--- /dev/null
@@ -0,0 +1,43 @@
+#include <glib.h>
+
+typedef struct _GstMemPool GstMemPool;
+typedef struct _GstMemPoolElement GstMemPoolElement;
+
+typedef void (*GstMemPoolAllocFunc) (GstMemPool *pool, gpointer data);
+typedef void (*GstMemPoolFreeFunc)  (GstMemPool *pool, gpointer data);
+
+struct _GstMemPoolElement
+{
+  GstMemPoolElement *link;             /* next cell in the lifo */
+  GstMemPoolElement *area;
+};
+
+struct _GstMemPool
+{
+  volatile GstMemPoolElement *free;    /* the first free element */
+  volatile gulong cnt;                 /* used to avoid ABA problem */
+
+  gchar *name;
+  gulong area_size;
+  gulong pool_size;
+  gulong atom_size;
+  gboolean cleanup;
+  GstMemPoolAllocFunc alloc_func;
+  GstMemPoolFreeFunc free_func;
+  GMutex *chunk_lock;
+};
+
+
+GstMemPool*    gst_mem_pool_new                (gchar *name,
+                                                gint atom_size,
+                                                gulong area_size,
+                                                gint type,
+                                                GstMemPoolAllocFunc alloc_func,
+                                                GstMemPoolFreeFunc free_func);
+
+void           gst_mem_pool_destroy            (GstMemPool *mem_pool);
+
+gpointer       gst_mem_pool_alloc              (GstMemPool *mem_pool);
+gpointer       gst_mem_pool_alloc0             (GstMemPool *mem_pool);
+void           gst_mem_pool_free               (GstMemPool *mem_pool,
+                                                gpointer mem);
diff --git a/tests/bufspeed/test1.c b/tests/bufspeed/test1.c
new file mode 100644 (file)
index 0000000..951af46
--- /dev/null
@@ -0,0 +1,19 @@
+#include "gstbuffer.h"
+
+int 
+main (int argc, char *argv[])
+{
+  GstBuffer *buf;
+  guint i;
+  
+  g_thread_init (NULL);
+  gtk_init (&argc, &argv);
+  _gst_buffer_initialize ();
+
+  for (i=0; i<5000000; i++) {
+    buf = gst_buffer_new ();
+    gst_buffer_unref (buf);
+  }
+
+  return 0;
+}
diff --git a/tests/bufspeed/test2.c b/tests/bufspeed/test2.c
new file mode 100644 (file)
index 0000000..616ad1f
--- /dev/null
@@ -0,0 +1,19 @@
+#include <gst/gst.h>
+
+int 
+main (int argc, char *argv[])
+{
+  GstBuffer *buf;
+  guint i;
+  
+  g_thread_init (NULL);
+  gtk_init (&argc, &argv);
+  _gst_buffer_initialize ();
+
+  for (i=0; i<5000000; i++) {
+    buf = gst_buffer_new ();
+    gst_buffer_unref (buf);
+  }
+
+  return 0;
+}
diff --git a/tests/memchunk/Makefile.am b/tests/memchunk/Makefile.am
new file mode 100644 (file)
index 0000000..fdc2c40
--- /dev/null
@@ -0,0 +1,7 @@
+noinst_PROGRAMS = gmemchunktest gstmemchunktest
+
+gmemchunktest_SOURCES = gmemchunktest.c
+gstmemchunktest_SOURCES = gstmemchunktest.c gstmemchunk.c gstmemchunk.h
+
+LIBS = $(GST_LIBS)
+CFLAGS = $(GLIB_CFLAGS) $(XML_CFLAGS) $(GST_CFLAGS)
diff --git a/tests/memchunk/gmemchunktest.c b/tests/memchunk/gmemchunktest.c
new file mode 100644 (file)
index 0000000..b3301f1
--- /dev/null
@@ -0,0 +1,78 @@
+#include <gst/gst.h>
+
+#define MAX_THREADS  100
+
+static GMemChunk *_chunks;
+static GMutex *_lock;
+
+static gint num_allocs;
+static gint num_threads;
+
+static gpointer 
+alloc_chunk (void)
+{
+  gpointer ret;
+  g_mutex_lock (_lock); 
+  ret = g_mem_chunk_alloc (_chunks);
+  g_mutex_unlock (_lock); 
+
+  return ret;
+}
+
+static void 
+free_chunk (gpointer chunk)
+{
+  g_mutex_lock (_lock); 
+  g_mem_chunk_free (_chunks, chunk);
+  g_mutex_unlock (_lock); 
+}
+
+
+void*
+run_test (void *threadid)
+{
+  gint i;
+  gpointer chunk;
+  sleep(1);
+
+  for (i = 0; i<num_allocs; i++) {
+    chunk = alloc_chunk ();
+    free_chunk (chunk);
+  }
+
+  pthread_exit(NULL);
+}
+
+
+gint 
+main (gint argc, gchar *argv[]) 
+{
+  pthread_t threads[MAX_THREADS];
+  int rc, t;
+  gst_init (&argc, &argv);
+
+  if (argc != 3) {
+    g_print ("usage: %s <num_threads> <num_allocs>\n", argv[0]);
+    exit (-1);
+  }
+
+  num_threads = atoi (argv[1]);
+  num_allocs = atoi (argv[2]);
+
+  _chunks = g_mem_chunk_new ("test", 32, 32 * 16, G_ALLOC_AND_FREE);
+  _lock = g_mutex_new ();
+
+  for(t=0; t < num_threads; t++) {
+    rc = pthread_create (&threads[t], NULL, run_test, (void *)t);
+    if (rc) {
+      printf ("ERROR: return code from pthread_create() is %d\n", rc);
+      printf ("Code %d= %s\n", rc, strerror(rc));
+      exit (-1);
+    }
+  }
+  printf ("main(): Created %d threads.\n", t);
+
+  pthread_exit (NULL);
+  g_mem_chunk_info();
+}
diff --git a/tests/memchunk/gstmemchunk.c b/tests/memchunk/gstmemchunk.c
new file mode 100644 (file)
index 0000000..be99be5
--- /dev/null
@@ -0,0 +1,162 @@
+#include "gstmemchunk.h"
+
+#ifdef __SMP__
+#define CHUNK_LOCK "lock ; "
+#else
+#define CHUNK_LOCK ""
+#endif
+
+#define GST_MEM_CHUNK_AREA(chunk)      (((GstMemChunkElement*)(chunk))->area)
+#define GST_MEM_CHUNK_DATA(chunk)      ((gpointer)(((GstMemChunkElement*)(chunk)) + 1))
+#define GST_MEM_CHUNK_LINK(mem)        ((GstMemChunkElement*)((guint8*)(mem) - sizeof (GstMemChunkElement)))
+
+/*******************************************************
+ *         area size
+ * +-----------------------------------------+
+ *   chunk size
+ * +------------+
+ *
+ * !next!data... !next!data.... !next!data...
+ *  !             ^ !            ^ !
+ *  +-------------+ +------------+ +---> NULL
+ *
+ */
+static gboolean
+populate (GstMemChunk *mem_chunk) 
+{
+  guint8 *area;
+  gint i;
+
+  if (mem_chunk->cleanup)
+    return FALSE;
+
+  area = (guint8 *) g_malloc (mem_chunk->area_size);
+  g_print ("alloc %p\n", area);
+
+  for (i=0; i < mem_chunk->area_size; i += mem_chunk->chunk_size) { 
+    GST_MEM_CHUNK_AREA (area + i) = (GstMemChunkElement *)area;
+    gst_mem_chunk_free (mem_chunk, GST_MEM_CHUNK_DATA (area + i));
+  }
+
+  return TRUE;
+}
+
+
+GstMemChunk*
+gst_mem_chunk_new (gchar* name, gint atom_size, gulong area_size, gint type)
+{
+  GstMemChunk *mem_chunk;
+
+  g_return_val_if_fail (atom_size > 0, NULL);
+  g_return_val_if_fail (area_size >= atom_size, NULL);
+
+  mem_chunk = g_malloc (sizeof (GstMemChunk));
+
+  mem_chunk->chunk_size = atom_size + sizeof (GstMemChunkElement);
+  area_size = (area_size/atom_size) * mem_chunk->chunk_size;
+
+  mem_chunk->name = g_strdup (name);
+  mem_chunk->free = NULL;
+  mem_chunk->cnt = 0;
+  mem_chunk->atom_size = atom_size;
+  mem_chunk->area_size = area_size;
+  mem_chunk->cleanup = FALSE;
+
+  populate (mem_chunk);
+
+  return mem_chunk;
+}
+
+static gboolean
+free_area (gpointer key, gpointer value, gpointer user_data)
+{
+  g_print ("free %p\n", key);
+  g_free (key);
+
+  return TRUE;
+}
+
+void
+gst_mem_chunk_destroy (GstMemChunk *mem_chunk) 
+{
+  GHashTable *elements = g_hash_table_new (NULL, NULL);
+  gpointer data;
+
+  mem_chunk->cleanup = TRUE;
+
+  data = gst_mem_chunk_alloc (mem_chunk);
+  while (data) {
+    GstMemChunkElement *elem = GST_MEM_CHUNK_LINK (data);
+
+    g_hash_table_insert (elements, GST_MEM_CHUNK_AREA (elem), NULL); 
+    
+    data = gst_mem_chunk_alloc (mem_chunk);
+  } 
+  g_hash_table_foreach_remove (elements, free_area, NULL);
+
+  g_hash_table_destroy (elements);
+  g_free (mem_chunk->name);
+  g_free (mem_chunk);
+}
+
+gpointer
+gst_mem_chunk_alloc (GstMemChunk *mem_chunk)
+{
+  GstMemChunkElement *chunk = NULL;
+  
+  g_return_val_if_fail (mem_chunk != NULL, NULL);
+
+again:
+  __asm__ __volatile__ ("  testl %%eax, %%eax          \n\t"
+                       "  jz 20f                       \n"
+                       "10:                            \t"
+                       "  movl (%%eax), %%ebx          \n\t"
+                       "  movl %%edx, %%ecx            \n\t"
+                       "  incl %%ecx                   \n\t"
+                       CHUNK_LOCK "cmpxchg8b %1        \n\t"
+                       "  jz 20f                       \n\t"
+                       "  testl %%eax, %%eax           \n\t"
+                       "  jnz 10b                      \n"
+                       "20:\t"
+                       :"=a" (chunk)
+                       :"m" (*mem_chunk), "a" (mem_chunk->free), "d" (mem_chunk->cnt)
+                       :"ecx", "ebx");
+
+  if (!chunk) {
+    //g_print ("extending\n");
+    if (populate (mem_chunk))
+      goto again;
+    else 
+      return NULL;
+  }
+  return GST_MEM_CHUNK_DATA (chunk);
+}
+
+gpointer
+gst_mem_chunk_alloc0 (GstMemChunk *mem_chunk)
+{
+  gpointer mem = gst_mem_chunk_alloc (mem_chunk);
+
+  if (mem)
+    memset (mem, 0, mem_chunk->atom_size);
+  
+  return mem;
+}
+
+void
+gst_mem_chunk_free (GstMemChunk *mem_chunk, gpointer mem)
+{
+  GstMemChunkElement *chunk;
+  
+  g_return_if_fail (mem_chunk != NULL);
+  g_return_if_fail (mem != NULL);
+
+  chunk = GST_MEM_CHUNK_LINK (mem);
+
+  __asm__ __volatile__ ( "1:                           \t"
+                       "  movl %2, (%1)                \n"
+                       CHUNK_LOCK "cmpxchg %1, %0      \n\t"
+                       "  jnz 1b                       \n\t"
+                       :
+                       :"m" (*mem_chunk), "r" (chunk), "a" (mem_chunk->free));
+}
diff --git a/tests/memchunk/gstmemchunk.h b/tests/memchunk/gstmemchunk.h
new file mode 100644 (file)
index 0000000..c71660a
--- /dev/null
@@ -0,0 +1,34 @@
+
+#include <gst/gst.h>
+
+typedef struct _GstMemChunk GstMemChunk;
+typedef struct _GstMemChunkElement GstMemChunkElement;
+
+struct _GstMemChunkElement
+{
+  GstMemChunkElement *link;            /* next cell in the lifo */
+  GstMemChunkElement *area;
+};
+
+struct _GstMemChunk
+{
+  volatile GstMemChunkElement *free;   /* the first free element */
+  volatile gulong cnt;                 /* used to avoid ABA problem */
+
+  gchar *name;
+  gulong area_size;
+  gulong chunk_size;
+  gulong atom_size;
+  gboolean cleanup;
+};
+
+GstMemChunk*   gst_mem_chunk_new       (gchar *name,
+                                        gint atom_size,
+                                        gulong area_size,
+                                        gint type);
+
+void           gst_mem_chunk_destroy   (GstMemChunk *mem_chunk);
+
+gpointer       gst_mem_chunk_alloc     (GstMemChunk *mem_chunk);
+void           gst_mem_chunk_free      (GstMemChunk *mem_chunk,
+                                        gpointer mem);
diff --git a/tests/memchunk/gstmemchunktest.c b/tests/memchunk/gstmemchunktest.c
new file mode 100644 (file)
index 0000000..05acccb
--- /dev/null
@@ -0,0 +1,79 @@
+#include <gst/gst.h>
+#include "gstmemchunk.h"
+
+#define MAX_THREADS  100
+
+static GstMemChunk *_chunks;
+
+static gint num_allocs;
+static gint num_threads;
+
+static gpointer 
+alloc_chunk (void)
+{
+  gpointer ret;
+  ret = gst_mem_chunk_alloc (_chunks);
+
+  return ret;
+}
+
+static void 
+free_chunk (gpointer chunk)
+{
+  gst_mem_chunk_free (_chunks, chunk);
+}
+
+
+void*
+run_test (void *threadid)
+{
+  gint i;
+  gpointer chunk;
+  sleep(1);
+
+  for (i = 0; i<num_allocs; i++) {
+    chunk = alloc_chunk ();
+    free_chunk (chunk);
+  }
+
+  pthread_exit(NULL);
+}
+
+
+gint 
+main (gint argc, gchar *argv[]) 
+{
+  pthread_t threads[MAX_THREADS];
+  int rc, t;
+  gst_init (&argc, &argv);
+
+  if (argc != 3) {
+    g_print ("usage: %s <num_threads> <num_allocs>\n", argv[0]);
+    exit (-1);
+  }
+
+  num_threads = atoi (argv[1]);
+  num_allocs = atoi (argv[2]);
+
+  _chunks = gst_mem_chunk_new ("test", 32, 32 * 16, G_ALLOC_AND_FREE);
+
+  for(t=0; t < num_threads; t++) {
+    rc = pthread_create (&threads[t], NULL, run_test, (void *)t);
+    if (rc) {
+      printf ("ERROR: return code from pthread_create() is %d\n", rc);
+      printf ("Code %d= %s\n", rc, strerror(rc));
+      exit (-1);
+    }
+  }
+  printf ("main(): Created %d threads.\n", t);
+
+  for(t=0; t < num_threads; t++) {
+    pthread_join (threads[t], NULL);
+  }
+  g_mem_chunk_info();
+
+  gst_mem_chunk_destroy (_chunks);
+
+  return 0;
+}