Fixed MSVC build by explicitly providing "ssize_t".
[mono.git] / mono / utils / mono-linked-list-set.c
index d9ab6210c381274670d32a9340ab2e6fb06df3c3..b7391962eaae5d35ab1e704801f7d36e533224aa 100644 (file)
@@ -16,8 +16,7 @@
 
 #include <mono/utils/mono-linked-list-set.h>
 
-/*atomics.*/
-#include <mono/io-layer/io-layer.h>
+#include <mono/utils/atomic.h>
 
 static inline gpointer
 mask (gpointer n, uintptr_t bit)
@@ -39,6 +38,9 @@ get_hazardous_pointer_with_mask (gpointer volatile *pp, MonoThreadHazardPointers
                        return p;
                /* Make it hazardous */
                mono_hazard_pointer_set (hp, hazard_index, mono_lls_pointer_unmask (p));
+
+               mono_memory_barrier ();
+
                /* Check that it's still the same.  If not, try
                   again. */
                if (*pp != p) {
@@ -53,7 +55,8 @@ get_hazardous_pointer_with_mask (gpointer volatile *pp, MonoThreadHazardPointers
 
 /*
 Initialize @list and will use @free_node_func to release memory.
-If @free_node_func is null the caller is responsible for releasing node memory. 
+If @free_node_func is null the caller is responsible for releasing node memory.
+@free_node_func must be lock-free.  That implies that it cannot use malloc/free.
 */
 void
 mono_lls_init (MonoLinkedListSet *list, void (*free_node_func)(void *))
@@ -81,9 +84,17 @@ mono_lls_find (MonoLinkedListSet *list, MonoThreadHazardPointers *hp, uintptr_t
 
 try_again:
        prev = &list->head;
+
+       /*
+        * prev is not really a hazardous pointer, but we return prev
+        * in hazard pointer 2, so we set it here.  Note also that
+        * prev is not a pointer to a node.  We use here the fact that
+        * the first element in a node is the next pointer, so it
+        * works, but it's not pretty.
+        */
        mono_hazard_pointer_set (hp, 2, prev);
 
-       cur = mono_lls_pointer_unmask (get_hazardous_pointer ((gpointer*)prev, hp, 1));
+       cur = get_hazardous_pointer_with_mask ((gpointer*)prev, hp, 1);
 
        while (1) {
                if (cur == NULL)
@@ -91,6 +102,13 @@ try_again:
                next = get_hazardous_pointer_with_mask ((gpointer*)&cur->next, hp, 0);
                cur_key = cur->key;
 
+               /*
+                * We need to make sure that we dereference prev below
+                * after reading cur->next above, so we need a read
+                * barrier.
+                */
+               mono_memory_read_barrier ();
+
                if (*prev != cur)
                        goto try_again;
 
@@ -102,9 +120,12 @@ try_again:
                        mono_hazard_pointer_set (hp, 2, cur);
                } else {
                        next = mono_lls_pointer_unmask (next);
-                       if (InterlockedCompareExchangePointer ((volatile gpointer*)prev, next, cur) == next) {
+                       if (InterlockedCompareExchangePointer ((volatile gpointer*)prev, next, cur) == cur) {
+                               /* The hazard pointer must be cleared after the CAS. */
+                               mono_memory_write_barrier ();
+                               mono_hazard_pointer_clear (hp, 1);
                                if (list->free_node_func)
-                                       mono_thread_hazardous_free_or_queue (cur, list->free_node_func);
+                                       mono_thread_hazardous_free_or_queue (cur, list->free_node_func, FALSE, TRUE);
                        } else
                                goto try_again;
                }
@@ -136,6 +157,8 @@ mono_lls_insert (MonoLinkedListSet *list, MonoThreadHazardPointers *hp, MonoLink
 
                value->next = cur;
                mono_hazard_pointer_set (hp, 0, value);
+               /* The CAS must happen after setting the hazard pointer. */
+               mono_memory_write_barrier ();
                if (InterlockedCompareExchangePointer ((volatile gpointer*)prev, value, cur) == cur)
                        return TRUE;
        }
@@ -159,11 +182,18 @@ mono_lls_remove (MonoLinkedListSet *list, MonoThreadHazardPointers *hp, MonoLink
                cur = mono_hazard_pointer_get_val (hp, 1);
                prev = mono_hazard_pointer_get_val (hp, 2);
 
+               g_assert (cur == value);
+
                if (InterlockedCompareExchangePointer ((volatile gpointer*)&cur->next, mask (next, 1), next) != next)
                        continue;
-               if (InterlockedCompareExchangePointer ((volatile gpointer*)prev, next, cur) == cur) {
+               /* The second CAS must happen before the first. */
+               mono_memory_write_barrier ();
+               if (InterlockedCompareExchangePointer ((volatile gpointer*)prev, mono_lls_pointer_unmask (next), cur) == cur) {
+                       /* The CAS must happen before the hazard pointer clear. */
+                       mono_memory_write_barrier ();
+                       mono_hazard_pointer_clear (hp, 1);
                        if (list->free_node_func)
-                               mono_thread_hazardous_free_or_queue (value, list->free_node_func);
+                               mono_thread_hazardous_free_or_queue (value, list->free_node_func, FALSE, TRUE);
                } else
                        mono_lls_find (list, hp, value->key);
                return TRUE;