blob: 85c08405691b09d81d4ab9f38b4f08a9fca6f857 [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/scope.h"
#include <chrono>
#include <thread>
#include "iree/task/submission.h"
#include "iree/task/task_impl.h"
#include "iree/testing/gtest.h"
namespace {
TEST(ScopeTest, Lifetime) {
iree_task_scope_t scope;
iree_task_scope_initialize(iree_make_cstring_view("scope_a"), &scope);
EXPECT_TRUE(iree_task_scope_is_idle(&scope));
iree_task_scope_deinitialize(&scope);
}
// NOTE: the exact capacity (and whether we store the name at all) is an
// implementation detail.
TEST(ScopeTest, LongNameTruncation) {
iree_task_scope_t scope;
iree_task_scope_initialize(iree_make_cstring_view("01234567890123456789"),
&scope);
EXPECT_TRUE(iree_string_view_equal(iree_make_cstring_view("012345678901234"),
iree_task_scope_name(&scope)));
iree_task_scope_deinitialize(&scope);
}
TEST(ScopeTest, AbortEmpty) {
iree_task_scope_t scope;
iree_task_scope_initialize(iree_make_cstring_view("scope_a"), &scope);
// Current state is OK.
EXPECT_TRUE(iree_task_scope_is_idle(&scope));
EXPECT_TRUE(iree_status_is_ok(iree_task_scope_consume_status(&scope)));
// Enter aborted state.
iree_task_scope_abort(&scope);
iree_status_t consumed_status = iree_task_scope_consume_status(&scope);
EXPECT_TRUE(iree_status_is_aborted(consumed_status));
iree_status_ignore(consumed_status);
// Ensure aborted state is sticky.
EXPECT_TRUE(iree_status_is_aborted(iree_task_scope_consume_status(&scope)));
iree_task_scope_deinitialize(&scope);
}
TEST(ScopeTest, FailEmpty) {
iree_task_scope_t scope;
iree_task_scope_initialize(iree_make_cstring_view("scope_a"), &scope);
// Current state is OK.
EXPECT_TRUE(iree_task_scope_is_idle(&scope));
EXPECT_TRUE(iree_status_is_ok(iree_task_scope_consume_status(&scope)));
// Enter failure state.
iree_task_t failed_task = {0};
failed_task.scope = &scope;
iree_task_scope_fail(failed_task.scope,
iree_make_status(IREE_STATUS_DATA_LOSS, "whoops!"));
iree_status_t consumed_status = iree_task_scope_consume_status(&scope);
EXPECT_TRUE(iree_status_is_data_loss(consumed_status));
iree_status_ignore(consumed_status);
// Ensure failure state is sticky.
EXPECT_TRUE(iree_status_is_data_loss(iree_task_scope_consume_status(&scope)));
iree_task_scope_deinitialize(&scope);
}
// NOTE: only the first failure is recorded and made sticky; subsequent failure
// calls are ignored.
TEST(ScopeTest, FailAgain) {
iree_task_scope_t scope;
iree_task_scope_initialize(iree_make_cstring_view("scope_a"), &scope);
// Current state is OK.
EXPECT_TRUE(iree_task_scope_is_idle(&scope));
EXPECT_TRUE(iree_status_is_ok(iree_task_scope_consume_status(&scope)));
// Enter initial failure state.
iree_task_t failed_task_a = {0};
failed_task_a.scope = &scope;
iree_task_scope_fail(failed_task_a.scope,
iree_make_status(IREE_STATUS_DATA_LOSS, "whoops 1"));
iree_status_t consumed_status_a = iree_task_scope_consume_status(&scope);
EXPECT_TRUE(iree_status_is_data_loss(consumed_status_a));
iree_status_ignore(consumed_status_a);
// Ensure failure s tate is sticky.
EXPECT_TRUE(iree_status_is_data_loss(iree_task_scope_consume_status(&scope)));
// Try failing again - it should be ignored and correctly iree_status_free'd.
iree_task_t failed_task_b = {0};
failed_task_b.scope = &scope;
iree_task_scope_fail(
failed_task_b.scope,
iree_make_status(IREE_STATUS_FAILED_PRECONDITION, "whoops 2"));
iree_status_t consumed_status_b = iree_task_scope_consume_status(&scope);
EXPECT_TRUE(iree_status_is_data_loss(consumed_status_b));
iree_status_ignore(consumed_status_b);
// Still the first failure status.
EXPECT_TRUE(iree_status_is_data_loss(iree_task_scope_consume_status(&scope)));
iree_task_scope_deinitialize(&scope);
}
TEST(ScopeTest, WaitIdleWhenIdle) {
iree_task_scope_t scope;
iree_task_scope_initialize(iree_make_cstring_view("scope_a"), &scope);
// Current state is OK and idle.
EXPECT_TRUE(iree_task_scope_is_idle(&scope));
EXPECT_TRUE(iree_status_is_ok(iree_task_scope_consume_status(&scope)));
// Wait until idle... which is now.
EXPECT_TRUE(iree_status_is_ok(
iree_task_scope_wait_idle(&scope, IREE_TIME_INFINITE_FUTURE)));
EXPECT_TRUE(iree_task_scope_is_idle(&scope));
iree_task_scope_deinitialize(&scope);
}
TEST(ScopeTest, WaitIdleDeadlineExceeded) {
iree_task_scope_t scope;
iree_task_scope_initialize(iree_make_cstring_view("scope_a"), &scope);
// Current state is OK and idle.
EXPECT_TRUE(iree_task_scope_is_idle(&scope));
EXPECT_TRUE(iree_status_is_ok(iree_task_scope_consume_status(&scope)));
// Enqueue a task to the scope so it is no longer idle.
iree_task_fence_t fence_task;
iree_task_fence_initialize(&scope, iree_wait_primitive_immediate(),
&fence_task);
EXPECT_FALSE(iree_task_scope_is_idle(&scope));
// Poll, which should fail immediately because we have the outstanding task.
iree_status_t wait_status =
iree_task_scope_wait_idle(&scope, IREE_TIME_INFINITE_PAST);
EXPECT_TRUE(iree_status_is_deadline_exceeded(wait_status));
EXPECT_FALSE(iree_task_scope_is_idle(&scope));
// Complete the task (required as part of the scope contract).
iree_task_submission_t pending_submission;
iree_task_submission_initialize(&pending_submission);
iree_task_fence_retire(&fence_task, &pending_submission);
EXPECT_TRUE(iree_task_submission_is_empty(&pending_submission));
iree_task_scope_deinitialize(&scope);
}
TEST(ScopeTest, WaitIdleSuccess) {
iree_task_scope_t scope;
iree_task_scope_initialize(iree_make_cstring_view("scope_a"), &scope);
// Current state is OK and idle.
EXPECT_TRUE(iree_task_scope_is_idle(&scope));
EXPECT_TRUE(iree_status_is_ok(iree_task_scope_consume_status(&scope)));
// Enqueue a task to the scope so it is no longer idle.
iree_task_fence_t fence_task;
iree_task_fence_initialize(&scope, iree_wait_primitive_immediate(),
&fence_task);
EXPECT_FALSE(iree_task_scope_is_idle(&scope));
// Spin up a thread to wait on the scope.
std::thread wait_thread([&]() {
EXPECT_FALSE(iree_task_scope_is_idle(&scope));
EXPECT_TRUE(iree_status_is_ok(
iree_task_scope_wait_idle(&scope, IREE_TIME_INFINITE_FUTURE)));
EXPECT_TRUE(iree_task_scope_is_idle(&scope));
});
// Wait a moment for the thread to spin up.
// NOTE: this may flake. Need to see if there's a better way to do this.
std::this_thread::sleep_for(std::chrono::milliseconds(150));
// Complete the task.
iree_task_submission_t pending_submission;
iree_task_submission_initialize(&pending_submission);
iree_task_fence_retire(&fence_task, &pending_submission);
EXPECT_TRUE(iree_task_submission_is_empty(&pending_submission));
EXPECT_TRUE(iree_task_scope_is_idle(&scope));
// Join with the thread - this will hang if it didn't wake correctly.
wait_thread.join();
iree_task_scope_deinitialize(&scope);
}
TEST(ScopeTest, WaitIdleFailure) {
iree_task_scope_t scope;
iree_task_scope_initialize(iree_make_cstring_view("scope_a"), &scope);
// Current state is OK and idle.
EXPECT_TRUE(iree_task_scope_is_idle(&scope));
EXPECT_TRUE(iree_status_is_ok(iree_task_scope_consume_status(&scope)));
// Enqueue a task to the scope so it is no longer idle.
iree_task_fence_t fence_task;
iree_task_fence_initialize(&scope, iree_wait_primitive_immediate(),
&fence_task);
EXPECT_FALSE(iree_task_scope_is_idle(&scope));
// Spin up a thread to wait on the scope.
std::thread wait_thread([&]() {
EXPECT_FALSE(iree_task_scope_is_idle(&scope));
EXPECT_TRUE(iree_status_is_ok(
iree_task_scope_wait_idle(&scope, IREE_TIME_INFINITE_FUTURE)));
EXPECT_TRUE(iree_task_scope_is_idle(&scope));
});
// Wait a moment for the thread to spin up.
// NOTE: this may flake. Need to see if there's a better way to do this.
std::this_thread::sleep_for(std::chrono::milliseconds(150));
// Set the failure state.
iree_task_scope_fail(
&scope, iree_make_status(IREE_STATUS_FAILED_PRECONDITION, "whoops"));
EXPECT_FALSE(iree_task_scope_is_idle(&scope));
// Complete the task.
// Note that even if a scope fails we still must complete the tasks so it
// becomes idle. This ensures that if the scope state is used to control
// deallocation we don't go deallocating the tasks still in flight and waiting
// to gracefully fail.
iree_task_submission_t pending_submission;
iree_task_submission_initialize(&pending_submission);
iree_task_fence_retire(&fence_task, &pending_submission);
EXPECT_TRUE(iree_task_submission_is_empty(&pending_submission));
EXPECT_TRUE(iree_task_scope_is_idle(&scope));
// Join with the thread - this will hang if it didn't wake correctly.
wait_thread.join();
iree_task_scope_deinitialize(&scope);
}
} // namespace