Don't do pointer arithmetics on void*
[platform/upstream/glib.git] / gobject / gatomicarray.c
1 /* GObject - GLib Type, Object, Parameter and Signal Library
2  * Copyright (C) 2009 Benjamin Otte <otte@gnome.org>
3  *
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.
8  *
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.
13  *
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.
18  */
19
20 #include "config.h"
21
22 #include "gatomicarray.h"
23
24 #include <string.h>
25
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.
29  *
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.
37  *
38  * The size of an array however cannot change during a read
39  * transaction.
40  *
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.
44  */
45
46 G_LOCK_DEFINE_STATIC (array);
47
48 typedef struct _FreeListNode FreeListNode;
49 struct _FreeListNode {
50   FreeListNode *next;
51 };
52
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;
57
58 /* must hold array lock */
59 static gpointer
60 freelist_alloc (gsize size, gboolean reuse)
61 {
62   gpointer mem;
63   FreeListNode *free, **prev;
64   gsize real_size;
65
66   if (reuse)
67     {
68       for (free = freelist, prev = &freelist; free != NULL; prev = &free->next, free = free->next)
69         {
70           if (G_ATOMIC_ARRAY_DATA_SIZE (free) == size)
71             {
72               *prev = free->next;
73               return (gpointer)free;
74             }
75         }
76     }
77
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;
83 }
84
85 /* must hold array lock */
86 static void
87 freelist_free (gpointer mem)
88 {
89   FreeListNode *free;
90
91   free = mem;
92   free->next = freelist;
93   freelist = free;
94 }
95
96 void
97 _g_atomic_array_init (GAtomicArray *array)
98 {
99   array->data = NULL;
100 }
101
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().
105  *
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.
113  */
114 gpointer
115 _g_atomic_array_copy (GAtomicArray *array,
116                       gsize header_size,
117                       gsize additional_element_size)
118 {
119   guint8 *new, *old;
120   gsize old_size, new_size;
121
122   /* We don't support shrinking arrays, as if
123      we then re-grow we may reuse an old pointer
124      value and confuse the transaction check. */
125   g_assert (additional_element_size >= 0);
126
127   G_LOCK (array);
128   old = g_atomic_pointer_get (&array->data);
129   if (old)
130     {
131       old_size = G_ATOMIC_ARRAY_DATA_SIZE (old);
132       new_size = old_size + additional_element_size;
133       /* Don't reuse if copying to same size, as this may end
134          up reusing the same pointer for the same array thus
135          confusing the transaction check */
136       new = freelist_alloc (new_size, additional_element_size != 0);
137       memcpy (new, old, old_size);
138     }
139   else if (additional_element_size != 0)
140     {
141       new_size = header_size + additional_element_size;
142       new = freelist_alloc (new_size, TRUE);
143     }
144   else
145     new = NULL;
146   G_UNLOCK (array);
147   return new;
148 }
149
150 /* Replace the data in the array with the new data,
151  * freeing the old data (for reuse). The new data may
152  * not be smaller than the current data.
153  */
154 void
155 _g_atomic_array_update (GAtomicArray *array,
156                         gpointer new_data)
157 {
158   guint8 *old;
159
160   G_LOCK (array);
161   old = g_atomic_pointer_get (&array->data);
162
163   g_assert (old == NULL || G_ATOMIC_ARRAY_DATA_SIZE (old) <= G_ATOMIC_ARRAY_DATA_SIZE (new_data));
164
165   g_atomic_pointer_set (&array->data, new_data);
166   if (old)
167     freelist_free (old);
168   G_UNLOCK (array);
169 }