- Added a bunch of fast atomic operations on x86 with C fallbacks
authorWim Taymans <wim.taymans@gmail.com>
Mon, 8 Jul 2002 19:12:38 +0000 (19:12 +0000)
committerWim Taymans <wim.taymans@gmail.com>
Mon, 8 Jul 2002 19:12:38 +0000 (19:12 +0000)
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 [new file with mode: 0644]
gst/gstdata.c [new file with mode: 0644]
gst/gstdata_private.h [new file with mode: 0644]
gst/gstmemchunk.c [new file with mode: 0644]
gst/gstmemchunk.h [new file with mode: 0644]

diff --git a/gst/gstatomic.h b/gst/gstatomic.h
new file mode 100644 (file)
index 0000000..db1c4c2
--- /dev/null
@@ -0,0 +1,213 @@
+/* GStreamer
+ * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
+ *
+ * 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 <asm/atomic.h>
+#endif
+
+#include <glib.h>
+
+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 (file)
index 0000000..ddb1a08
--- /dev/null
@@ -0,0 +1,165 @@
+/* GStreamer
+ * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
+ *                    2000 Wim Taymans <wtay@chello.be>
+ *
+ * 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 (file)
index 0000000..36932c6
--- /dev/null
@@ -0,0 +1,36 @@
+/* GStreamer
+ * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
+ *                    2000 Wim Taymans <wtay@chello.be>
+ *
+ * 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 (file)
index 0000000..be65f37
--- /dev/null
@@ -0,0 +1,159 @@
+/* GStreamer
+ * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
+ *
+ * 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 <string.h>            /* 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 (file)
index 0000000..e668e55
--- /dev/null
@@ -0,0 +1,67 @@
+/* GStreamer
+ * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
+ *
+ * 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 <glib.h>
+#include <gst/gstatomic.h>
+
+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__ */