Skip to content

Commit

Permalink
Implement a simple library for task cancellation and status management.
Browse files Browse the repository at this point in the history
There are things about this that I'm far from sold on.  In
particular, I'm concerned that in order to implement escalation
correctly, we're going to have to add a status record for the
fact that the task is being executed, which means we're going
to have to potentially wait to acquire the status lock; overall,
that means making an extra runtime function call and doing some
atomics whenever we resume or suspend a task, which is an
uncomfortable amount of overhead.

The testing here is pretty grossly inadequate, but I wanted to
lay down the groundwork here.
  • Loading branch information
rjmccall committed Oct 15, 2020
1 parent 1cc3a57 commit 8ac4362
Show file tree
Hide file tree
Showing 12 changed files with 1,712 additions and 0 deletions.
171 changes: 171 additions & 0 deletions include/swift/ABI/MetadataValues.h
Expand Up @@ -1155,6 +1155,16 @@ namespace SpecialPointerAuthDiscriminators {

/// Actor enqueue(partialTask:).
const uint16_t ActorEnqueuePartialTask = 0x8f3d;

/// Jobs, tasks, and continuations.
const uint16_t JobInvokeFunction = 0xcc64; // = 52324
const uint16_t TaskResumeFunction = 0x2c42; // = 11330
const uint16_t TaskResumeContext = 0x753a; // = 30010
const uint16_t AsyncContextParent = 0xbda2; // = 48546
const uint16_t AsyncContextResume = 0xd707; // = 55047
const uint16_t AsyncContextYield = 0xe207; // = 57863
const uint16_t CancellationNotificationFunction = 0x1933; // = 6451
const uint16_t EscalationNotificationFunction = 0x5be4; // = 23524
}

/// The number of arguments that will be passed directly to a generic
Expand Down Expand Up @@ -1867,6 +1877,167 @@ class IntegerLiteralFlags {
}
};

/// Kinds of schedulable job.s
enum class JobKind : size_t {
// There are 256 possible job kinds.

/// An AsyncTask.
Task = 0,

/// Job kinds >= 192 are private to the implementation.
First_Reserved = 192
};

/// The priority of a job. Higher priorities are larger values.
enum class JobPriority : size_t {
// This is modelled off of Dispatch.QoS, and the values are directly
// stolen from there.
UserInteractive = 0x21,
UserInitiated = 0x19,
Default = 0x15,
Utility = 0x11,
Background = 0x09,
Unspecified = 0x00,
};

/// Flags for schedulable jobs.
class JobFlags : public FlagSet<size_t> {
public:
enum {
Kind = 0,
Kind_width = 8,

Priority = 8,
Priority_width = 8,

// 8 bits reserved for more generic job flags.

// Kind-specific flags.

Task_IsHeapObject = 24,
Task_IsChildTask = 25,
Task_IsFuture = 26
};

explicit JobFlags(size_t bits) : FlagSet(bits) {}
JobFlags(JobKind kind) { setKind(kind); }
constexpr JobFlags() {}

FLAGSET_DEFINE_FIELD_ACCESSORS(Kind, Kind_width, JobKind,
getKind, setKind)

FLAGSET_DEFINE_FIELD_ACCESSORS(Priority, Priority_width, JobPriority,
getPriority, setPriority)

bool isAsyncTask() const {
return getKind() == JobKind::Task;
}

FLAGSET_DEFINE_FLAG_ACCESSORS(Task_IsHeapObject,
task_isHeapObject,
task_setIsHeapObject)
FLAGSET_DEFINE_FLAG_ACCESSORS(Task_IsChildTask,
task_isChildTask,
task_setIsChildTask)
FLAGSET_DEFINE_FLAG_ACCESSORS(Task_IsFuture,
task_isFuture,
task_setIsFuture)

};

/// Kinds of task status record.
enum class TaskStatusRecordKind : uint8_t {
/// A DeadlineStatusRecord, which represents an active deadline.
Deadline = 0,

/// A ChildTaskStatusRecord, which represents the potential for
/// active child tasks.
ChildTask = 1,

/// A CancellationNotificationStatusRecord, which represents the
/// need to call a custom function when the task is cancelled.
CancellationNotification = 2,

/// An EscalationNotificationStatusRecord, which represents the
/// need to call a custom function when the task's priority is
/// escalated.
EscalationNotification = 3,

// Kinds >= 192 are private to the implementation.
First_Reserved = 192,
Private_RecordLock = 192
};

/// Flags for cancellation records.
class TaskStatusRecordFlags : public FlagSet<size_t> {
public:
enum {
Kind = 0,
Kind_width = 8,
};

explicit TaskStatusRecordFlags(size_t bits) : FlagSet(bits) {}
constexpr TaskStatusRecordFlags() {}
TaskStatusRecordFlags(TaskStatusRecordKind kind) {
setKind(kind);
}

FLAGSET_DEFINE_FIELD_ACCESSORS(Kind, Kind_width, TaskStatusRecordKind,
getKind, setKind)
};

/// Kinds of async context.
enum class AsyncContextKind {
/// An ordinary asynchronous function.
Ordinary = 0,

/// A context which can yield to its caller.
Yielding = 1,

// Other kinds are reserved for interesting special
// intermediate contexts.

// Kinds >= 192 are private to the implementation.
First_Reserved = 192
};

/// Flags for async contexts.
class AsyncContextFlags : public FlagSet<uint32_t> {
public:
enum {
Kind = 0,
Kind_width = 8,

CanThrow = 8,
ShouldNotDeallocate = 9
};

explicit AsyncContextFlags(uint32_t bits) : FlagSet(bits) {}
constexpr AsyncContextFlags() {}
AsyncContextFlags(AsyncContextKind kind) {
setKind(kind);
}

/// The kind of context this represents.
FLAGSET_DEFINE_FIELD_ACCESSORS(Kind, Kind_width, AsyncContextKind,
getKind, setKind)

/// Whether this context is permitted to throw.
FLAGSET_DEFINE_FLAG_ACCESSORS(CanThrow, canThrow, setCanThrow)

/// Whether a function should avoid deallocating its context before
/// returning. It should still pass its caller's context to its
/// return continuation.
///
/// This flag can be set in the caller to optimize context allocation,
/// e.g. if the callee's context size is known statically and simply
/// allocated as part of the caller's context, or if the callee will
/// be called multiple times.
FLAGSET_DEFINE_FLAG_ACCESSORS(ShouldNotDeallocate,
shouldNotDeallocateInCaller,
setShouldNotDeallocateInCaller)
};

} // end namespace swift

#endif // SWIFT_ABI_METADATAVALUES_H

0 comments on commit 8ac4362

Please sign in to comment.