blob: 765e0b68c618ecaf2ef51b6e300bd5e38e6ea00d [file] [log] [blame]
// Copyright 2020 The IREE Authors
//
// Licensed under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#include "iree/task/list.h"
#include <string.h>
void iree_atomic_task_slist_discard(iree_atomic_task_slist_t* slist) {
iree_task_list_t discard_list;
iree_task_list_initialize(&discard_list);
iree_task_list_append_from_fifo_slist(&discard_list, slist);
iree_task_list_discard(&discard_list);
}
void iree_task_list_initialize(iree_task_list_t* out_list) {
memset(out_list, 0, sizeof(*out_list));
}
void iree_task_list_move(iree_task_list_t* list, iree_task_list_t* out_list) {
memcpy(out_list, list, sizeof(*out_list));
memset(list, 0, sizeof(*list));
}
void iree_task_list_discard(iree_task_list_t* list) {
// Fixed point iteration over the task list and all its transitive dependent
// tasks that get discarded. This is in contrast to a recursive discard that
// could potentially be thousands of calls deep in a large graph.
while (!iree_task_list_is_empty(list)) {
iree_task_t* task = iree_task_list_pop_front(list);
iree_task_discard(task, list);
task = NULL; // invalidated during discard
}
}
bool iree_task_list_is_empty(const iree_task_list_t* list) {
return list->head == NULL;
}
iree_host_size_t iree_task_list_calculate_size(const iree_task_list_t* list) {
iree_host_size_t count = 0;
iree_task_t* p = list->head;
while (p) {
++count;
p = p->next_task;
}
return count;
}
iree_task_t* iree_task_list_front(iree_task_list_t* list) { return list->head; }
iree_task_t* iree_task_list_back(iree_task_list_t* list) { return list->tail; }
void iree_task_list_push_back(iree_task_list_t* list, iree_task_t* task) {
if (!list->head) {
list->head = task;
}
if (list->tail) {
list->tail->next_task = task;
}
list->tail = task;
task->next_task = NULL;
}
void iree_task_list_push_front(iree_task_list_t* list, iree_task_t* task) {
task->next_task = list->head;
list->head = task;
if (!list->tail) {
list->tail = task;
}
}
iree_task_t* iree_task_list_pop_front(iree_task_list_t* list) {
if (!list->head) return NULL;
iree_task_t* task = list->head;
list->head = task->next_task;
if (list->tail == task) {
list->tail = NULL;
}
task->next_task = NULL;
return task;
}
void iree_task_list_erase(iree_task_list_t* list, iree_task_t* prev_task,
iree_task_t* task) {
if (task == list->head) {
// Removing head (which may _also_ be the tail).
list->head = task->next_task;
if (list->tail == task) list->tail = task->next_task;
} else if (task == list->tail) {
// Removing tail.
list->tail = prev_task;
prev_task->next_task = NULL;
} else {
// Removing inner.
prev_task->next_task = task->next_task;
}
task->next_task = NULL;
}
void iree_task_list_prepend(iree_task_list_t* list, iree_task_list_t* prefix) {
if (iree_task_list_is_empty(prefix)) return;
if (iree_task_list_is_empty(list)) {
list->head = prefix->head;
list->tail = prefix->tail;
} else {
prefix->tail->next_task = list->head;
list->head = prefix->head;
}
memset(prefix, 0, sizeof(*prefix));
}
void iree_task_list_append(iree_task_list_t* list, iree_task_list_t* suffix) {
if (iree_task_list_is_empty(suffix)) return;
if (iree_task_list_is_empty(list)) {
list->head = suffix->head;
list->tail = suffix->tail;
} else {
list->tail->next_task = suffix->head;
list->tail = suffix->tail;
}
memset(suffix, 0, sizeof(*suffix));
}
void iree_task_list_append_from_fifo_slist(iree_task_list_t* list,
iree_atomic_task_slist_t* slist) {
iree_task_list_t suffix;
iree_task_list_initialize(&suffix);
if (!iree_atomic_task_slist_flush(
slist, IREE_ATOMIC_SLIST_FLUSH_ORDER_APPROXIMATE_FIFO, &suffix.head,
&suffix.tail)) {
return; // empty
}
iree_task_list_append(list, &suffix);
}
void iree_task_list_reverse(iree_task_list_t* list) {
if (iree_task_list_is_empty(list)) return;
iree_task_t* tail = list->head;
iree_task_t* head = list->tail;
iree_task_t* p = list->head;
do {
iree_task_t* next = p->next_task;
p->next_task = head;
head = p;
p = next;
} while (p != NULL);
tail->next_task = NULL;
list->head = head;
list->tail = tail;
}
void iree_task_list_split(iree_task_list_t* head_list,
iree_host_size_t max_tasks,
iree_task_list_t* out_tail_list) {
iree_task_list_initialize(out_tail_list);
if (head_list->head == NULL) return;
if (head_list->head == head_list->tail) {
// 1 task in the source list; always prefer to steal it.
// This is because the victim is likely working on their last item and we
// can help them out by popping this off. It also has the side-effect of
// handling cases of donated workers wanting to steal all tasks to
// synchronously execute things.
iree_task_list_move(head_list, out_tail_list);
return;
}
// Walk through the |head_list| with two iterators; one at double-rate.
// If we ever notice this function showing up in profiling then we should
// build an acceleration structure to avoid the full walk of the first half
// (e.g. skip list).
iree_task_t* p_x1_m1 = head_list->head; // p_x1 - 1 (previous to p_x1)
iree_task_t* p_x1 = head_list->head; // x1 speed ptr
iree_task_t* p_x2 = head_list->head; // x2 speed ptr
while (p_x2->next_task != NULL) {
p_x1_m1 = p_x1;
p_x1 = p_x1->next_task;
p_x2 = p_x2->next_task;
if (p_x2->next_task) p_x2 = p_x2->next_task;
}
// p_x1 now points at the half way point in the head_list. This is where we
// *start* our windowed walk for pulling out max_tasks, implicitly limiting us
// to take at most half of the tasks from the list.
// Advance the tail list keeping an iterator -max_tasks back; when we hit the
// end we have our head and tail to form the list.
iree_task_t* p_window_prev = p_x1_m1;
iree_task_t* p_window_head = p_x1;
iree_task_t* p_window_tail = p_x1;
while (p_window_tail->next_task != NULL && --max_tasks > 0) {
p_window_tail = p_window_tail->next_task;
}
while (p_window_tail->next_task != NULL) {
p_window_prev = p_window_head;
p_window_head = p_window_head->next_task;
p_window_tail = p_window_tail->next_task;
}
head_list->tail = p_window_prev;
p_window_prev->next_task = NULL;
out_tail_list->head = p_window_head;
out_tail_list->tail = p_window_tail;
}