Merge pull request #4630 from BrzVlad/feature-valloc-limit
[mono-project.git] / mono / utils / lock-free-array-queue.c
blob2e892357b63377ff98bb516d26f56c4f6d2138a2
1 /**
2 * \file
3 * A lock-free somewhat-queue that doesn't
4 * require hazard pointers.
6 * (C) Copyright 2011 Xamarin Inc.
7 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
8 */
11 * The queue is a linked list of arrays (chunks). Chunks are never
12 * removed from the list, only added to the end, in a lock-free manner.
14 * Adding or removing an entry in the queue is only possible at the
15 * end. To do so, the thread first has to increment or decrement
16 * q->num_used_entries. The entry thus added or removed now "belongs"
17 * to that thread. It first CASes the state to BUSY, writes/reads the
18 * entry data, and then sets the state to USED or FREE.
21 #include <string.h>
23 #include <mono/utils/atomic.h>
24 #include <mono/utils/mono-membar.h>
25 #ifdef SGEN_WITHOUT_MONO
26 #include <mono/sgen/sgen-gc.h>
27 #include <mono/sgen/sgen-client.h>
28 #else
29 #include <mono/utils/mono-mmap.h>
30 #endif
32 #include <mono/utils/lock-free-array-queue.h>
34 struct _MonoLockFreeArrayChunk {
35 MonoLockFreeArrayChunk *next;
36 gint32 num_entries;
37 char entries [MONO_ZERO_LEN_ARRAY];
40 typedef MonoLockFreeArrayChunk Chunk;
42 #define CHUNK_NTH(arr,chunk,index) ((chunk)->entries + (index) * (arr)->entry_size)
44 static Chunk*
45 alloc_chunk (MonoLockFreeArray *arr)
47 int size = mono_pagesize ();
48 int num_entries = (size - (sizeof (Chunk) - arr->entry_size * MONO_ZERO_LEN_ARRAY)) / arr->entry_size;
49 Chunk *chunk = (Chunk *) mono_valloc (NULL, size, MONO_MMAP_READ | MONO_MMAP_WRITE, arr->account_type);
50 g_assert (chunk);
51 chunk->num_entries = num_entries;
52 return chunk;
55 static void
56 free_chunk (Chunk *chunk, MonoMemAccountType type)
58 mono_vfree (chunk, mono_pagesize (), type);
61 gpointer
62 mono_lock_free_array_nth (MonoLockFreeArray *arr, int index)
64 Chunk *chunk;
66 g_assert (index >= 0);
68 if (!arr->chunk_list) {
69 chunk = alloc_chunk (arr);
70 mono_memory_write_barrier ();
71 if (InterlockedCompareExchangePointer ((volatile gpointer *)&arr->chunk_list, chunk, NULL) != NULL)
72 free_chunk (chunk, arr->account_type);
75 chunk = arr->chunk_list;
76 g_assert (chunk);
78 while (index >= chunk->num_entries) {
79 Chunk *next = chunk->next;
80 if (!next) {
81 next = alloc_chunk (arr);
82 mono_memory_write_barrier ();
83 if (InterlockedCompareExchangePointer ((volatile gpointer *) &chunk->next, next, NULL) != NULL) {
84 free_chunk (next, arr->account_type);
85 next = chunk->next;
86 g_assert (next);
89 index -= chunk->num_entries;
90 chunk = next;
93 return CHUNK_NTH (arr, chunk, index);
96 gpointer
97 mono_lock_free_array_iterate (MonoLockFreeArray *arr, MonoLockFreeArrayIterateFunc func, gpointer user_data)
99 Chunk *chunk;
100 for (chunk = arr->chunk_list; chunk; chunk = chunk->next) {
101 int i;
102 for (i = 0; i < chunk->num_entries; ++i) {
103 gpointer result = func (i, CHUNK_NTH (arr, chunk, i), user_data);
104 if (result)
105 return result;
108 return NULL;
111 void
112 mono_lock_free_array_cleanup (MonoLockFreeArray *arr)
114 Chunk *chunk;
116 chunk = arr->chunk_list;
117 arr->chunk_list = NULL;
118 while (chunk) {
119 Chunk *next = chunk->next;
120 free_chunk (chunk, arr->account_type);
121 chunk = next;
125 enum {
126 STATE_FREE,
127 STATE_USED,
128 STATE_BUSY
131 typedef struct {
132 gint32 state;
133 gpointer data [MONO_ZERO_LEN_ARRAY];
134 } Entry;
136 typedef MonoLockFreeArrayQueue Queue;
138 /* The queue's entry size, calculated from the array's. */
139 #define ENTRY_SIZE(q) ((q)->array.entry_size - sizeof (gpointer))
141 void
142 mono_lock_free_array_queue_push (MonoLockFreeArrayQueue *q, gpointer entry_data_ptr)
144 int index, num_used;
145 Entry *entry;
147 do {
148 index = InterlockedIncrement (&q->num_used_entries) - 1;
149 entry = (Entry *) mono_lock_free_array_nth (&q->array, index);
150 } while (InterlockedCompareExchange (&entry->state, STATE_BUSY, STATE_FREE) != STATE_FREE);
152 mono_memory_write_barrier ();
154 memcpy (entry->data, entry_data_ptr, ENTRY_SIZE (q));
156 mono_memory_write_barrier ();
158 entry->state = STATE_USED;
160 mono_memory_barrier ();
162 do {
163 num_used = q->num_used_entries;
164 if (num_used > index)
165 break;
166 } while (InterlockedCompareExchange (&q->num_used_entries, index + 1, num_used) != num_used);
168 mono_memory_write_barrier ();
171 gboolean
172 mono_lock_free_array_queue_pop (MonoLockFreeArrayQueue *q, gpointer entry_data_ptr)
174 int index;
175 Entry *entry;
177 do {
178 do {
179 index = q->num_used_entries;
180 if (index == 0)
181 return FALSE;
182 } while (InterlockedCompareExchange (&q->num_used_entries, index - 1, index) != index);
184 entry = (Entry *) mono_lock_free_array_nth (&q->array, index - 1);
185 } while (InterlockedCompareExchange (&entry->state, STATE_BUSY, STATE_USED) != STATE_USED);
187 /* Reading the item must happen before CASing the state. */
188 mono_memory_barrier ();
190 memcpy (entry_data_ptr, entry->data, ENTRY_SIZE (q));
192 mono_memory_barrier ();
194 entry->state = STATE_FREE;
196 mono_memory_write_barrier ();
198 return TRUE;
201 void
202 mono_lock_free_array_queue_cleanup (MonoLockFreeArrayQueue *q)
204 mono_lock_free_array_cleanup (&q->array);
205 q->num_used_entries = 0;