Blame gobject/gatomicarray.c

Packit Service d3d246
/* GObject - GLib Type, Object, Parameter and Signal Library
Packit Service d3d246
 * Copyright (C) 2009 Benjamin Otte <otte@gnome.org>
Packit Service d3d246
 *
Packit Service d3d246
 * This library is free software; you can redistribute it and/or
Packit Service d3d246
 * modify it under the terms of the GNU Lesser General Public
Packit Service d3d246
 * License as published by the Free Software Foundation; either
Packit Service d3d246
 * version 2.1 of the License, or (at your option) any later version.
Packit Service d3d246
 *
Packit Service d3d246
 * This library is distributed in the hope that it will be useful,
Packit Service d3d246
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service d3d246
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit Service d3d246
 * Lesser General Public License for more details.
Packit Service d3d246
 *
Packit Service d3d246
 * You should have received a copy of the GNU Lesser General
Packit Service d3d246
 * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
Packit Service d3d246
 */
Packit Service d3d246
Packit Service d3d246
#include "config.h"
Packit Service d3d246
Packit Service d3d246
#include <string.h>
Packit Service d3d246
Packit Service d3d246
#include "gatomicarray.h"
Packit Service d3d246
Packit Service d3d246
/* A GAtomicArray is a growable, mutable array of data
Packit Service d3d246
 * generally of the form of a header of a specific size and
Packit Service d3d246
 * then a array of items of a fixed size.
Packit Service d3d246
 *
Packit Service d3d246
 * It is possible to do lock-less read transactions from the
Packit Service d3d246
 * array without any protection against other reads or writes,
Packit Service d3d246
 * but such read operation must be aware that the data in the
Packit Service d3d246
 * atomic array can change at any time during the transaction,
Packit Service d3d246
 * and only at the end can we verify if the transaction succeeded
Packit Service d3d246
 * or not. Thus the reading transaction cannot for instance
Packit Service d3d246
 * dereference a pointer in the array inside the transaction.
Packit Service d3d246
 *
Packit Service d3d246
 * The size of an array however cannot change during a read
Packit Service d3d246
 * transaction.
Packit Service d3d246
 *
Packit Service d3d246
 * Writes to the array is done in a copy-update style, but there
Packit Service d3d246
 * is no real protection against multiple writers overwriting each
Packit Service d3d246
 * others updates, so writes must be protected by an external lock.
Packit Service d3d246
 */
Packit Service d3d246
Packit Service d3d246
G_LOCK_DEFINE_STATIC (array);
Packit Service d3d246
Packit Service d3d246
typedef struct _FreeListNode FreeListNode;
Packit Service d3d246
struct _FreeListNode {
Packit Service d3d246
  FreeListNode *next;
Packit Service d3d246
};
Packit Service d3d246
Packit Service d3d246
/* This is really a list of array memory blocks, using the
Packit Service d3d246
 * first item as the next pointer to chain them together.
Packit Service d3d246
 * Protected by array lock */
Packit Service d3d246
static FreeListNode *freelist = NULL;
Packit Service d3d246
Packit Service d3d246
/* must hold array lock */
Packit Service d3d246
static gpointer
Packit Service d3d246
freelist_alloc (gsize size, gboolean reuse)
Packit Service d3d246
{
Packit Service d3d246
  gpointer mem;
Packit Service d3d246
  FreeListNode *free, **prev;
Packit Service d3d246
  gsize real_size;
Packit Service d3d246
Packit Service d3d246
  if (reuse)
Packit Service d3d246
    {
Packit Service d3d246
      for (free = freelist, prev = &freelist; free != NULL; prev = &free->next, free = free->next)
Packit Service d3d246
	{
Packit Service d3d246
	  if (G_ATOMIC_ARRAY_DATA_SIZE (free) == size)
Packit Service d3d246
	    {
Packit Service d3d246
	      *prev = free->next;
Packit Service d3d246
	      return (gpointer)free;
Packit Service d3d246
	    }
Packit Service d3d246
	}
Packit Service d3d246
    }
Packit Service d3d246
Packit Service d3d246
  real_size = sizeof (gsize) + MAX (size, sizeof (FreeListNode));
Packit Service d3d246
  mem = g_slice_alloc (real_size);
Packit Service d3d246
  mem = ((char *) mem) + sizeof (gsize);
Packit Service d3d246
  G_ATOMIC_ARRAY_DATA_SIZE (mem) = size;
Packit Service d3d246
  return mem;
Packit Service d3d246
}
Packit Service d3d246
Packit Service d3d246
/* must hold array lock */
Packit Service d3d246
static void
Packit Service d3d246
freelist_free (gpointer mem)
Packit Service d3d246
{
Packit Service d3d246
  FreeListNode *free;
Packit Service d3d246
Packit Service d3d246
  free = mem;
Packit Service d3d246
  free->next = freelist;
Packit Service d3d246
  freelist = free;
Packit Service d3d246
}
Packit Service d3d246
Packit Service d3d246
void
Packit Service d3d246
_g_atomic_array_init (GAtomicArray *array)
Packit Service d3d246
{
Packit Service d3d246
  array->data = NULL;
Packit Service d3d246
}
Packit Service d3d246
Packit Service d3d246
/* Get a copy of the data (if non-NULL) that
Packit Service d3d246
 * can be changed and then re-applied with
Packit Service d3d246
 * g_atomic_array_update().
Packit Service d3d246
 *
Packit Service d3d246
 * If additional_element_size is > 0 then
Packit Service d3d246
 * then the new memory chunk is that much
Packit Service d3d246
 * larger, or there were no data we return
Packit Service d3d246
 * a chunk of header_size + additional_element_size.
Packit Service d3d246
 * This means you can use this to grow the
Packit Service d3d246
 * array part and it handles the first element
Packit Service d3d246
 * being added automatically.
Packit Service d3d246
 *
Packit Service d3d246
 * We don't support shrinking arrays, as if
Packit Service d3d246
 * we then re-grow we may reuse an old pointer
Packit Service d3d246
 * value and confuse the transaction check.
Packit Service d3d246
 */
Packit Service d3d246
gpointer
Packit Service d3d246
_g_atomic_array_copy (GAtomicArray *array,
Packit Service d3d246
		      gsize header_size,
Packit Service d3d246
		      gsize additional_element_size)
Packit Service d3d246
{
Packit Service d3d246
  guint8 *new, *old;
Packit Service d3d246
  gsize old_size, new_size;
Packit Service d3d246
Packit Service d3d246
  G_LOCK (array);
Packit Service d3d246
  old = g_atomic_pointer_get (&array->data);
Packit Service d3d246
  if (old)
Packit Service d3d246
    {
Packit Service d3d246
      old_size = G_ATOMIC_ARRAY_DATA_SIZE (old);
Packit Service d3d246
      new_size = old_size + additional_element_size;
Packit Service d3d246
      /* Don't reuse if copying to same size, as this may end
Packit Service d3d246
	 up reusing the same pointer for the same array thus
Packit Service d3d246
	 confusing the transaction check */
Packit Service d3d246
      new = freelist_alloc (new_size, additional_element_size != 0);
Packit Service d3d246
      memcpy (new, old, old_size);
Packit Service d3d246
    }
Packit Service d3d246
  else if (additional_element_size != 0)
Packit Service d3d246
    {
Packit Service d3d246
      new_size = header_size + additional_element_size;
Packit Service d3d246
      new = freelist_alloc (new_size, TRUE);
Packit Service d3d246
    }
Packit Service d3d246
  else
Packit Service d3d246
    new = NULL;
Packit Service d3d246
  G_UNLOCK (array);
Packit Service d3d246
  return new;
Packit Service d3d246
}
Packit Service d3d246
Packit Service d3d246
/* Replace the data in the array with the new data,
Packit Service d3d246
 * freeing the old data (for reuse). The new data may
Packit Service d3d246
 * not be smaller than the current data.
Packit Service d3d246
 */
Packit Service d3d246
void
Packit Service d3d246
_g_atomic_array_update (GAtomicArray *array,
Packit Service d3d246
			gpointer new_data)
Packit Service d3d246
{
Packit Service d3d246
  guint8 *old;
Packit Service d3d246
Packit Service d3d246
  G_LOCK (array);
Packit Service d3d246
  old = g_atomic_pointer_get (&array->data);
Packit Service d3d246
Packit Service d3d246
  g_assert (old == NULL || G_ATOMIC_ARRAY_DATA_SIZE (old) <= G_ATOMIC_ARRAY_DATA_SIZE (new_data));
Packit Service d3d246
Packit Service d3d246
  g_atomic_pointer_set (&array->data, new_data);
Packit Service d3d246
  if (old)
Packit Service d3d246
    freelist_free (old);
Packit Service d3d246
  G_UNLOCK (array);
Packit Service d3d246
}