| /* Copyright (c) 2014 The Regents of the University of California | 
 |  * Kevin Klues <klueska@cs.berkeley.edu> | 
 |  * Andrew Waterman <waterman@cs.berkeley.edu> | 
 |  * See LICENSE for details. | 
 |  */ | 
 |  | 
 | #include <parlib/assert.h> | 
 | #include <stdlib.h> | 
 | #include <parlib/arch/atomic.h> | 
 | #include <parlib/waitfreelist.h> | 
 |  | 
 | void wfl_init(struct wfl *list) | 
 | { | 
 | 	list->first.next = NULL; | 
 | 	list->first.data = NULL; | 
 | 	list->head = &list->first; | 
 | } | 
 |  | 
 | void wfl_destroy(struct wfl *list) | 
 | { | 
 | 	assert(list->first.data == NULL); | 
 | 	struct wfl_entry *p = list->first.next; // don't free the first element | 
 |  | 
 | 	while (p != NULL) { | 
 | 		assert(p->data == NULL); | 
 | 		struct wfl_entry *tmp = p; | 
 | 		p = p->next; | 
 | 		free(tmp); | 
 | 	} | 
 | } | 
 |  | 
 | size_t wfl_capacity(struct wfl *list) | 
 | { | 
 | 	size_t res = 0; | 
 |  | 
 | 	for (struct wfl_entry *p = list->head; p != NULL; p = p->next) | 
 | 		res++; | 
 | 	return res; | 
 | } | 
 |  | 
 | size_t wfl_size(struct wfl *list) | 
 | { | 
 | 	size_t res = 0; | 
 |  | 
 | 	for (struct wfl_entry *p = list->head; p != NULL; p = p->next) | 
 | 		res += p->data != NULL; | 
 | 	return res; | 
 | } | 
 |  | 
 | #if 0 | 
 | void wfl_insert(struct wfl *list, void *data) | 
 | { | 
 | retry: | 
 |   struct wfl_entry *p = list->head; // list head is never null | 
 |   struct wfl_entry *new_entry = NULL; | 
 |   while (1) { | 
 |     if (p->data == NULL) { | 
 |       if (__sync_bool_compare_and_swap(&p->data, NULL, data)) { | 
 |         free(new_entry); | 
 |         return; | 
 |       } | 
 |     } | 
 |  | 
 |     if (p->next != NULL) { | 
 |       p = p->next; | 
 |       continue; | 
 |     } | 
 |  | 
 |     if (new_entry == NULL) { | 
 |       new_entry = malloc(sizeof(struct wfl_entry)); | 
 |       if (new_entry == NULL) | 
 |         abort(); | 
 |       new_entry->data = data; | 
 |       new_entry->next = NULL; | 
 |       wmb(); | 
 |     } | 
 |   | 
 |     if (__sync_bool_compare_and_swap(&p->next, NULL, new_entry)) | 
 |       return; | 
 |     goto retry; | 
 |     p = list->head; | 
 |   } | 
 | } | 
 | #endif | 
 |  | 
 | void wfl_insert(struct wfl *list, void *data) | 
 | { | 
 | 	struct wfl_entry *p = list->head; // list head is never null | 
 | 	 | 
 | 	while (1) { | 
 | 		if (p->data == NULL) { | 
 | 			if (__sync_bool_compare_and_swap(&p->data, NULL, data)) | 
 | 				return; | 
 | 		} | 
 | 		 | 
 | 		if (p->next == NULL) | 
 | 			break; | 
 | 		 | 
 | 		p = p->next; | 
 | 	} | 
 | 	 | 
 | 	struct wfl_entry *new_entry = malloc(sizeof(struct wfl_entry)); | 
 |  | 
 | 	if (new_entry == NULL) | 
 | 		abort(); | 
 | 	new_entry->data = data; | 
 | 	new_entry->next = NULL; | 
 | 	 | 
 | 	wmb(); | 
 | 	 | 
 | 	struct wfl_entry *next; | 
 |  | 
 | 	while ((next = __sync_val_compare_and_swap(&p->next, NULL, new_entry))) | 
 | 		p = next; | 
 | } | 
 |  | 
 | void *wfl_remove(struct wfl *list) | 
 | { | 
 | 	for (struct wfl_entry *p = list->head; p != NULL; p = p->next) { | 
 | 		if (p->data != NULL) { | 
 | 			void *data = atomic_swap_ptr(&p->data, 0); | 
 |  | 
 | 			if (data != NULL) | 
 | 				return data; | 
 | 		} | 
 | 	} | 
 | 	return NULL; | 
 | } | 
 |  | 
 | size_t wfl_remove_all(struct wfl *list, void *data) | 
 | { | 
 | 	size_t n = 0; | 
 |  | 
 | 	for (struct wfl_entry *p = list->head; p != NULL; p = p->next) { | 
 | 		if (p->data == data) | 
 | 			n += __sync_bool_compare_and_swap(&p->data, data, NULL); | 
 | 	} | 
 | 	return n; | 
 | } |