Files
swift-mirror/unittests/runtime/TaskStatus.cpp
Arnold Schwaighofer daa72d3cc5 Add llvm::Attribute::SwiftAsync to the context parameter
* Adds support for generating code that uses swiftasync parameter lowering.

* Currently only arm64's llvm lowering supports the swift_async_context_addr intrinsic.

* Add arm64e pointer signing of updated swift_async_context_addr.

This commit needs the PR llvm-project#2291.

* [runtime] unittests should use just-built compiler if the runtime did

This will start to matter with the introduction of usage of swiftasync parameters which only very recent compilers support.

rdar://71499498
2021-01-22 10:01:55 -08:00

285 lines
9.5 KiB
C++

//===--- TaskStatus.cpp - Unit tests for the task-status API --------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
#include "swift/Runtime/Concurrency.h"
#include "swift/Basic/STLExtras.h"
#include "gtest/gtest.h"
using namespace swift;
namespace {
template <class T> struct ValueContext;
template <class T>
using InvokeFunctionRef =
llvm::function_ref<void(AsyncTask *task,
ExecutorRef executor,
ValueContext<T> *context)>;
using BodyFunctionRef =
llvm::function_ref<void(AsyncTask *task)>;
template <class Storage> struct ValueContext : AsyncContext {
Storage Value;
InvokeFunctionRef<Storage> StoredInvokeFn;
};
// Disable template argument deduction.
template <class T>
using undeduced =
typename std::enable_if<std::is_same<T, T>::value, T>::type;
template <class T>
SWIFT_CC(swift)
static void simpleTaskInvokeFunction(AsyncTask *task, ExecutorRef executor,
SWIFT_ASYNC_CONTEXT AsyncContext *context) {
auto valueContext = static_cast<ValueContext<T>*>(context);
valueContext->StoredInvokeFn(task, executor, valueContext);
// Destroy the stored value.
valueContext->Value.T::~T();
// Return to finish off the task.
// In a normal situation we'd need to free the context, but here
// we know we're at the top level.
valueContext->ResumeParent(task, executor, valueContext->Parent);
}
template <class T>
static void withSimpleTask(JobFlags flags, T &&value,
undeduced<InvokeFunctionRef<T>> invokeFn,
BodyFunctionRef body) {
auto taskAndContext =
swift_task_create_f(flags, /*parent*/ nullptr,
&simpleTaskInvokeFunction<T>,
sizeof(ValueContext<T>));
auto valueContext =
static_cast<ValueContext<T>*>(taskAndContext.InitialContext);
new (&valueContext->Value) T(std::forward<T>(value));
valueContext->StoredInvokeFn = invokeFn;
// Forward our owning reference to the task into its execution,
// causing it to be destroyed when it completes.
body(taskAndContext.Task);
}
template <class T>
static void withSimpleTask(T &&value,
undeduced<InvokeFunctionRef<T>> invokeFn,
BodyFunctionRef bodyFn) {
withSimpleTask(JobKind::Task, std::forward<T>(value), invokeFn, bodyFn);
}
static ExecutorRef createFakeExecutor(uintptr_t value) {
return ExecutorRef::forDefaultActor(reinterpret_cast<DefaultActor*>(value));
}
} // end anonymous namespace
TEST(TaskStatusTest, basicTasks) {
AsyncTask *createdTask = nullptr;
auto createdExecutor = createFakeExecutor(1234);
bool hasRun = false;
struct Storage { int value; };
withSimpleTask(Storage{47},
[&](AsyncTask *task, ExecutorRef executor,
ValueContext<Storage> *context) {
// The task passed in should be the task we created.
EXPECT_EQ(createdTask, task);
// The executor passed in should be the executor we created.
EXPECT_EQ(createdExecutor, executor);
// We shouldn't have run yet.
EXPECT_FALSE(hasRun);
// The context should've been initialized correctly.
// (This is really just testing our harness, not the runtime.)
EXPECT_EQ(47, context->Value.value);
hasRun = true;
}, [&](AsyncTask *task) {
createdTask = task;
EXPECT_FALSE(hasRun);
createdTask->run(createdExecutor);
EXPECT_TRUE(hasRun);
createdTask = nullptr;
});
EXPECT_TRUE(hasRun);
EXPECT_EQ(nullptr, createdTask);
}
TEST(TaskStatusTest, cancellation_simple) {
struct Storage { int value; };
withSimpleTask(Storage{47},
[&](AsyncTask *task, ExecutorRef executor,
ValueContext<Storage> *context) {
EXPECT_FALSE(task->isCanceled());
swift_task_cancel(task);
EXPECT_TRUE(task->isCanceled());
swift_task_cancel(task);
EXPECT_TRUE(task->isCanceled());
}, [&](AsyncTask *task) {
task->run(createFakeExecutor(1234));
});
}
// Test basic deadline mechanics (other than actually setting up
// something to cancel the task). Also tests adding and removing
// records quite a bit.
TEST(TaskStatusTest, deadline) {
struct Storage { int value; };
withSimpleTask(Storage{47},
[&](AsyncTask *task, ExecutorRef executor,
ValueContext<Storage> *context) {
EXPECT_FALSE(task->isCanceled());
TaskDeadline deadlineOne = { 1234 };
TaskDeadline deadlineTwo = { 2345 };
DeadlineStatusRecord recordOne(deadlineOne);
DeadlineStatusRecord recordTwo(deadlineTwo);
bool result;
NearestTaskDeadline nearest = swift_task_getNearestDeadline(task);
EXPECT_EQ(NearestTaskDeadline::None, nearest.ValueKind);
// Add deadline 1. Check that we haven't been cancelled yet.
result = swift_task_addStatusRecord(task, &recordOne);
EXPECT_TRUE(result);
// There should now be an active deadline.
nearest = swift_task_getNearestDeadline(task);
EXPECT_EQ(NearestTaskDeadline::Active, nearest.ValueKind);
EXPECT_EQ(deadlineOne, nearest.Value);
// Remove deadline 1. Check that we haven't been cancelled yet.
result = swift_task_removeStatusRecord(task, &recordOne);
EXPECT_TRUE(result);
// There shouldn't be an active deadline anymore.
nearest = swift_task_getNearestDeadline(task);
EXPECT_EQ(NearestTaskDeadline::None, nearest.ValueKind);
// Add deadline 1, then 2.
result = swift_task_addStatusRecord(task, &recordOne);
EXPECT_TRUE(result);
result = swift_task_addStatusRecord(task, &recordTwo);
EXPECT_TRUE(result);
// The nearest deadline should be deadline 1.
nearest = swift_task_getNearestDeadline(task);
EXPECT_EQ(NearestTaskDeadline::Active, nearest.ValueKind);
EXPECT_EQ(deadlineOne, nearest.Value);
// Remove the deadlines.
result = swift_task_removeStatusRecord(task, &recordTwo);
EXPECT_TRUE(result);
result = swift_task_removeStatusRecord(task, &recordOne);
EXPECT_TRUE(result);
// Add deadline 2, then 1s.
result = swift_task_addStatusRecord(task, &recordTwo);
EXPECT_TRUE(result);
// In the middle, the nearest deadline should be deadline 2.
nearest = swift_task_getNearestDeadline(task);
EXPECT_EQ(NearestTaskDeadline::Active, nearest.ValueKind);
EXPECT_EQ(deadlineTwo, nearest.Value);
result = swift_task_addStatusRecord(task, &recordOne);
EXPECT_TRUE(result);
// The nearest deadline should be deadline 1.
nearest = swift_task_getNearestDeadline(task);
EXPECT_EQ(NearestTaskDeadline::Active, nearest.ValueKind);
EXPECT_EQ(deadlineOne, nearest.Value);
// Remove the deadlines.
result = swift_task_removeStatusRecord(task, &recordOne);
EXPECT_TRUE(result);
result = swift_task_removeStatusRecord(task, &recordTwo);
EXPECT_TRUE(result);
// Do the same thing with tryAddStatus.
result = swift_task_tryAddStatusRecord(task, &recordTwo);
EXPECT_TRUE(result);
result = swift_task_tryAddStatusRecord(task, &recordOne);
EXPECT_TRUE(result);
// The nearest deadline should be deadline 1.
nearest = swift_task_getNearestDeadline(task);
EXPECT_EQ(NearestTaskDeadline::Active, nearest.ValueKind);
EXPECT_EQ(deadlineOne, nearest.Value);
result = swift_task_removeStatusRecord(task, &recordOne);
EXPECT_TRUE(result);
result = swift_task_removeStatusRecord(task, &recordTwo);
EXPECT_TRUE(result);
// Remove out of order.
result = swift_task_addStatusRecord(task, &recordTwo);
EXPECT_TRUE(result);
result = swift_task_addStatusRecord(task, &recordOne);
EXPECT_TRUE(result);
// The nearest deadline should be deadline 1.
nearest = swift_task_getNearestDeadline(task);
EXPECT_EQ(NearestTaskDeadline::Active, nearest.ValueKind);
EXPECT_EQ(deadlineOne, nearest.Value);
result = swift_task_removeStatusRecord(task, &recordTwo);
EXPECT_TRUE(result);
result = swift_task_removeStatusRecord(task, &recordOne);
EXPECT_TRUE(result);
// Add deadline 2, then cancel.
result = swift_task_addStatusRecord(task, &recordTwo);
EXPECT_TRUE(result);
// The nearest deadline should be deadline 2.
nearest = swift_task_getNearestDeadline(task);
EXPECT_EQ(NearestTaskDeadline::Active, nearest.ValueKind);
EXPECT_EQ(deadlineTwo, nearest.Value);
// Cancel.
swift_task_cancel(task);
EXPECT_TRUE(task->isCanceled());
// We should report already cancelled now.
nearest = swift_task_getNearestDeadline(task);
EXPECT_EQ(NearestTaskDeadline::AlreadyCancelled, nearest.ValueKind);
// Add deadline 1.
result = swift_task_addStatusRecord(task, &recordOne);
EXPECT_FALSE(result);
nearest = swift_task_getNearestDeadline(task);
EXPECT_EQ(NearestTaskDeadline::AlreadyCancelled, nearest.ValueKind);
result = swift_task_removeStatusRecord(task, &recordOne);
EXPECT_FALSE(result);
result = swift_task_tryAddStatusRecord(task, &recordOne);
EXPECT_FALSE(result);
result = swift_task_removeStatusRecord(task, &recordTwo);
EXPECT_FALSE(result);
nearest = swift_task_getNearestDeadline(task);
EXPECT_EQ(NearestTaskDeadline::AlreadyCancelled, nearest.ValueKind);
EXPECT_TRUE(task->isCanceled());
}, [&](AsyncTask *task) {
task->run(createFakeExecutor(1234));
});
}