| /* 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; |
| } |