/**
 * \file
 * A lock-free split ordered list.
 *
 * Author:
 *	Rodrigo Kumpera (kumpera@gmail.com)
 *
 * (C) 2011 Novell, Inc
 */

#ifndef __MONO_SPLIT_ORDERED_LIST_H__
#define __MONO_SPLIT_ORDERED_LIST_H__

#include <mono/utils/hazard-pointer.h>
#include <mono/utils/mono-membar.h>

typedef struct _MonoLinkedListSetNode MonoLinkedListSetNode;

struct _MonoLinkedListSetNode {
	/* next must be the first element in this struct! */
	MonoLinkedListSetNode *next;
	uintptr_t key;
};

typedef struct {
	MonoLinkedListSetNode *head;
	void (*free_node_func)(void *);
} MonoLinkedListSet;


static inline gpointer
mono_lls_pointer_unmask (gpointer p)
{
	return (gpointer)((uintptr_t)p & ~(uintptr_t)0x3);
}

static inline uintptr_t
mono_lls_pointer_get_mark (gpointer n)
{
	return (uintptr_t)n & 0x1;
}

/*
Those are low level operations. prev, cur, next are returned in the hazard pointer table.
You must manually clean the hazard pointer table after using them.
*/

MONO_API void
mono_lls_init (MonoLinkedListSet *list, void (*free_node_func)(void *));

MONO_API gboolean
mono_lls_find (MonoLinkedListSet *list, MonoThreadHazardPointers *hp, uintptr_t key);

MONO_API gboolean
mono_lls_insert (MonoLinkedListSet *list, MonoThreadHazardPointers *hp, MonoLinkedListSetNode *value);

MONO_API gboolean
mono_lls_remove (MonoLinkedListSet *list, MonoThreadHazardPointers *hp, MonoLinkedListSetNode *value);

MONO_API gpointer
mono_lls_get_hazardous_pointer_with_mask (gpointer volatile *pp, MonoThreadHazardPointers *hp, int hazard_index);

static inline gboolean
mono_lls_filter_accept_all (gpointer elem, gpointer dummy)
{
	return TRUE;
}

/*
 * These macros assume that no other threads are actively modifying the list.
 */

#define MONO_LLS_FOREACH_FILTERED(list, type, elem, filter, ...) \
	do { \
		MonoLinkedListSet *list__ = (list); \
		for (MonoLinkedListSetNode *cur__ = list__->head; cur__; cur__ = (MonoLinkedListSetNode *) mono_lls_pointer_unmask (cur__->next)) { \
			if (!mono_lls_pointer_get_mark (cur__->next)) { \
				type *elem = (type *) cur__; \
				if (filter (elem, __VA_ARGS__)) {

#define MONO_LLS_FOREACH_END \
				} \
			} \
		} \
	} while (0);

#define MONO_LLS_FOREACH(list, type, elem) \
	MONO_LLS_FOREACH_FILTERED ((list), type, elem, mono_lls_filter_accept_all, NULL)

/*
 * These macros can be used while other threads are potentially modifying the
 * list, but they only provide a snapshot of the list as a result.
 *
 * NOTE: Do NOT break out of the loop through any other means than a break
 * statement, as other ways of breaking the loop (return, goto, etc) will skip
 * past important cleanup work.
 */

#define MONO_LLS_FOREACH_FILTERED_SAFE(list, type, elem, filter, ...) \
	do { \
		/* NOTE: Keep this macro's code in sync with the mono_lls_find () logic. */ \
		MonoLinkedListSet *list__ = (list); \
		MonoThreadHazardPointers *hp__ = mono_hazard_pointer_get (); \
		gboolean progress__ = FALSE; \
		uintptr_t hkey__ = 0; \
		gboolean restart__ = FALSE; \
		do { \
			restart__ = FALSE; \
			MonoLinkedListSetNode **prev__ = &list__->head; \
			mono_hazard_pointer_set (hp__, 2, prev__); \
			MonoLinkedListSetNode *cur__ = (MonoLinkedListSetNode *) mono_lls_get_hazardous_pointer_with_mask ((gpointer *) prev__, hp__, 1); \
			while (1) { \
				if (!cur__) { \
					break; \
				} \
				MonoLinkedListSetNode *next__ = (MonoLinkedListSetNode *) mono_lls_get_hazardous_pointer_with_mask ((gpointer *) &cur__->next, hp__, 0); \
				uintptr_t ckey__ = cur__->key; \
				mono_memory_read_barrier (); \
				if (*prev__ != cur__) { \
					restart__ = TRUE; \
					break; \
				} \
				if (!mono_lls_pointer_get_mark (next__)) { \
					if (!progress__ || ckey__ > hkey__) { \
						progress__ = TRUE; \
						hkey__ = ckey__; \
						type *elem = (type *) cur__; \
						if (filter (elem, __VA_ARGS__)) { \
							gboolean broke__ = TRUE; \
							gboolean done__ = FALSE; \
							do { \
								if (done__) { \
									broke__ = FALSE; \
									break; \
								} \
								done__ = TRUE;

#define MONO_LLS_FOREACH_SAFE_END \
								broke__ = FALSE; \
								break; \
							} while (1); \
							if (broke__) { \
								break; \
							} \
						} \
					} \
					prev__ = &cur__->next; \
					mono_hazard_pointer_set (hp__, 2, cur__); \
				} else { \
					next__ = (MonoLinkedListSetNode *) mono_lls_pointer_unmask (next__); \
					if (mono_atomic_cas_ptr ((volatile gpointer *) prev__, next__, cur__) == cur__) { \
						mono_memory_write_barrier (); \
						mono_hazard_pointer_clear (hp__, 1); \
						if (list__->free_node_func) { \
							mono_thread_hazardous_queue_free (cur__, list__->free_node_func); \
						} \
					} else { \
						restart__ = TRUE; \
						break; \
					} \
				} \
				cur__ = (MonoLinkedListSetNode *) mono_lls_pointer_unmask (next__); \
				mono_hazard_pointer_set (hp__, 1, cur__); \
			} \
		} while (restart__); \
		mono_hazard_pointer_clear (hp__, 0); \
		mono_hazard_pointer_clear (hp__, 1); \
		mono_hazard_pointer_clear (hp__, 2); \
	} while (0);

#define MONO_LLS_FOREACH_SAFE(list, type, elem) \
	MONO_LLS_FOREACH_FILTERED_SAFE ((list), type, elem, mono_lls_filter_accept_all, NULL)

#endif /* __MONO_SPLIT_ORDERED_LIST_H__ */
