| //===--- 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 |