2 * hazard-pointer.c: Hazard pointer related code.
4 * (C) Copyright 2011 Novell, Inc
9 #include <mono/metadata/class-internals.h>
10 #include <mono/utils/hazard-pointer.h>
11 #include <mono/utils/mono-membar.h>
12 #include <mono/utils/mono-memory-model.h>
13 #include <mono/utils/mono-mmap.h>
14 #include <mono/utils/monobitset.h>
15 #include <mono/utils/mono-threads.h>
16 #include <mono/utils/lock-free-array-queue.h>
17 #include <mono/io-layer/io-layer.h>
21 MonoHazardousFreeFunc free_func;
24 /* The hazard table */
26 #define HAZARD_TABLE_MAX_SIZE 256
28 #define HAZARD_TABLE_MAX_SIZE 16384 /* There cannot be more threads than this number. */
31 static volatile int hazard_table_size = 0;
32 static MonoThreadHazardPointers * volatile hazard_table = NULL;
34 /* The table where we keep pointers to blocks to be freed but that
35 have to wait because they're guarded by a hazard pointer. */
36 static MonoLockFreeArrayQueue delayed_free_queue = MONO_LOCK_FREE_ARRAY_QUEUE_INIT (sizeof (DelayedFreeItem));
38 /* The table for small ID assignment */
39 static CRITICAL_SECTION small_id_mutex;
40 static int small_id_next;
41 static int highest_small_id = -1;
42 static MonoBitSet *small_id_table;
45 * Allocate a small thread id.
47 * FIXME: The biggest part of this function is very similar to
48 * domain_id_alloc() in domain.c and should be merged.
51 mono_thread_small_id_alloc (void)
55 EnterCriticalSection (&small_id_mutex);
58 small_id_table = mono_bitset_new (1, 0);
60 id = mono_bitset_find_first_unset (small_id_table, small_id_next);
62 id = mono_bitset_find_first_unset (small_id_table, -1);
65 MonoBitSet *new_table;
66 if (small_id_table->size * 2 >= (1 << 16))
67 g_assert_not_reached ();
68 new_table = mono_bitset_clone (small_id_table, small_id_table->size * 2);
69 id = mono_bitset_find_first_unset (new_table, small_id_table->size - 1);
71 mono_bitset_free (small_id_table);
72 small_id_table = new_table;
75 g_assert (!mono_bitset_test_fast (small_id_table, id));
76 mono_bitset_set_fast (small_id_table, id);
79 if (small_id_next >= small_id_table->size)
82 g_assert (id < HAZARD_TABLE_MAX_SIZE);
83 if (id >= hazard_table_size) {
85 hazard_table = g_malloc0 (sizeof (MonoThreadHazardPointers) * HAZARD_TABLE_MAX_SIZE);
86 hazard_table_size = HAZARD_TABLE_MAX_SIZE;
89 int pagesize = mono_pagesize ();
90 int num_pages = (hazard_table_size * sizeof (MonoThreadHazardPointers) + pagesize - 1) / pagesize;
92 if (hazard_table == NULL) {
93 hazard_table = mono_valloc (NULL,
94 sizeof (MonoThreadHazardPointers) * HAZARD_TABLE_MAX_SIZE,
98 g_assert (hazard_table != NULL);
99 page_addr = (guint8*)hazard_table + num_pages * pagesize;
101 mono_mprotect (page_addr, pagesize, MONO_MMAP_READ | MONO_MMAP_WRITE);
104 hazard_table_size = num_pages * pagesize / sizeof (MonoThreadHazardPointers);
107 g_assert (id < hazard_table_size);
108 for (i = 0; i < HAZARD_POINTER_COUNT; ++i)
109 hazard_table [id].hazard_pointers [i] = NULL;
112 if (id > highest_small_id) {
113 highest_small_id = id;
114 mono_memory_write_barrier ();
117 LeaveCriticalSection (&small_id_mutex);
123 mono_thread_small_id_free (int id)
125 /* MonoBitSet operations are not atomic. */
126 EnterCriticalSection (&small_id_mutex);
128 g_assert (id >= 0 && id < small_id_table->size);
129 g_assert (mono_bitset_test_fast (small_id_table, id));
130 mono_bitset_clear_fast (small_id_table, id);
132 LeaveCriticalSection (&small_id_mutex);
136 is_pointer_hazardous (gpointer p)
139 int highest = highest_small_id;
141 g_assert (highest < hazard_table_size);
143 for (i = 0; i <= highest; ++i) {
144 for (j = 0; j < HAZARD_POINTER_COUNT; ++j) {
145 if (hazard_table [i].hazard_pointers [j] == p)
154 MonoThreadHazardPointers*
155 mono_hazard_pointer_get (void)
157 int small_id = mono_thread_info_get_small_id ();
160 static MonoThreadHazardPointers emerg_hazard_table;
161 g_warning ("Thread %p may have been prematurely finalized", (gpointer)mono_native_thread_id_get ());
162 return &emerg_hazard_table;
165 return &hazard_table [small_id];
168 /* Can be called with hp==NULL, in which case it acts as an ordinary
169 pointer fetch. It's used that way indirectly from
170 mono_jit_info_table_add(), which doesn't have to care about hazards
171 because it holds the respective domain lock. */
173 get_hazardous_pointer (gpointer volatile *pp, MonoThreadHazardPointers *hp, int hazard_index)
178 /* Get the pointer */
180 /* If we don't have hazard pointers just return the
184 /* Make it hazardous */
185 mono_hazard_pointer_set (hp, hazard_index, p);
186 /* Check that it's still the same. If not, try
189 mono_hazard_pointer_clear (hp, hazard_index);
199 try_free_delayed_free_item (void)
201 DelayedFreeItem item;
202 gboolean popped = mono_lock_free_array_queue_pop (&delayed_free_queue, &item);
207 if (is_pointer_hazardous (item.p)) {
208 mono_lock_free_array_queue_push (&delayed_free_queue, &item);
212 item.free_func (item.p);
218 mono_thread_hazardous_free_or_queue (gpointer p, MonoHazardousFreeFunc free_func)
222 /* First try to free a few entries in the delayed free
224 for (i = 0; i < 3; ++i)
225 try_free_delayed_free_item ();
227 /* Now see if the pointer we're freeing is hazardous. If it
228 isn't, free it. Otherwise put it in the delay list. */
229 if (is_pointer_hazardous (p)) {
230 DelayedFreeItem item = { p, free_func };
232 ++mono_stats.hazardous_pointer_count;
234 mono_lock_free_array_queue_push (&delayed_free_queue, &item);
241 mono_thread_hazardous_try_free_all (void)
243 while (try_free_delayed_free_item ())
248 mono_thread_smr_init (void)
250 InitializeCriticalSection(&small_id_mutex);
254 mono_thread_smr_cleanup (void)
256 mono_thread_hazardous_try_free_all ();
258 mono_lock_free_array_queue_cleanup (&delayed_free_queue);
260 /*FIXME, can't we release the small id table here?*/