Files
swift-mirror/include/swift/ABI/AsyncLet.h
Joe Groff 439edbce1f Handle multiple awaits and suspend-on-exit for async let tasks.
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
2021-07-22 10:19:31 -07:00

67 lines
2.3 KiB
C++

//===--- AsyncLet.h - ABI structures for async let -00-----------*- 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 groups.
//
//===----------------------------------------------------------------------===//
#ifndef SWIFT_ABI_TASK_ASYNC_LET_H
#define SWIFT_ABI_TASK_ASYNC_LET_H
#include "swift/ABI/Task.h"
#include "swift/ABI/HeapObject.h"
#include "swift/Runtime/Concurrency.h"
#include "swift/Runtime/Config.h"
#include "swift/Basic/RelativePointer.h"
#include "swift/Basic/STLExtras.h"
namespace swift {
/// Represents an in-flight `async let`, i.e. the Task that is computing the
/// result of the async let, along with the awaited status and other metadata.
class alignas(Alignment_AsyncLet) AsyncLet {
public:
// These constructors do not initialize the AsyncLet instance, and the
// destructor does not destroy the AsyncLet instance; you must call
// swift_asyncLet_{start,end} yourself.
constexpr AsyncLet()
: PrivateData{} {}
void *PrivateData[NumWords_AsyncLet];
// TODO: we could offer a "was awaited on" check here
/// Returns the child task that is associated with this async let.
/// The tasks completion is used to fulfil the value represented by this async let.
AsyncTask *getTask() const;
// The compiler preallocates a large fixed space for the `async let`, with the
// intent that most of it be used for the child task context. The next two
// methods return the address and size of that space.
/// Return a pointer to the unused space within the async let block.
void *getPreallocatedSpace();
/// Return the size of the unused space within the async let block.
static size_t getSizeOfPreallocatedSpace();
/// Was the task allocated out of the parent's allocator?
bool didAllocateFromParentTask();
/// Flag that the task was allocated from the parent's allocator.
void setDidAllocateFromParentTask(bool value = true);
};
} // end namespace swift
#endif // SWIFT_ABI_TASK_ASYNC_LET_H