Skip to content

Commit 8ac4362

Browse files
committed
Implement a simple library for task cancellation and status management.
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.
1 parent 1cc3a57 commit 8ac4362

File tree

12 files changed

+1712
-0
lines changed

12 files changed

+1712
-0
lines changed

include/swift/ABI/MetadataValues.h

Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1155,6 +1155,16 @@ namespace SpecialPointerAuthDiscriminators {
11551155

11561156
/// Actor enqueue(partialTask:).
11571157
const uint16_t ActorEnqueuePartialTask = 0x8f3d;
1158+
1159+
/// Jobs, tasks, and continuations.
1160+
const uint16_t JobInvokeFunction = 0xcc64; // = 52324
1161+
const uint16_t TaskResumeFunction = 0x2c42; // = 11330
1162+
const uint16_t TaskResumeContext = 0x753a; // = 30010
1163+
const uint16_t AsyncContextParent = 0xbda2; // = 48546
1164+
const uint16_t AsyncContextResume = 0xd707; // = 55047
1165+
const uint16_t AsyncContextYield = 0xe207; // = 57863
1166+
const uint16_t CancellationNotificationFunction = 0x1933; // = 6451
1167+
const uint16_t EscalationNotificationFunction = 0x5be4; // = 23524
11581168
}
11591169

11601170
/// The number of arguments that will be passed directly to a generic
@@ -1867,6 +1877,167 @@ class IntegerLiteralFlags {
18671877
}
18681878
};
18691879

1880+
/// Kinds of schedulable job.s
1881+
enum class JobKind : size_t {
1882+
// There are 256 possible job kinds.
1883+
1884+
/// An AsyncTask.
1885+
Task = 0,
1886+
1887+
/// Job kinds >= 192 are private to the implementation.
1888+
First_Reserved = 192
1889+
};
1890+
1891+
/// The priority of a job. Higher priorities are larger values.
1892+
enum class JobPriority : size_t {
1893+
// This is modelled off of Dispatch.QoS, and the values are directly
1894+
// stolen from there.
1895+
UserInteractive = 0x21,
1896+
UserInitiated = 0x19,
1897+
Default = 0x15,
1898+
Utility = 0x11,
1899+
Background = 0x09,
1900+
Unspecified = 0x00,
1901+
};
1902+
1903+
/// Flags for schedulable jobs.
1904+
class JobFlags : public FlagSet<size_t> {
1905+
public:
1906+
enum {
1907+
Kind = 0,
1908+
Kind_width = 8,
1909+
1910+
Priority = 8,
1911+
Priority_width = 8,
1912+
1913+
// 8 bits reserved for more generic job flags.
1914+
1915+
// Kind-specific flags.
1916+
1917+
Task_IsHeapObject = 24,
1918+
Task_IsChildTask = 25,
1919+
Task_IsFuture = 26
1920+
};
1921+
1922+
explicit JobFlags(size_t bits) : FlagSet(bits) {}
1923+
JobFlags(JobKind kind) { setKind(kind); }
1924+
constexpr JobFlags() {}
1925+
1926+
FLAGSET_DEFINE_FIELD_ACCESSORS(Kind, Kind_width, JobKind,
1927+
getKind, setKind)
1928+
1929+
FLAGSET_DEFINE_FIELD_ACCESSORS(Priority, Priority_width, JobPriority,
1930+
getPriority, setPriority)
1931+
1932+
bool isAsyncTask() const {
1933+
return getKind() == JobKind::Task;
1934+
}
1935+
1936+
FLAGSET_DEFINE_FLAG_ACCESSORS(Task_IsHeapObject,
1937+
task_isHeapObject,
1938+
task_setIsHeapObject)
1939+
FLAGSET_DEFINE_FLAG_ACCESSORS(Task_IsChildTask,
1940+
task_isChildTask,
1941+
task_setIsChildTask)
1942+
FLAGSET_DEFINE_FLAG_ACCESSORS(Task_IsFuture,
1943+
task_isFuture,
1944+
task_setIsFuture)
1945+
1946+
};
1947+
1948+
/// Kinds of task status record.
1949+
enum class TaskStatusRecordKind : uint8_t {
1950+
/// A DeadlineStatusRecord, which represents an active deadline.
1951+
Deadline = 0,
1952+
1953+
/// A ChildTaskStatusRecord, which represents the potential for
1954+
/// active child tasks.
1955+
ChildTask = 1,
1956+
1957+
/// A CancellationNotificationStatusRecord, which represents the
1958+
/// need to call a custom function when the task is cancelled.
1959+
CancellationNotification = 2,
1960+
1961+
/// An EscalationNotificationStatusRecord, which represents the
1962+
/// need to call a custom function when the task's priority is
1963+
/// escalated.
1964+
EscalationNotification = 3,
1965+
1966+
// Kinds >= 192 are private to the implementation.
1967+
First_Reserved = 192,
1968+
Private_RecordLock = 192
1969+
};
1970+
1971+
/// Flags for cancellation records.
1972+
class TaskStatusRecordFlags : public FlagSet<size_t> {
1973+
public:
1974+
enum {
1975+
Kind = 0,
1976+
Kind_width = 8,
1977+
};
1978+
1979+
explicit TaskStatusRecordFlags(size_t bits) : FlagSet(bits) {}
1980+
constexpr TaskStatusRecordFlags() {}
1981+
TaskStatusRecordFlags(TaskStatusRecordKind kind) {
1982+
setKind(kind);
1983+
}
1984+
1985+
FLAGSET_DEFINE_FIELD_ACCESSORS(Kind, Kind_width, TaskStatusRecordKind,
1986+
getKind, setKind)
1987+
};
1988+
1989+
/// Kinds of async context.
1990+
enum class AsyncContextKind {
1991+
/// An ordinary asynchronous function.
1992+
Ordinary = 0,
1993+
1994+
/// A context which can yield to its caller.
1995+
Yielding = 1,
1996+
1997+
// Other kinds are reserved for interesting special
1998+
// intermediate contexts.
1999+
2000+
// Kinds >= 192 are private to the implementation.
2001+
First_Reserved = 192
2002+
};
2003+
2004+
/// Flags for async contexts.
2005+
class AsyncContextFlags : public FlagSet<uint32_t> {
2006+
public:
2007+
enum {
2008+
Kind = 0,
2009+
Kind_width = 8,
2010+
2011+
CanThrow = 8,
2012+
ShouldNotDeallocate = 9
2013+
};
2014+
2015+
explicit AsyncContextFlags(uint32_t bits) : FlagSet(bits) {}
2016+
constexpr AsyncContextFlags() {}
2017+
AsyncContextFlags(AsyncContextKind kind) {
2018+
setKind(kind);
2019+
}
2020+
2021+
/// The kind of context this represents.
2022+
FLAGSET_DEFINE_FIELD_ACCESSORS(Kind, Kind_width, AsyncContextKind,
2023+
getKind, setKind)
2024+
2025+
/// Whether this context is permitted to throw.
2026+
FLAGSET_DEFINE_FLAG_ACCESSORS(CanThrow, canThrow, setCanThrow)
2027+
2028+
/// Whether a function should avoid deallocating its context before
2029+
/// returning. It should still pass its caller's context to its
2030+
/// return continuation.
2031+
///
2032+
/// This flag can be set in the caller to optimize context allocation,
2033+
/// e.g. if the callee's context size is known statically and simply
2034+
/// allocated as part of the caller's context, or if the callee will
2035+
/// be called multiple times.
2036+
FLAGSET_DEFINE_FLAG_ACCESSORS(ShouldNotDeallocate,
2037+
shouldNotDeallocateInCaller,
2038+
setShouldNotDeallocateInCaller)
2039+
};
2040+
18702041
} // end namespace swift
18712042

18722043
#endif // SWIFT_ABI_METADATAVALUES_H

0 commit comments

Comments
 (0)