[utils/lls] Make the node free function support more flexible.
authorAlex Rønne Petersen <alexrp@xamarin.com>
Sat, 20 Feb 2016 00:59:47 +0000 (01:59 +0100)
committerAlex Rønne Petersen <alexrp@xamarin.com>
Wed, 2 Mar 2016 16:26:43 +0000 (17:26 +0100)
The caller of mono_lls_init () can now specify whether the free function can or
cannot lock, rather than the LLS assuming that it can't. The only reason this
limitation existed was because the LLS wasn't extended to allow specifying this
information in/after 56a6ae2bd0ac3b9ebf8ad9daec6aca484b7e487b.

In addition, all LLS functions are now callable from both safe and async
context; the caller must specify the context as an additional argument. If a
free function is in use and it may lock, we'll simply enqueue it for execution
at a later point if an LLS function is invoked in async context.

mono/unit-tests/test-mono-linked-list-set.c
mono/utils/mono-linked-list-set.c
mono/utils/mono-linked-list-set.h
mono/utils/mono-threads.c

index a5e01ebe09ec6e5f913e1ad936c822596d39ecde..3841581e0bb02fd61fe9f407dbcbef8891a0cea9 100644 (file)
@@ -74,11 +74,11 @@ worker (void *arg)
                        break;
                case STATE_OUT:
                        if (InterlockedCompareExchange (&nodes [i].state, STATE_BUSY, STATE_OUT) == STATE_OUT) {
-                               result = mono_lls_find (&lls, hp, i);
+                               result = mono_lls_find (&lls, hp, i, HAZARD_FREE_SAFE_CTX);
                                assert (!result);
                                mono_hazard_pointer_clear_all (hp, -1);
 
-                               result = mono_lls_insert (&lls, hp, &nodes [i].node);
+                               result = mono_lls_insert (&lls, hp, &nodes [i].node, HAZARD_FREE_SAFE_CTX);
                                mono_hazard_pointer_clear_all (hp, -1);
 
                                assert (nodes [i].state == STATE_BUSY);
@@ -89,12 +89,12 @@ worker (void *arg)
                        break;
                case STATE_IN:
                        if (InterlockedCompareExchange (&nodes [i].state, STATE_BUSY, STATE_IN) == STATE_IN) {
-                               result = mono_lls_find (&lls, hp, i);
+                               result = mono_lls_find (&lls, hp, i, HAZARD_FREE_SAFE_CTX);
                                assert (result);
                                assert (mono_hazard_pointer_get_val (hp, 1) == &nodes [i].node);
                                mono_hazard_pointer_clear_all (hp, -1);
 
-                               result = mono_lls_remove (&lls, hp, &nodes [i].node);
+                               result = mono_lls_remove (&lls, hp, &nodes [i].node, HAZARD_FREE_SAFE_CTX);
                                mono_hazard_pointer_clear_all (hp, -1);
 
                                ++thread_data->num_removes;
@@ -126,7 +126,7 @@ main (int argc, char *argv [])
 
        mono_threads_init (&thread_callbacks, 0);
 
-       mono_lls_init (&lls, free_node);
+       mono_lls_init (&lls, free_node, HAZARD_FREE_NO_LOCK);
 
        for (i = 0; i < N; ++i) {
                nodes [i].node.key = i;
index 454b5ea9eb204b6f88b274fafc9dd413b7b22959..bed9595f84d344e3209521564fc756efcfdb77c9 100644 (file)
@@ -56,27 +56,28 @@ 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.
-@free_node_func must be lock-free.  That implies that it cannot use malloc/free.
+If @free_node_func may lock, @free_node_func_locking must be
+HAZARD_FREE_MAY_LOCK; otherwise, HAZARD_FREE_NO_LOCK. It is ignored if
+@free_node_func is null.
 */
 void
-mono_lls_init (MonoLinkedListSet *list, void (*free_node_func)(void *))
+mono_lls_init (MonoLinkedListSet *list, void (*free_node_func)(void *), HazardFreeLocking free_node_func_locking)
 {
        list->head = NULL;
        list->free_node_func = free_node_func;
+       list->locking = free_node_func_locking;
 }
 
 /*
 Search @list for element with key @key.
+@context specifies whether the function is being called from a lock-free (i.e.
+signal handler or world stopped) context. It is only relevant if a node free
+function was given.
 The nodes next, cur and prev are returned in @hp.
 Returns true if a node with key @key was found.
-This function cannot be called from a signal nor within interrupt context*.
-XXX A variant that works within interrupted is possible if needed.
-
-* interrupt context is when the current thread is reposible for another thread
-been suspended at an arbritary point. This is a limitation of our SMR implementation.
 */
 gboolean
-mono_lls_find (MonoLinkedListSet *list, MonoThreadHazardPointers *hp, uintptr_t key)
+mono_lls_find (MonoLinkedListSet *list, MonoThreadHazardPointers *hp, uintptr_t key, HazardFreeContext context)
 {
        MonoLinkedListSetNode *cur, *next;
        MonoLinkedListSetNode **prev;
@@ -125,7 +126,7 @@ try_again:
                                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, HAZARD_FREE_NO_LOCK, HAZARD_FREE_ASYNC_CTX);
+                                       mono_thread_hazardous_free_or_queue (cur, list->free_node_func, list->locking, context);
                        } else
                                goto try_again;
                }
@@ -136,13 +137,15 @@ try_again:
 
 /*
 Insert @value into @list.
+@context specifies whether the function is being called from a lock-free (i.e.
+signal handler or world stopped) context. It is only relevant if a node free
+function was given.
 The nodes value, cur and prev are returned in @hp.
 Return true if @value was inserted by this call. If it returns FALSE, it's the caller
 resposibility to release memory.
-This function cannot be called from a signal nor with the world stopped.
 */
 gboolean
-mono_lls_insert (MonoLinkedListSet *list, MonoThreadHazardPointers *hp, MonoLinkedListSetNode *value)
+mono_lls_insert (MonoLinkedListSet *list, MonoThreadHazardPointers *hp, MonoLinkedListSetNode *value, HazardFreeContext context)
 {
        MonoLinkedListSetNode *cur, **prev;
        /*We must do a store barrier before inserting 
@@ -150,7 +153,7 @@ mono_lls_insert (MonoLinkedListSet *list, MonoThreadHazardPointers *hp, MonoLink
        mono_memory_barrier ();
 
        while (1) {
-               if (mono_lls_find (list, hp, value->key))
+               if (mono_lls_find (list, hp, value->key, context))
                        return FALSE;
                cur = (MonoLinkedListSetNode *) mono_hazard_pointer_get_val (hp, 1);
                prev = (MonoLinkedListSetNode **) mono_hazard_pointer_get_val (hp, 2);
@@ -165,17 +168,19 @@ mono_lls_insert (MonoLinkedListSet *list, MonoThreadHazardPointers *hp, MonoLink
 }
 
 /*
-Search @list for element with key @key.
+Search @list for element with key @key and remove it.
+@context specifies whether the function is being called from a lock-free (i.e.
+signal handler or world stopped) context. It is only relevant if a node free
+function was given.
 The nodes next, cur and prev are returned in @hp
 Returns true if @value was removed by this call.
-This function cannot be called from a signal nor with the world stopped.
 */
 gboolean
-mono_lls_remove (MonoLinkedListSet *list, MonoThreadHazardPointers *hp, MonoLinkedListSetNode *value)
+mono_lls_remove (MonoLinkedListSet *list, MonoThreadHazardPointers *hp, MonoLinkedListSetNode *value, HazardFreeContext context)
 {
        MonoLinkedListSetNode *cur, **prev, *next;
        while (1) {
-               if (!mono_lls_find (list, hp, value->key))
+               if (!mono_lls_find (list, hp, value->key, context))
                        return FALSE;
 
                next = (MonoLinkedListSetNode *) mono_hazard_pointer_get_val (hp, 0);
@@ -193,9 +198,9 @@ mono_lls_remove (MonoLinkedListSet *list, MonoThreadHazardPointers *hp, MonoLink
                        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, HAZARD_FREE_NO_LOCK, HAZARD_FREE_ASYNC_CTX);
+                               mono_thread_hazardous_free_or_queue (value, list->free_node_func, list->locking, context);
                } else
-                       mono_lls_find (list, hp, value->key);
+                       mono_lls_find (list, hp, value->key, context);
                return TRUE;
        }
 }
index 698f4083e7e21dbbc5a365c0b10befc7b74ecf87..6e0d6296fd6b53a04ba05727240d196daab5fb08 100644 (file)
@@ -24,6 +24,7 @@ struct _MonoLinkedListSetNode {
 typedef struct {
        MonoLinkedListSetNode *head;
        void (*free_node_func)(void *);
+       HazardFreeLocking locking;
 } MonoLinkedListSet;
 
 
@@ -45,16 +46,16 @@ You must manually clean the hazard pointer table after using them.
 */
 
 void
-mono_lls_init (MonoLinkedListSet *list, void (*free_node_func)(void *));
+mono_lls_init (MonoLinkedListSet *list, void (*free_node_func)(void *), HazardFreeLocking free_node_func_locking);
 
 gboolean
-mono_lls_find (MonoLinkedListSet *list, MonoThreadHazardPointers *hp, uintptr_t key);
+mono_lls_find (MonoLinkedListSet *list, MonoThreadHazardPointers *hp, uintptr_t key, HazardFreeContext context);
 
 gboolean
-mono_lls_insert (MonoLinkedListSet *list, MonoThreadHazardPointers *hp, MonoLinkedListSetNode *value);
+mono_lls_insert (MonoLinkedListSet *list, MonoThreadHazardPointers *hp, MonoLinkedListSetNode *value, HazardFreeContext context);
 
 gboolean
-mono_lls_remove (MonoLinkedListSet *list, MonoThreadHazardPointers *hp, MonoLinkedListSetNode *value);
+mono_lls_remove (MonoLinkedListSet *list, MonoThreadHazardPointers *hp, MonoLinkedListSetNode *value, HazardFreeContext context);
 
 gpointer
 get_hazardous_pointer_with_mask (gpointer volatile *pp, MonoThreadHazardPointers *hp, int hazard_index);
@@ -151,7 +152,7 @@ mono_lls_filter_accept_all (gpointer elem)
                                                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, HAZARD_FREE_NO_LOCK, HAZARD_FREE_ASYNC_CTX); \
+                                                       mono_thread_hazardous_free_or_queue (cur__, list__->free_node_func, list__->locking, HAZARD_FREE_ASYNC_CTX); \
                                                } \
                                        } else { \
                                                restart__ = TRUE; \
index 1e799a207be9bcf2ccbb09ded78959d8184561d3..ae4cb7d482cf1a0a201687af4a3fc5d3cd22b684 100644 (file)
@@ -269,7 +269,7 @@ mono_thread_info_lookup (MonoNativeThreadId id)
 {
                MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
 
-       if (!mono_lls_find (&thread_list, hp, (uintptr_t)id)) {
+       if (!mono_lls_find (&thread_list, hp, (uintptr_t)id, HAZARD_FREE_ASYNC_CTX)) {
                mono_hazard_pointer_clear_all (hp, -1);
                return NULL;
        } 
@@ -283,7 +283,7 @@ mono_thread_info_insert (MonoThreadInfo *info)
 {
        MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
 
-       if (!mono_lls_insert (&thread_list, hp, (MonoLinkedListSetNode*)info)) {
+       if (!mono_lls_insert (&thread_list, hp, (MonoLinkedListSetNode*)info, HAZARD_FREE_SAFE_CTX)) {
                mono_hazard_pointer_clear_all (hp, -1);
                return FALSE;
        } 
@@ -299,7 +299,7 @@ mono_thread_info_remove (MonoThreadInfo *info)
        gboolean res;
 
        THREADS_DEBUG ("removing info %p\n", info);
-       res = mono_lls_remove (&thread_list, hp, (MonoLinkedListSetNode*)info);
+       res = mono_lls_remove (&thread_list, hp, (MonoLinkedListSetNode*)info, HAZARD_FREE_SAFE_CTX);
        mono_hazard_pointer_clear_all (hp, -1);
        return res;
 }
@@ -635,7 +635,7 @@ mono_threads_init (MonoThreadInfoCallbacks *callbacks, size_t info_size)
        mono_coop_sem_init (&global_suspend_semaphore, 1);
        mono_os_sem_init (&suspend_semaphore, 0);
 
-       mono_lls_init (&thread_list, NULL);
+       mono_lls_init (&thread_list, NULL, HAZARD_FREE_NO_LOCK);
        mono_thread_smr_init ();
        mono_threads_init_platform ();
        mono_threads_init_coop ();