mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-22 09:41:07 +00:00
put everything in tests/
Original commit message from CVS: put everything in tests/
This commit is contained in:
parent
e1bc025265
commit
1a4c7726aa
15 changed files with 1317 additions and 16 deletions
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
6
tests/bufspeed/Makefile.am
Normal file
6
tests/bufspeed/Makefile.am
Normal file
|
@ -0,0 +1,6 @@
|
|||
noinst_PROGRAMS = test1 test2
|
||||
|
||||
test1_SOURCES = test1.c gstbuffer.c gstmempool.c
|
||||
|
||||
#LIBS += $(GST_LIBS)
|
||||
CFLAGS += $(GST_CFLAGS)
|
6
tests/bufspeed/README
Normal file
6
tests/bufspeed/README
Normal file
|
@ -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
|
495
tests/bufspeed/gstbuffer.c
Normal file
495
tests/bufspeed/gstbuffer.c
Normal file
|
@ -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);
|
||||
}
|
171
tests/bufspeed/gstbuffer.h
Normal file
171
tests/bufspeed/gstbuffer.h
Normal file
|
@ -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__ */
|
191
tests/bufspeed/gstmempool.c
Normal file
191
tests/bufspeed/gstmempool.c
Normal file
|
@ -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
|
||||
}
|
43
tests/bufspeed/gstmempool.h
Normal file
43
tests/bufspeed/gstmempool.h
Normal file
|
@ -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);
|
19
tests/bufspeed/test1.c
Normal file
19
tests/bufspeed/test1.c
Normal file
|
@ -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;
|
||||
}
|
19
tests/bufspeed/test2.c
Normal file
19
tests/bufspeed/test2.c
Normal file
|
@ -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;
|
||||
}
|
7
tests/memchunk/Makefile.am
Normal file
7
tests/memchunk/Makefile.am
Normal file
|
@ -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)
|
78
tests/memchunk/gmemchunktest.c
Normal file
78
tests/memchunk/gmemchunktest.c
Normal file
|
@ -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();
|
||||
}
|
162
tests/memchunk/gstmemchunk.c
Normal file
162
tests/memchunk/gstmemchunk.c
Normal file
|
@ -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));
|
||||
}
|
34
tests/memchunk/gstmemchunk.h
Normal file
34
tests/memchunk/gstmemchunk.h
Normal file
|
@ -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);
|
79
tests/memchunk/gstmemchunktest.c
Normal file
79
tests/memchunk/gstmemchunktest.c
Normal file
|
@ -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;
|
||||
}
|
Loading…
Reference in a new issue