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.1 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, see <http://www.gnu.org/licenses/>.
22 #include "gatomicarray.h"
24 /* A GAtomicArray is a growable, mutable array of data
25 * generally of the form of a header of a specific size and
26 * then a array of items of a fixed size.
28 * It is possible to do lock-less read transactions from the
29 * array without any protection against other reads or writes,
30 * but such read operation must be aware that the data in the
31 * atomic array can change at any time during the transaction,
32 * and only at the end can we verify if the transaction succeeded
33 * or not. Thus the reading transaction cannot for instance
34 * dereference a pointer in the array inside the transaction.
36 * The size of an array however cannot change during a read
39 * Writes to the array is done in a copy-update style, but there
40 * is no real protection against multiple writers overwriting each
41 * others updates, so writes must be protected by an external lock.
44 G_LOCK_DEFINE_STATIC (array
);
46 typedef struct _FreeListNode FreeListNode
;
47 struct _FreeListNode
{
51 /* This is really a list of array memory blocks, using the
52 * first item as the next pointer to chain them together.
53 * Protected by array lock */
54 static FreeListNode
*freelist
= NULL
;
56 /* must hold array lock */
58 freelist_alloc (gsize size
, gboolean reuse
)
61 FreeListNode
*free
, **prev
;
66 for (free
= freelist
, prev
= &freelist
; free
!= NULL
; prev
= &free
->next
, free
= free
->next
)
68 if (G_ATOMIC_ARRAY_DATA_SIZE (free
) == size
)
71 return (gpointer
)free
;
76 real_size
= sizeof (gsize
) + MAX (size
, sizeof (FreeListNode
));
77 mem
= g_slice_alloc (real_size
);
78 mem
= ((char *) mem
) + sizeof (gsize
);
79 G_ATOMIC_ARRAY_DATA_SIZE (mem
) = size
;
83 /* must hold array lock */
85 freelist_free (gpointer mem
)
90 free
->next
= freelist
;
95 _g_atomic_array_init (GAtomicArray
*array
)
100 /* Get a copy of the data (if non-NULL) that
101 * can be changed and then re-applied with
102 * g_atomic_array_update().
104 * If additional_element_size is > 0 then
105 * then the new memory chunk is that much
106 * larger, or there were no data we return
107 * a chunk of header_size + additional_element_size.
108 * This means you can use this to grow the
109 * array part and it handles the first element
110 * being added automatically.
112 * We don't support shrinking arrays, as if
113 * we then re-grow we may reuse an old pointer
114 * value and confuse the transaction check.
117 _g_atomic_array_copy (GAtomicArray
*array
,
119 gsize additional_element_size
)
122 gsize old_size
, new_size
;
125 old
= g_atomic_pointer_get (&array
->data
);
128 old_size
= G_ATOMIC_ARRAY_DATA_SIZE (old
);
129 new_size
= old_size
+ additional_element_size
;
130 /* Don't reuse if copying to same size, as this may end
131 up reusing the same pointer for the same array thus
132 confusing the transaction check */
133 new = freelist_alloc (new_size
, additional_element_size
!= 0);
134 memcpy (new, old
, old_size
);
136 else if (additional_element_size
!= 0)
138 new_size
= header_size
+ additional_element_size
;
139 new = freelist_alloc (new_size
, TRUE
);
147 /* Replace the data in the array with the new data,
148 * freeing the old data (for reuse). The new data may
149 * not be smaller than the current data.
152 _g_atomic_array_update (GAtomicArray
*array
,
158 old
= g_atomic_pointer_get (&array
->data
);
160 g_assert (old
== NULL
|| G_ATOMIC_ARRAY_DATA_SIZE (old
) <= G_ATOMIC_ARRAY_DATA_SIZE (new_data
));
162 g_atomic_pointer_set (&array
->data
, new_data
);