glib/tests: Fix non-debug build of slice test
[glib.git] / gobject / gatomicarray.c
blob77966f2eb4d2a064bd6a5e9ec10928e3c97ddde9
1 /* GObject - GLib Type, Object, Parameter and Signal Library
2 * Copyright (C) 2009 Benjamin Otte <otte@gnome.org>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General
15 * Public License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
17 * Boston, MA 02111-1307, USA.
20 #include "config.h"
22 #include <string.h>
24 #include "gatomicarray.h"
26 /* A GAtomicArray is a growable, mutable array of data
27 * generally of the form of a header of a specific size and
28 * then a array of items of a fixed size.
30 * It is possible to do lock-less read transactions from the
31 * array without any protection against other reads or writes,
32 * but such read operation must be aware that the data in the
33 * atomic array can change at any time during the transaction,
34 * and only at the end can we verify if the transaction succeeded
35 * or not. Thus the reading transaction cannot for instance
36 * dereference a pointer in the array inside the transaction.
38 * The size of an array however cannot change during a read
39 * transaction.
41 * Writes to the array is done in a copy-update style, but there
42 * is no real protection against multiple writers overwriting each
43 * others updates, so writes must be protected by an external lock.
46 G_LOCK_DEFINE_STATIC (array);
48 typedef struct _FreeListNode FreeListNode;
49 struct _FreeListNode {
50 FreeListNode *next;
53 /* This is really a list of array memory blocks, using the
54 * first item as the next pointer to chain them together.
55 * Protected by array lock */
56 static FreeListNode *freelist = NULL;
58 /* must hold array lock */
59 static gpointer
60 freelist_alloc (gsize size, gboolean reuse)
62 gpointer mem;
63 FreeListNode *free, **prev;
64 gsize real_size;
66 if (reuse)
68 for (free = freelist, prev = &freelist; free != NULL; prev = &free->next, free = free->next)
70 if (G_ATOMIC_ARRAY_DATA_SIZE (free) == size)
72 *prev = free->next;
73 return (gpointer)free;
78 real_size = sizeof (gsize) + MAX (size, sizeof (FreeListNode));
79 mem = g_slice_alloc (real_size);
80 mem = ((char *) mem) + sizeof (gsize);
81 G_ATOMIC_ARRAY_DATA_SIZE (mem) = size;
82 return mem;
85 /* must hold array lock */
86 static void
87 freelist_free (gpointer mem)
89 FreeListNode *free;
91 free = mem;
92 free->next = freelist;
93 freelist = free;
96 void
97 _g_atomic_array_init (GAtomicArray *array)
99 array->data = NULL;
102 /* Get a copy of the data (if non-NULL) that
103 * can be changed and then re-applied with
104 * g_atomic_array_update().
106 * If additional_element_size is > 0 then
107 * then the new memory chunk is that much
108 * larger, or there were no data we return
109 * a chunk of header_size + additional_element_size.
110 * This means you can use this to grow the
111 * array part and it handles the first element
112 * being added automatically.
114 * We don't support shrinking arrays, as if
115 * we then re-grow we may reuse an old pointer
116 * value and confuse the transaction check.
118 gpointer
119 _g_atomic_array_copy (GAtomicArray *array,
120 gsize header_size,
121 gsize additional_element_size)
123 guint8 *new, *old;
124 gsize old_size, new_size;
126 G_LOCK (array);
127 old = g_atomic_pointer_get (&array->data);
128 if (old)
130 old_size = G_ATOMIC_ARRAY_DATA_SIZE (old);
131 new_size = old_size + additional_element_size;
132 /* Don't reuse if copying to same size, as this may end
133 up reusing the same pointer for the same array thus
134 confusing the transaction check */
135 new = freelist_alloc (new_size, additional_element_size != 0);
136 memcpy (new, old, old_size);
138 else if (additional_element_size != 0)
140 new_size = header_size + additional_element_size;
141 new = freelist_alloc (new_size, TRUE);
143 else
144 new = NULL;
145 G_UNLOCK (array);
146 return new;
149 /* Replace the data in the array with the new data,
150 * freeing the old data (for reuse). The new data may
151 * not be smaller than the current data.
153 void
154 _g_atomic_array_update (GAtomicArray *array,
155 gpointer new_data)
157 guint8 *old;
159 G_LOCK (array);
160 old = g_atomic_pointer_get (&array->data);
162 g_assert (old == NULL || G_ATOMIC_ARRAY_DATA_SIZE (old) <= G_ATOMIC_ARRAY_DATA_SIZE (new_data));
164 g_atomic_pointer_set (&array->data, new_data);
165 if (old)
166 freelist_free (old);
167 G_UNLOCK (array);