blob: 77efa4d263af588f369468e5e5a48087e9553d02 [file] [log] [blame]
//===--- TaskStatusRecord.h - Structures to track task status --*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2020 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
//
//===----------------------------------------------------------------------===//
//
// Swift ABI describing "status records", the mechanism by which
// tasks track dynamic information about their child tasks, custom
// cancellation hooks, and other information which may need to be exposed
// asynchronously outside of the task.
//
//===----------------------------------------------------------------------===//
#ifndef SWIFT_ABI_TASKSTATUS_H
#define SWIFT_ABI_TASKSTATUS_H
#include "swift/ABI/MetadataValues.h"
#include "swift/ABI/Task.h"
namespace swift {
/// The abstract base class for all sttatus records.
///
/// TaskStatusRecords are typically allocated on the stack (possibly
/// in the task context), partially initialized, and then atomically
/// added to the task with `swift_task_addTaskStatusRecord`. While
/// registered with the task, a status record should only be
/// modified in ways that respect the possibility of asynchronous
/// access by a cancelling thread. In particular, the chain of
/// status records must not be disturbed. When the task leaves
/// the scope that requires the status record, the record can
/// be unregistered from the task with `swift_task_removeTaskStatusRecord`,
/// at which point the memory can be returned to the system.
class TaskStatusRecord {
public:
TaskStatusRecordFlags Flags;
TaskStatusRecord *Parent;
TaskStatusRecord(TaskStatusRecordKind kind,
TaskStatusRecord *parent = nullptr)
: Flags(kind) {
resetParent(parent);
}
TaskStatusRecord(const TaskStatusRecord &) = delete;
TaskStatusRecord &operator=(const TaskStatusRecord &) = delete;
TaskStatusRecordKind getKind() const {
return Flags.getKind();
}
TaskStatusRecord *getParent() const {
return Parent;
}
/// Change the parent of this unregistered status record to the
/// given record.
///
/// This should be used when the record has been previously initialized
/// without knowing what the true parent is. If we decide to cache
/// important information (e.g. the earliest timeout) in the innermost
/// status record, this is the method that should fill that in
/// from the parent.
void resetParent(TaskStatusRecord *newParent) {
Parent = newParent;
// TODO: cache
}
/// Splice a record out of the status-record chain.
///
/// Unlike resetParent, this assumes that it's just removing one or
/// more records from the chain and that there's no need to do any
/// extra cache manipulation.
void spliceParent(TaskStatusRecord *newParent) {
Parent = newParent;
}
};
inline TaskStatusRecord *
ActiveTaskStatus::getStatusRecordParent(TaskStatusRecord *ptr) {
return ptr->getParent();
}
/// A deadline for the task. If this is reached, the task will be
/// automatically cancelled. The deadline can also be queried and used
/// in other ways.
struct TaskDeadline {
// FIXME: I don't really know what this should look like right now.
// It's probably target-specific.
uint64_t Value;
bool operator==(const TaskDeadline &other) const {
return Value == other.Value;
}
bool operator<(const TaskDeadline &other) const {
return Value < other.Value;
}
};
/// A status record which states that there's an active deadline
/// within the task.
class DeadlineStatusRecord : public TaskStatusRecord {
TaskDeadline Deadline;
public:
DeadlineStatusRecord(TaskDeadline deadline)
: TaskStatusRecord(TaskStatusRecordKind::Deadline),
Deadline(deadline) {}
TaskDeadline getDeadline() const {
return Deadline;
}
static bool classof(const TaskStatusRecord *record) {
return record->getKind() == TaskStatusRecordKind::Deadline;
}
};
/// A status record which states that a task has one or
/// more active child tasks.
class ChildTaskStatusRecord : public TaskStatusRecord {
/// FIXME: should this be an array? How are things like task groups supposed
/// to actually manage this? Should it be atomically modifiable?
AsyncTask *FirstChild;
public:
ChildTaskStatusRecord(AsyncTask *child)
: TaskStatusRecord(TaskStatusRecordKind::ChildTask),
FirstChild(child) {}
/// Return the first child linked by this record. This may be null;
/// if not, it (and all of its successors) are guaranteed to satisfy
/// `isChildTask()`.
AsyncTask *getFirstChild() const {
return FirstChild;
}
static AsyncTask *getNextChildTask(AsyncTask *task) {
return task->childFragment()->getNextChild();
}
using child_iterator = LinkedListIterator<AsyncTask, getNextChildTask>;
llvm::iterator_range<child_iterator> children() const {
return child_iterator::rangeBeginning(getFirstChild());
}
static bool classof(const TaskStatusRecord *record) {
return record->getKind() == TaskStatusRecordKind::ChildTask;
}
};
/// A cancellation record which states that a task has an arbitrary
/// function that needs to be called if the task is cancelled.
///
/// The end of any call to the function will be ordered before the
/// end of a call to unregister this record from the task. That is,
/// code may call `swift_task_removeTaskStatusRecord` and freely
/// assume after it returns that this function will not be
/// subsequently used.
class CancellationNotificationStatusRecord : public TaskStatusRecord {
public:
using FunctionType = void (void *);
private:
FunctionType * __ptrauth_swift_cancellation_notification_function Function;
void *Argument;
public:
CancellationNotificationStatusRecord(FunctionType *fn, void *arg)
: TaskStatusRecord(TaskStatusRecordKind::CancellationNotification),
Function(fn), Argument(arg) {}
void run() {
Function(Argument);
}
static bool classof(const TaskStatusRecord *record) {
return record->getKind() == TaskStatusRecordKind::CancellationNotification;
}
};
/// A status record which says that a task has an arbitrary
/// function that needs to be called if the task's priority is escalated.
///
/// The end of any call to the function will be ordered before the
/// end of a call to unregister this record from the task. That is,
/// code may call `swift_task_removeTaskStatusRecord` and freely
/// assume after it returns that this function will not be
/// subsequently used.
class EscalationNotificationStatusRecord : public TaskStatusRecord {
public:
using FunctionType = void (void *, JobPriority);
private:
FunctionType * __ptrauth_swift_escalation_notification_function Function;
void *Argument;
public:
EscalationNotificationStatusRecord(FunctionType *fn, void *arg)
: TaskStatusRecord(TaskStatusRecordKind::EscalationNotification),
Function(fn), Argument(arg) {}
void run(JobPriority newPriority) {
Function(Argument, newPriority);
}
static bool classof(const TaskStatusRecord *record) {
return record->getKind() == TaskStatusRecordKind::EscalationNotification;
}
};
} // end namespace swift
#endif