mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
Change the code generation patterns for `async let` bindings to use an ABI based on the following functions: - `swift_asyncLet_begin`, which starts an `async let` child task, but which additionally now associates the `async let` with a caller-owned buffer to receive the result of the task. This is intended to allow the task to emplace its result in caller-owned memory, allowing the child task to be deallocated after completion without invalidating the result buffer. - `swift_asyncLet_get[_throwing]`, which replaces `swift_asyncLet_wait[_throwing]`. Instead of returning a copy of the value, this entry point concerns itself with populating the local buffer. If the buffer hasn't been populated, then it awaits completion of the task and emplaces the result in the buffer; otherwise, it simply returns. The caller can then read the result out of its owned memory. These entry points are intended to be used before every read from the `async let` binding, after which point the local buffer is guaranteed to contain an initialized value. - `swift_asyncLet_finish`, which replaces `swift_asyncLet_end`. Unlike `_end`, this variant is async and will suspend the parent task after cancelling the child to ensure it finishes before cleaning up. The local buffer will also be deinitialized if necessary. This is intended to be used on exit from an `async let` scope, to handle cleaning up the local buffer if necessary as well as cancelling, awaiting, and deallocating the child task. - `swift_asyncLet_consume[_throwing]`, which combines `get` and `finish`. This will await completion of the task, leaving the result value in the result buffer (or propagating the error, if it throws), while destroying and deallocating the child task. This is intended as an optimization for reading `async let` variables that are read exactly once by their parent task. To avoid an epoch break with existing swiftinterfaces and ABI clients, the old builtins and entry points are kept intact for now, but SILGen now only generates code using the new interface. This new interface fixes several issues with the old async let codegen, including use-after-free crashes if the `async let` was never awaited, and the inability to read from an `async let` variable more than once. rdar://77855176
147 lines
4.2 KiB
C++
147 lines
4.2 KiB
C++
//===--- TaskOptions.h - ABI structures for task options --------*- 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 task options.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#ifndef SWIFT_ABI_TASK_OPTIONS_H
|
|
#define SWIFT_ABI_TASK_OPTIONS_H
|
|
|
|
#include "swift/ABI/TaskLocal.h"
|
|
#include "swift/ABI/Executor.h"
|
|
#include "swift/ABI/HeapObject.h"
|
|
#include "swift/ABI/Metadata.h"
|
|
#include "swift/ABI/MetadataValues.h"
|
|
#include "swift/Runtime/Config.h"
|
|
#include "swift/Basic/STLExtras.h"
|
|
#include "llvm/Support/Casting.h"
|
|
|
|
namespace swift {
|
|
|
|
// ==== ------------------------------------------------------------------------
|
|
// ==== Task Options, for creating and waiting on tasks
|
|
|
|
/// The abstract base class for all options that may be used
|
|
/// to configure a newly spawned task.
|
|
class TaskOptionRecord {
|
|
public:
|
|
const TaskOptionRecordFlags Flags;
|
|
TaskOptionRecord *Parent;
|
|
|
|
TaskOptionRecord(TaskOptionRecordKind kind,
|
|
TaskOptionRecord *parent = nullptr)
|
|
: Flags(kind), Parent(parent) { }
|
|
|
|
TaskOptionRecord(const TaskOptionRecord &) = delete;
|
|
TaskOptionRecord &operator=(const TaskOptionRecord &) = delete;
|
|
|
|
TaskOptionRecordKind getKind() const {
|
|
return Flags.getKind();
|
|
}
|
|
|
|
TaskOptionRecord *getParent() const {
|
|
return Parent;
|
|
}
|
|
};
|
|
|
|
/******************************************************************************/
|
|
/****************************** TASK OPTIONS **********************************/
|
|
/******************************************************************************/
|
|
|
|
class TaskGroupTaskOptionRecord : public TaskOptionRecord {
|
|
TaskGroup * const Group;
|
|
|
|
public:
|
|
TaskGroupTaskOptionRecord(TaskGroup *group)
|
|
: TaskOptionRecord(TaskOptionRecordKind::TaskGroup),
|
|
Group(group) {}
|
|
|
|
TaskGroup *getGroup() const {
|
|
return Group;
|
|
}
|
|
|
|
static bool classof(const TaskOptionRecord *record) {
|
|
return record->getKind() == TaskOptionRecordKind::TaskGroup;
|
|
}
|
|
};
|
|
|
|
|
|
/// Task option to specify on what executor the task should be executed.
|
|
///
|
|
/// Not passing this option implies that that a "best guess" or good default
|
|
/// executor should be used instead, most often this may mean the global
|
|
/// concurrent executor, or the enclosing actor's executor.
|
|
class ExecutorTaskOptionRecord : public TaskOptionRecord {
|
|
const ExecutorRef Executor;
|
|
|
|
public:
|
|
ExecutorTaskOptionRecord(ExecutorRef executor)
|
|
: TaskOptionRecord(TaskOptionRecordKind::Executor),
|
|
Executor(executor) {}
|
|
|
|
ExecutorRef getExecutor() const {
|
|
return Executor;
|
|
}
|
|
|
|
static bool classof(const TaskOptionRecord *record) {
|
|
return record->getKind() == TaskOptionRecordKind::Executor;
|
|
}
|
|
};
|
|
|
|
/// DEPRECATED. AsyncLetWithBufferTaskOptionRecord is used instead.
|
|
/// Task option to specify that the created task is for an 'async let'.
|
|
class AsyncLetTaskOptionRecord : public TaskOptionRecord {
|
|
AsyncLet *asyncLet;
|
|
|
|
public:
|
|
AsyncLetTaskOptionRecord(AsyncLet *asyncLet)
|
|
: TaskOptionRecord(TaskOptionRecordKind::AsyncLet),
|
|
asyncLet(asyncLet) {}
|
|
|
|
AsyncLet *getAsyncLet() const {
|
|
return asyncLet;
|
|
}
|
|
|
|
static bool classof(const TaskOptionRecord *record) {
|
|
return record->getKind() == TaskOptionRecordKind::AsyncLet;
|
|
}
|
|
};
|
|
|
|
class AsyncLetWithBufferTaskOptionRecord : public TaskOptionRecord {
|
|
AsyncLet *asyncLet;
|
|
void *resultBuffer;
|
|
|
|
public:
|
|
AsyncLetWithBufferTaskOptionRecord(AsyncLet *asyncLet,
|
|
void *resultBuffer)
|
|
: TaskOptionRecord(TaskOptionRecordKind::AsyncLetWithBuffer),
|
|
asyncLet(asyncLet),
|
|
resultBuffer(resultBuffer) {}
|
|
|
|
AsyncLet *getAsyncLet() const {
|
|
return asyncLet;
|
|
}
|
|
|
|
void *getResultBuffer() const {
|
|
return resultBuffer;
|
|
}
|
|
|
|
static bool classof(const TaskOptionRecord *record) {
|
|
return record->getKind() == TaskOptionRecordKind::AsyncLetWithBuffer;
|
|
}
|
|
};
|
|
|
|
} // end namespace swift
|
|
|
|
#endif
|