|
|
|
@ -2,6 +2,7 @@
|
|
|
|
|
#include <limits.h>
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
|
|
|
|
|
#include <priority_queue.h>
|
|
|
|
|
|
|
|
|
@ -19,11 +20,12 @@ static inline int
|
|
|
|
|
priority_queue_append(struct priority_queue *self, void *new_item)
|
|
|
|
|
{
|
|
|
|
|
assert(self != NULL);
|
|
|
|
|
assert(ck_spinlock_fas_locked(&self->lock));
|
|
|
|
|
|
|
|
|
|
if (self->first_free >= MAX) return -1;
|
|
|
|
|
|
|
|
|
|
self->items[self->first_free] = new_item;
|
|
|
|
|
self->first_free++;
|
|
|
|
|
self->items[self->first_free++] = new_item;
|
|
|
|
|
// self->first_free++;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -36,9 +38,11 @@ priority_queue_percolate_up(struct priority_queue *self)
|
|
|
|
|
{
|
|
|
|
|
assert(self != NULL);
|
|
|
|
|
assert(self->get_priority != NULL);
|
|
|
|
|
assert(ck_spinlock_fas_locked(&self->lock));
|
|
|
|
|
|
|
|
|
|
for (int i = self->first_free - 1;
|
|
|
|
|
i / 2 != 0 && self->get_priority(self->items[i]) < self->get_priority(self->items[i / 2]); i /= 2) {
|
|
|
|
|
assert(self->get_priority(self->items[i]) != ULONG_MAX);
|
|
|
|
|
void *temp = self->items[i / 2];
|
|
|
|
|
self->items[i / 2] = self->items[i];
|
|
|
|
|
self->items[i] = temp;
|
|
|
|
@ -59,6 +63,7 @@ priority_queue_find_smallest_child(struct priority_queue *self, int parent_index
|
|
|
|
|
assert(self != NULL);
|
|
|
|
|
assert(parent_index >= 1 && parent_index < self->first_free);
|
|
|
|
|
assert(self->get_priority != NULL);
|
|
|
|
|
assert(ck_spinlock_fas_locked(&self->lock));
|
|
|
|
|
|
|
|
|
|
int left_child_index = 2 * parent_index;
|
|
|
|
|
int right_child_index = 2 * parent_index + 1;
|
|
|
|
@ -86,6 +91,7 @@ priority_queue_percolate_down(struct priority_queue *self, int parent_index)
|
|
|
|
|
{
|
|
|
|
|
assert(self != NULL);
|
|
|
|
|
assert(self->get_priority != NULL);
|
|
|
|
|
assert(ck_spinlock_fas_locked(&self->lock));
|
|
|
|
|
|
|
|
|
|
int left_child_index = 2 * parent_index;
|
|
|
|
|
while (left_child_index >= 2 && left_child_index < self->first_free) {
|
|
|
|
@ -123,7 +129,8 @@ priority_queue_initialize(struct priority_queue *self, priority_queue_get_priori
|
|
|
|
|
memset(self->items, 0, sizeof(void *) * MAX);
|
|
|
|
|
|
|
|
|
|
ck_spinlock_fas_init(&self->lock);
|
|
|
|
|
self->first_free = 1;
|
|
|
|
|
self->first_free = 1;
|
|
|
|
|
printf("[Init] First Free: %d\n", self->first_free);
|
|
|
|
|
self->get_priority = get_priority;
|
|
|
|
|
|
|
|
|
|
// We're assuming a min-heap implementation, so set to larget possible value
|
|
|
|
@ -137,9 +144,13 @@ priority_queue_initialize(struct priority_queue *self, priority_queue_get_priori
|
|
|
|
|
int
|
|
|
|
|
priority_queue_length(struct priority_queue *self)
|
|
|
|
|
{
|
|
|
|
|
// printf("[Length] First Free: %d\n", self->first_free);
|
|
|
|
|
assert(self != NULL);
|
|
|
|
|
|
|
|
|
|
return self->first_free - 1;
|
|
|
|
|
ck_spinlock_fas_lock(&self->lock);
|
|
|
|
|
assert(ck_spinlock_fas_locked(&self->lock));
|
|
|
|
|
int length = self->first_free - 1;
|
|
|
|
|
ck_spinlock_fas_unlock(&self->lock);
|
|
|
|
|
return length;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
@ -148,29 +159,37 @@ priority_queue_length(struct priority_queue *self)
|
|
|
|
|
* @returns 0 on success. -1 on full. -2 on unable to take lock
|
|
|
|
|
**/
|
|
|
|
|
int
|
|
|
|
|
priority_queue_enqueue(struct priority_queue *self, void *value)
|
|
|
|
|
priority_queue_enqueue(struct priority_queue *self, void *value, char *name)
|
|
|
|
|
{
|
|
|
|
|
assert(self != NULL);
|
|
|
|
|
int rc;
|
|
|
|
|
ck_spinlock_fas_lock(&self->lock);
|
|
|
|
|
|
|
|
|
|
if (ck_spinlock_fas_trylock(&self->lock) == false) return -2;
|
|
|
|
|
int pre_length = self->first_free - 1;
|
|
|
|
|
|
|
|
|
|
// Start of Critical Section
|
|
|
|
|
if (priority_queue_append(self, value) == 0) {
|
|
|
|
|
if (self->first_free > 2) {
|
|
|
|
|
priority_queue_percolate_up(self);
|
|
|
|
|
} else {
|
|
|
|
|
// If this is the first element we add, update the highest priority
|
|
|
|
|
self->highest_priority = self->get_priority(value);
|
|
|
|
|
}
|
|
|
|
|
rc = 0;
|
|
|
|
|
if (priority_queue_append(self, value) == -1) {
|
|
|
|
|
printf("Priority Queue is full");
|
|
|
|
|
fflush(stdout);
|
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
|
ck_spinlock_fas_unlock(&self->lock);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int post_length = self->first_free - 1;
|
|
|
|
|
printf("[%s Enqueue] First Free: %d\n", name, self->first_free);
|
|
|
|
|
|
|
|
|
|
// We should have appended here
|
|
|
|
|
assert(post_length == pre_length + 1);
|
|
|
|
|
|
|
|
|
|
// If this is the first element we add, update the highest priority
|
|
|
|
|
if (self->first_free == 2) {
|
|
|
|
|
self->highest_priority = self->get_priority(value);
|
|
|
|
|
} else {
|
|
|
|
|
// Priority Queue is full
|
|
|
|
|
rc = -1;
|
|
|
|
|
priority_queue_percolate_up(self);
|
|
|
|
|
}
|
|
|
|
|
// End of Critical Section
|
|
|
|
|
ck_spinlock_fas_unlock(&self->lock);
|
|
|
|
|
return rc;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
/**
|
|
|
|
|
* @param self - the priority queue we want to delete from
|
|
|
|
@ -178,23 +197,27 @@ priority_queue_enqueue(struct priority_queue *self, void *value)
|
|
|
|
|
* @returns 0 on success. -1 on not found. -2 on unable to take lock
|
|
|
|
|
**/
|
|
|
|
|
int
|
|
|
|
|
priority_queue_delete(struct priority_queue *self, void *value)
|
|
|
|
|
priority_queue_delete(struct priority_queue *self, void *value, char *name)
|
|
|
|
|
{
|
|
|
|
|
assert(self != NULL);
|
|
|
|
|
if (ck_spinlock_fas_trylock(&self->lock) == false) return -2;
|
|
|
|
|
ck_spinlock_fas_lock(&self->lock);
|
|
|
|
|
|
|
|
|
|
bool did_delete = false;
|
|
|
|
|
for (int i = 1; i < self->first_free; i++) {
|
|
|
|
|
if (self->items[i] == value) {
|
|
|
|
|
self->items[i] = self->items[self->first_free - 1];
|
|
|
|
|
self->items[self->first_free - 1] = NULL;
|
|
|
|
|
self->first_free--;
|
|
|
|
|
self->items[i] = self->items[--self->first_free];
|
|
|
|
|
self->items[self->first_free] = NULL;
|
|
|
|
|
priority_queue_percolate_down(self, i);
|
|
|
|
|
did_delete = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
ck_spinlock_fas_unlock(&self->lock);
|
|
|
|
|
if (!did_delete) return -1;
|
|
|
|
|
assert(did_delete);
|
|
|
|
|
if (!did_delete) {
|
|
|
|
|
printf("[priority_queue_delete] Not found!\n");
|
|
|
|
|
return -1;
|
|
|
|
|
};
|
|
|
|
|
printf("[%s Delete] First Free: %d\n", name, self->first_free);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -202,8 +225,12 @@ static bool
|
|
|
|
|
priority_queue_is_empty(struct priority_queue *self)
|
|
|
|
|
{
|
|
|
|
|
assert(self != NULL);
|
|
|
|
|
bool caller_locked = ck_spinlock_fas_locked(&self->lock);
|
|
|
|
|
if (!caller_locked) ck_spinlock_fas_lock(&self->lock);
|
|
|
|
|
assert(self->first_free != 0);
|
|
|
|
|
return self->first_free == 1;
|
|
|
|
|
bool is_empty = self->first_free == 1;
|
|
|
|
|
if (!caller_locked) ck_spinlock_fas_unlock(&self->lock);
|
|
|
|
|
return is_empty;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
@ -211,19 +238,20 @@ priority_queue_is_empty(struct priority_queue *self)
|
|
|
|
|
* @returns The head of the priority queue or NULL when empty
|
|
|
|
|
**/
|
|
|
|
|
void *
|
|
|
|
|
priority_queue_dequeue(struct priority_queue *self)
|
|
|
|
|
priority_queue_dequeue(struct priority_queue *self, char *name)
|
|
|
|
|
{
|
|
|
|
|
assert(self != NULL);
|
|
|
|
|
assert(self->get_priority != NULL);
|
|
|
|
|
if (priority_queue_is_empty(self)) return NULL;
|
|
|
|
|
if (ck_spinlock_fas_trylock(&self->lock) == false) return NULL;
|
|
|
|
|
|
|
|
|
|
ck_spinlock_fas_lock(&self->lock);
|
|
|
|
|
assert(ck_spinlock_fas_locked(&self->lock));
|
|
|
|
|
// Start of Critical Section
|
|
|
|
|
void *min = NULL;
|
|
|
|
|
if (!priority_queue_is_empty(self)) {
|
|
|
|
|
min = self->items[1];
|
|
|
|
|
self->items[1] = self->items[self->first_free - 1];
|
|
|
|
|
self->items[self->first_free - 1] = NULL;
|
|
|
|
|
self->first_free--;
|
|
|
|
|
min = self->items[1];
|
|
|
|
|
self->items[1] = self->items[--self->first_free];
|
|
|
|
|
self->items[self->first_free] = NULL;
|
|
|
|
|
// Because of 1-based indices, first_free is 2 when there is only one element
|
|
|
|
|
if (self->first_free > 2) priority_queue_percolate_down(self, 1);
|
|
|
|
|
|
|
|
|
@ -231,13 +259,26 @@ priority_queue_dequeue(struct priority_queue *self)
|
|
|
|
|
self->highest_priority = !priority_queue_is_empty(self) ? self->get_priority(self->items[1])
|
|
|
|
|
: ULONG_MAX;
|
|
|
|
|
}
|
|
|
|
|
printf("[%s Dequeue] First Free: %d\n", name, self->first_free);
|
|
|
|
|
ck_spinlock_fas_unlock(&self->lock);
|
|
|
|
|
// End of Critical Section
|
|
|
|
|
return min;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// /**
|
|
|
|
|
// * Returns the head of the priority queue without removing it
|
|
|
|
|
// **/
|
|
|
|
|
// void *
|
|
|
|
|
// priority_queue_get_head(struct priority_queue *self)
|
|
|
|
|
// {
|
|
|
|
|
// ck_spinlock_fas_lock(&self->lock);
|
|
|
|
|
|
|
|
|
|
// ck_spinlock_fas_unlock(&self->lock);
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
uint64_t
|
|
|
|
|
priority_queue_peek(struct priority_queue *self)
|
|
|
|
|
{
|
|
|
|
|
return self->highest_priority;
|
|
|
|
|
uint64_t highest_priority = self->highest_priority;
|
|
|
|
|
return highest_priority;
|
|
|
|
|
}
|