From 74b6b732f14e8d4565e150dfb163db0bddaa158d Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Mon, 8 Jul 2002 19:12:38 +0000 Subject: [PATCH] - Added a bunch of fast atomic operations on x86 with C fallbacks Original commit message from CVS: - Added a bunch of fast atomic operations on x86 with C fallbacks - Added the code for refcounting in GstData - Added a gstmemchunk allocator using atomic operations --- gst/gstatomic.h | 213 ++++++++++++++++++++++++++++++++++++++++++ gst/gstdata.c | 165 ++++++++++++++++++++++++++++++++ gst/gstdata_private.h | 36 +++++++ gst/gstmemchunk.c | 159 +++++++++++++++++++++++++++++++ gst/gstmemchunk.h | 67 +++++++++++++ 5 files changed, 640 insertions(+) create mode 100644 gst/gstatomic.h create mode 100644 gst/gstdata.c create mode 100644 gst/gstdata_private.h create mode 100644 gst/gstmemchunk.c create mode 100644 gst/gstmemchunk.h diff --git a/gst/gstatomic.h b/gst/gstatomic.h new file mode 100644 index 0000000000..db1c4c213f --- /dev/null +++ b/gst/gstatomic.h @@ -0,0 +1,213 @@ +/* GStreamer + * Copyright (C) <1999> Erik Walthinsen + * + * 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_ATOMIC_H__ +#define __GST_ATOMIC_H__ + +/* FIXME */ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef HAVE_ATOMIC_H +# include +#endif + +#include + +G_BEGIN_DECLS + +typedef struct _GstAtomicInt GstAtomicInt; + +struct _GstAtomicInt { +#ifdef HAVE_ATOMIC_H + atomic_t value; +#else + int value; + GMutex *lock; +#endif +}; + +#ifdef HAVE_ATOMIC_H + +/* atomic functions */ +#define GST_ATOMIC_INT_INIT(ref, val) (atomic_set(&((ref)->value), (val))) +#define GST_ATOMIC_INT_FREE(ref) + +#define GST_ATOMIC_INT_SET(ref,val) (atomic_set(&((ref)->value), (val))) +#define GST_ATOMIC_INT_VALUE(ref) (atomic_read(&((ref)->value))) +#define GST_ATOMIC_INT_READ(ref,res) (*res = atomic_read(&((ref)->value))) +#define GST_ATOMIC_INT_INC(ref) (atomic_inc (&((ref)->value))) +#define GST_ATOMIC_INT_DEC_AND_TEST(ref,zero) (*zero = atomic_dec_and_test (&((ref)->value))) +#define GST_ATOMIC_INT_ADD(ref, count) (atomic_add ((count), &((ref)->value))) + +#else + +/* fallback using a lock */ +#define GST_ATOMIC_INT_INIT(ref, val) \ +G_STMT_START { \ + (ref)->value = (val); \ + (ref)->lock = g_mutex_new(); \ +} G_STMT_END + +#define GST_ATOMIC_INT_FREE(ref) g_mutex_free (&(ref)->lock) + +#define GST_ATOMIC_INT_SET(ref,val) \ +G_STMT_START { \ + g_mutex_lock ((ref)->lock); \ + (ref)->value = (val); \ + g_mutex_unlock ((ref)->lock); \ +} G_STMT_END + +#define GST_ATOMIC_INT_VALUE(ref) ((ref)->value) +#define GST_ATOMIC_INT_READ(ref,res) \ +G_STMT_START { \ + g_mutex_lock ((ref)->lock); \ + *res = (ref)->value; \ + g_mutex_unlock ((ref)->lock); \ +} G_STMT_END + +#define GST_ATOMIC_INT_INC(ref) \ +G_STMT_START { \ + g_mutex_lock ((ref)->lock); \ + (ref)->value++; \ + g_mutex_unlock ((ref)->lock); \ +} G_STMT_END + +#define GST_ATOMIC_INT_DEC_AND_TEST(ref,zero) \ +G_STMT_START { \ + g_mutex_lock ((ref)->lock); \ + (ref)->value--; \ + *zero = ((ref)->value == 0); \ + g_mutex_unlock ((ref)->lock); \ +} G_STMT_END + +#define GST_ATOMIC_INT_ADD(ref, count) \ +G_STMT_START { \ + g_mutex_lock ((ref)->lock); \ + (ref)->value += count; \ + g_mutex_unlock ((ref)->lock); \ +} G_STMT_END + +#endif /* HAVE_ATOMIC_H */ + +typedef struct _GstAtomicSwap GstAtomicSwap; + +#define GST_ATOMIC_SWAP_VALUE(swap) ((swap)->value) + +struct _GstAtomicSwap { + volatile gpointer value; + volatile gulong cnt; /* for the ABA problem */ + GMutex *lock; /* lock for C fallback */ +}; + +#if defined (__i386__) && defined (__GNUC__) && __GNUC__ >= 2 + +# ifdef __SMP__ +# define GST_ATOMIC_LOCK "lock ; " +# else +# define GST_ATOMIC_LOCK "" +# endif + +#define _GST_ATOMIC_SWAP_INIT(swap,val) \ +G_STMT_START { \ + (swap)->value = (gpointer)(val); \ + (swap)->cnt = 0; \ +} G_STMT_END + +#define _GST_ATOMIC_SWAP(swap, val) \ +G_STMT_START { \ + __asm__ __volatile__ ("1:" \ + " movl %2, (%1);" \ + GST_ATOMIC_LOCK "cmpxchg %1, %0;" \ + " jnz 1b;" \ + : \ + : "m" (*swap), \ + "r" (val), \ + "a" ((swap)->value)); \ +} G_STMT_END + +#define _GST_ATOMIC_SWAP_GET(swap, val, res) \ +G_STMT_START { \ + __asm__ __volatile__ (" testl %%eax, %%eax;" \ + " jz 20f;" \ + "10:" \ + " movl (%%eax), %%ebx;" \ + " movl %%edx, %%ecx;" \ + " incl %%ecx;" \ + GST_ATOMIC_LOCK "cmpxchg8b %1;" \ + " jz 20f;" \ + " testl %%eax, %%eax;" \ + " jnz 10b;" \ + "20:\t" \ + : "=a" (*res) \ + : "m" (*(swap)), \ + "a" (val), \ + "d" ((swap)->cnt) \ + : "ecx", "ebx"); \ +} G_STMT_END + +#else + +#define _GST_ATOMIC_SWAP_INIT(swap,val) \ +G_STMT_START { \ + (swap)->lock = g_mutex_new(); \ + (swap)->value = (gpointer)val; \ +} G_STMT_END + +#define _GST_ATOMIC_SWAP(swap, val) \ +G_STMT_START { \ + gpointer tmp; \ + g_mutex_lock ((swap)->lock); \ + tmp = (swap)->value; \ + (swap)->value = val; \ + ((gpointer)*val) = tmp; \ + g_mutex_unlock ((swap)->lock); \ +} G_STMT_END + +#define _GST_ATOMIC_SWAP_GET(swap, val, res) \ +G_STMT_START { \ + if (val) { \ + gpointer tmp; \ + gint *tmp2; /* this is pretty EVIL */ \ + g_mutex_lock ((swap)->lock); \ + tmp = (swap)->value; \ + tmp2 = val; \ + (swap)->value = (gpointer)*tmp2; \ + (*res) = (gpointer)*tmp2 = tmp; \ + g_mutex_unlock ((swap)->lock); \ + } \ +} G_STMT_END +#endif + +/* initialize the swap structure with an initial value */ +#define GST_ATOMIC_SWAP_INIT(swap,val) _GST_ATOMIC_SWAP_INIT(swap, val) + +/* atomically swap the contents the swap value with the value pointed to + * by val. */ +#define GST_ATOMIC_SWAP(swap, val) _GST_ATOMIC_SWAP(swap, val) + +/* atomically swap the contents the swap value with the value pointed to + * by val. The new value of the swap value is returned in the memory pointed + * to by res */ +#define GST_ATOMIC_SWAP_GET(swap,val,res) _GST_ATOMIC_SWAP_GET(swap, val, res) + +G_END_DECLS + +#endif /* __GST_ATOMIC_H__ */ diff --git a/gst/gstdata.c b/gst/gstdata.c new file mode 100644 index 0000000000..ddb1a08fac --- /dev/null +++ b/gst/gstdata.c @@ -0,0 +1,165 @@ +/* GStreamer + * Copyright (C) 1999,2000 Erik Walthinsen + * 2000 Wim Taymans + * + * gstdata.c: Data 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_private.h" + +#include "gstdata.h" +#include "gstdata_private.h" +#include "gstlog.h" + +void +_gst_data_init (GstData *data, GType type, guint16 flags, GstDataFreeFunction free, GstDataCopyFunction copy) +{ + _GST_DATA_INIT (data, type, flags, free, copy); +} + +void +_gst_data_free (GstData *data) +{ + _GST_DATA_DISPOSE (data); + g_free (data); +} + +/** + * gst_data_copy: + * @data: a #GstData to copy + * + * Copies the given #GstData + * + * Returns: a copy of the data or NULL if the data cannot be copied. + */ +GstData* +gst_data_copy (const GstData *data) +{ + if (data->copy) + return data->copy (data); + + return NULL; +} + +/** + * gst_data_copy_on_write: + * @data: a #GstData to copy + * + * Copies the given #GstData if the refcount is greater than 1 so that the + * #GstData object can be written to safely. + * + * Returns: a copy of the data if the refcount is > 1, data if the refcount == 1 + * or NULL if the data could not be copied. + */ +GstData* +gst_data_copy_on_write (const GstData *data) +{ + gint refcount; + + GST_ATOMIC_INT_READ (&data->refcount, &refcount); + + if (refcount == 1) + return GST_DATA (data); + + if (data->copy) + return data->copy (data); + + return NULL; +} + +/** + * gst_data_free: + * @data: a #GstData to free + * + * Frees the given #GstData + */ +void +gst_data_free (GstData *data) +{ + if (data->free) + data->free (data); +} + +/** + * gst_data_ref: + * @data: a #GstData to reference + * + * Increments the reference count of this data. + * + * Returns: the data + */ +GstData* +gst_data_ref (GstData *data) +{ + g_return_val_if_fail (data != NULL, NULL); + g_return_val_if_fail (GST_DATA_REFCOUNT_VALUE(data) > 0, NULL); + + GST_ATOMIC_INT_INC (&data->refcount); + + return data; +} + +/** + * gst_data_ref_by_count: + * @data: a #GstData to reference + * @count: the number to increment the reference count by + * + * Increments the reference count of this data by the given number. + * + * Returns: the data + */ +GstData* +gst_data_ref_by_count (GstData *data, gint count) +{ + g_return_val_if_fail (data != NULL, NULL); + g_return_val_if_fail (count >= 0, NULL); + g_return_val_if_fail (GST_DATA_REFCOUNT_VALUE(data) > 0, NULL); + + GST_ATOMIC_INT_ADD (&data->refcount, count); + + return data; +} + +/** + * gst_data_unref: + * @data: a #GstData to unreference + * + * Decrements the refcount of this data. If the refcount is + * zero, the data will be freeed. + */ +void +gst_data_unref (GstData *data) +{ + gint zero; + + g_return_if_fail (data != NULL); + + GST_INFO (GST_CAT_BUFFER, "unref data %p, current count is %d", data,GST_DATA_REFCOUNT_VALUE(data)); + g_return_if_fail (GST_DATA_REFCOUNT_VALUE(data) > 0); + + GST_ATOMIC_INT_DEC_AND_TEST (&data->refcount, &zero); + + /* if we ended up with the refcount at zero, free the data */ + if (zero) { + if (data->free) + data->free (data); + } +} + diff --git a/gst/gstdata_private.h b/gst/gstdata_private.h new file mode 100644 index 0000000000..36932c6e59 --- /dev/null +++ b/gst/gstdata_private.h @@ -0,0 +1,36 @@ +/* GStreamer + * Copyright (C) 1999,2000 Erik Walthinsen + * 2000 Wim Taymans + * + * gstdata.c: Data 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. + */ + +#define _GST_DATA_INIT(data, ptype, pflags, pfree, pcopy) \ +G_STMT_START { \ + GST_ATOMIC_INT_INIT (&(data)->refcount, 1); \ + (data)->type = ptype; \ + (data)->flags = pflags; \ + (data)->free = pfree; \ + (data)->copy = pcopy; \ +} G_STMT_END; + +#define _GST_DATA_DISPOSE(data) \ +G_STMT_START { \ + GST_ATOMIC_INT_FREE (&(data)->refcount); \ +} G_STMT_END; + diff --git a/gst/gstmemchunk.c b/gst/gstmemchunk.c new file mode 100644 index 0000000000..be65f37182 --- /dev/null +++ b/gst/gstmemchunk.c @@ -0,0 +1,159 @@ +/* GStreamer + * Copyright (C) <1999> Erik Walthinsen + * + * 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. + */ + +#include /* memset */ + +#include "gstlog.h" +#include "gstmemchunk.h" +#include "gstutils.h" + +#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_malloc0 (mem_chunk->area_size); + + 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->atom_size = atom_size; + mem_chunk->area_size = area_size; + mem_chunk->cleanup = FALSE; + GST_ATOMIC_SWAP_INIT (&mem_chunk->swap, NULL); + + populate (mem_chunk); + + return mem_chunk; +} + +static gboolean +free_area (gpointer key, gpointer value, gpointer user_data) +{ + 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: + GST_ATOMIC_SWAP_GET (&mem_chunk->swap, + GST_ATOMIC_SWAP_VALUE (&mem_chunk->swap), + &chunk); + + if (!chunk) { + 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); + + GST_ATOMIC_SWAP (&mem_chunk->swap, &chunk->link); +} diff --git a/gst/gstmemchunk.h b/gst/gstmemchunk.h new file mode 100644 index 0000000000..e668e55df0 --- /dev/null +++ b/gst/gstmemchunk.h @@ -0,0 +1,67 @@ +/* GStreamer + * Copyright (C) <1999> Erik Walthinsen + * + * 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_MEM_CHUNK_H__ +#define __GST_MEM_CHUNK_H__ + +#include +#include + +G_BEGIN_DECLS + +typedef struct _GstMemChunk GstMemChunk; +typedef struct _GstMemChunkElement GstMemChunkElement; + +struct _GstMemChunkElement +{ + GstMemChunkElement *link; /* next cell in the lifo */ + GstMemChunkElement *area; +}; + +struct _GstMemChunk +{ +#if 0 + volatile GstMemChunkElement *free; /* the first free element */ + volatile gulong cnt; /* used to avoid ABA problem */ +#endif + GstAtomicSwap swap; + + gchar *name; + gulong area_size; + gulong chunk_size; + gulong atom_size; + gboolean cleanup; + GMutex *lock; +}; + +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); +gpointer gst_mem_chunk_alloc0 (GstMemChunk *mem_chunk); +void gst_mem_chunk_free (GstMemChunk *mem_chunk, + gpointer mem); + +G_END_DECLS + +#endif /* __GST_MEM_CHUNK_H__ */