gkdbus: Fix underflow and unreachable code bug
[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  * SPDX-License-Identifier: LGPL-2.1-or-later
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General
17  * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #include "config.h"
21
22 #include "../glib/gvalgrind.h"
23 #include <string.h>
24
25 #include "gatomicarray.h"
26
27 /* A GAtomicArray is a growable, mutable array of data
28  * generally of the form of a header of a specific size and
29  * then an array of items of a fixed size.
30  *
31  * It is possible to do lock-less read transactions from the
32  * array without any protection against other reads or writes,
33  * but such read operation must be aware that the data in the
34  * atomic array can change at any time during the transaction,
35  * and only at the end can we verify if the transaction succeeded
36  * or not. Thus the reading transaction cannot for instance
37  * dereference a pointer in the array inside the transaction.
38  *
39  * The size of an array however cannot change during a read
40  * transaction.
41  *
42  * Writes to the array is done in a copy-update style, but there
43  * is no real protection against multiple writers overwriting each
44  * others updates, so writes must be protected by an external lock.
45  */
46
47 G_LOCK_DEFINE_STATIC (array);
48
49 typedef struct _FreeListNode FreeListNode;
50 struct _FreeListNode {
51   FreeListNode *next;
52 };
53
54 /* This is really a list of array memory blocks, using the
55  * first item as the next pointer to chain them together.
56  * Protected by array lock */
57 static FreeListNode *freelist = NULL;
58
59 /* must hold array lock */
60 static gpointer
61 freelist_alloc (gsize size, gboolean reuse)
62 {
63   gpointer mem;
64   FreeListNode *free, **prev;
65   gsize real_size;
66
67   if (reuse)
68     {
69       for (free = freelist, prev = &freelist; free != NULL; prev = &free->next, free = free->next)
70         {
71           if (G_ATOMIC_ARRAY_DATA_SIZE (free) == size)
72             {
73               *prev = free->next;
74               return (gpointer)free;
75             }
76         }
77     }
78
79   real_size = sizeof (GAtomicArrayMetadata) + MAX (size, sizeof (FreeListNode));
80   mem = g_slice_alloc (real_size);
81   mem = ((char *) mem) + sizeof (GAtomicArrayMetadata);
82   G_ATOMIC_ARRAY_DATA_SIZE (mem) = size;
83
84 #if ENABLE_VALGRIND
85   VALGRIND_MALLOCLIKE_BLOCK (mem, real_size - sizeof (GAtomicArrayMetadata),
86                              FALSE, FALSE);
87 #endif
88
89   return mem;
90 }
91
92 /* must hold array lock */
93 static void
94 freelist_free (gpointer mem)
95 {
96   FreeListNode *free;
97
98   free = mem;
99   free->next = freelist;
100   freelist = free;
101 }
102
103 void
104 _g_atomic_array_init (GAtomicArray *array)
105 {
106   array->data = NULL;
107 }
108
109 /* Get a copy of the data (if non-NULL) that
110  * can be changed and then re-applied with
111  * g_atomic_array_update().
112  *
113  * If additional_element_size is > 0 then
114  * then the new memory chunk is that much
115  * larger, or there were no data we return
116  * a chunk of header_size + additional_element_size.
117  * This means you can use this to grow the
118  * array part and it handles the first element
119  * being added automatically.
120  *
121  * We don't support shrinking arrays, as if
122  * we then re-grow we may reuse an old pointer
123  * value and confuse the transaction check.
124  */
125 gpointer
126 _g_atomic_array_copy (GAtomicArray *array,
127                       gsize header_size,
128                       gsize additional_element_size)
129 {
130   guint8 *new, *old;
131   gsize old_size, new_size;
132
133   G_LOCK (array);
134   old = g_atomic_pointer_get (&array->data);
135   if (old)
136     {
137       old_size = G_ATOMIC_ARRAY_DATA_SIZE (old);
138       new_size = old_size + additional_element_size;
139       /* Don't reuse if copying to same size, as this may end
140          up reusing the same pointer for the same array thus
141          confusing the transaction check */
142       new = freelist_alloc (new_size, additional_element_size != 0);
143       memcpy (new, old, old_size);
144     }
145   else if (additional_element_size != 0)
146     {
147       new_size = header_size + additional_element_size;
148       new = freelist_alloc (new_size, TRUE);
149     }
150   else
151     new = NULL;
152   G_UNLOCK (array);
153   return new;
154 }
155
156 /* Replace the data in the array with the new data,
157  * freeing the old data (for reuse). The new data may
158  * not be smaller than the current data.
159  */
160 void
161 _g_atomic_array_update (GAtomicArray *array,
162                         gpointer new_data)
163 {
164   guint8 *old;
165
166   G_LOCK (array);
167   old = g_atomic_pointer_exchange (&array->data, new_data);
168
169 #ifdef G_DISABLE_ASSERT
170   if (old && G_ATOMIC_ARRAY_DATA_SIZE (new_data) < G_ATOMIC_ARRAY_DATA_SIZE (old))
171     {
172       g_atomic_pointer_set (&array->data, old);
173       g_return_if_reached ();
174     }
175 #else
176   g_assert (old == NULL || G_ATOMIC_ARRAY_DATA_SIZE (old) <= G_ATOMIC_ARRAY_DATA_SIZE (new_data));
177 #endif
178
179   if (old)
180     freelist_free (old);
181   G_UNLOCK (array);
182 }