/* GObject - GLib Type, Object, Parameter and Signal Library * Copyright (C) 2009 Benjamin Otte * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser 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 "config.h" #include "gatomicarray.h" #include G_LOCK_DEFINE_STATIC (array); static GList *freelist; /* must hold array lock */ static gpointer freelist_alloc (gsize size, gboolean reuse) { GList *walk; gpointer mem; if (reuse) for (walk = freelist; walk; walk = walk->next) { mem = walk->data; if (ATOMIC_ARRAY_DATA_SIZE (mem) == size) { freelist = g_list_delete_link (freelist, walk); return mem; } } mem = g_slice_alloc (size + sizeof (gsize)); mem += sizeof (gsize); ATOMIC_ARRAY_DATA_SIZE (mem) = size; return mem; } /* must hold array lock */ static void freelist_free (gpointer mem) { freelist = g_list_prepend (freelist, mem); } void g_atomic_array_init (GAtomicArray *array) { array->data = NULL; } gpointer g_atomic_array_copy (GAtomicArray *array, gsize header_size, gsize additional_element_size) { guint8 *new, *old; gsize old_size, new_size; /* We don't support shrinking arrays, as if we then re-grow we may reuse an old pointer value and confuse the transaction check. */ g_assert (additional_element_size >= 0); G_LOCK (array); old = g_atomic_pointer_get (&array->data); if (old) { old_size = ATOMIC_ARRAY_DATA_SIZE (old); new_size = old_size + additional_element_size; /* Don't reuse if copying to same size, as this may end up reusing the same pointer for the same array thus confusing the transaction check */ new = freelist_alloc (new_size, additional_element_size != 0); memcpy (new, old, old_size); } else if (additional_element_size != 0) { new_size = header_size + additional_element_size; new = freelist_alloc (new_size, TRUE); } else new = NULL; G_UNLOCK (array); return new; } void g_atomic_array_update (GAtomicArray *array, gpointer new_data) { guint8 *old; G_LOCK (array); old = g_atomic_pointer_get (&array->data); g_atomic_pointer_set (&array->data, new_data); if (old) freelist_free (old); G_UNLOCK (array); }