mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
* Backdeploy swift_task_future_wait
This patch adds the implementation for `swift_task_future_wait`
entrypoint to the backdeploy library.
This involves pulling in `AsyncTask::waitFuture`, which relies on a fair
bit.
Please note, this pulls in the `StaticMutex` implementation from Swift
5.6. There are some challenges here. The concurrency version of the
`StaticMutex` involves a fairly nasty set of ODR violations in the
normal setup. See `public/Concurrency/Mutex.cpp`, which includes the
Mutex implementations cpp files directly, while defining a single macro
to replace the implementation of swift::fatalError with
swift_concurrency_fatalError. We only need the concurrency mutex (at
least for now), so I have hard-coded the `swift_concurrency_fatalError`
version into this library. If we should need the other implementation,
we are forced to include ODR-related undefined behavior.
We need symbols from C++, so I've added an implicit linker flag whenever
the static library is used, namely, it passes `-lc++` to the linker.
Since we only backdeploy on Apple platforms, this should be fine.
Some of the platform runtimes we need to backdeploy to have the
enter/exitThreadLocalContext functions defined, while others don't. We
define our own backdeploy56 shim function that dlsym's the function
pointer for these symbols if we have exclusivity checking available.
Otherwise, it doesn't do anything. If concurrency exclusivity checking
is available, we'll use it, otherwise we wont'.
The same dlsym check is done for `swift_task_escalate`. Not all
platforms we need to backdeploy to have a concurrency runtime. The
symbols that do need to use pieces of the concurrency runtime should not
be getting hit when deploying to systems that don't have concurrency. In
the event that you've gotten around the language blocking you from
calling these symbols and you've managed to call concurrency pieces
without using concurrency, we'll abort because something is seriously
wrong.
* Backdeploy swift_task_future_wait_throwing
Drop the remaining pieces in for adding
`swift_task_future_wait_throwing`.
* Apply task_wait_future fix
Actually apply the fix from ef80a315f8.
This deviates slightly from the original patch.
AsyncTask::PrivateStorage::_Status() does not exist in the Swift 5.6
library. Instead I am using `AsyncTask::PrivateStorage::Status`.
* Workaround missing compiler-rt linking
Working around the missing link against compiler-rt in these test.
They are a bit brittle as if anything in them uses compiler-rt, they
will start failing. The backdeploy 5.6 library uses some symbols from
compiler-rt, thus causes them to fail to link.
Disabling the runtime compatibility version checking to avoid these
symbols. This should be fine for the MicroStdlib test, but we should fix
'%target-ld' to handle this better in the future.
rdar://100868842
225 lines
8.2 KiB
C++
225 lines
8.2 KiB
C++
//===--- TaskLocal.h - ABI of task local values -----------------*- 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 locals.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#ifndef SWIFT_ABI_TASKLOCAL_BACKDEPLOY56_H
|
|
#define SWIFT_ABI_TASKLOCAL_BACKDEPLOY56_H
|
|
|
|
#include "swift/ABI/HeapObject.h"
|
|
#include "swift/ABI/Metadata.h"
|
|
#include "swift/ABI/MetadataValues.h"
|
|
|
|
namespace swift {
|
|
class AsyncTask;
|
|
struct OpaqueValue;
|
|
struct SwiftError;
|
|
class TaskStatusRecord;
|
|
class TaskGroup;
|
|
|
|
// ==== Task Locals Values ---------------------------------------------------
|
|
|
|
class TaskLocal {
|
|
public:
|
|
/// Type of the pointed at `next` task local item.
|
|
enum class NextLinkType : uintptr_t {
|
|
/// The storage pointer points at the next TaskLocal::Item in this task.
|
|
IsNext = 0b00,
|
|
/// The storage pointer points at a item stored by another AsyncTask.
|
|
///
|
|
/// Note that this may not necessarily be the same as the task's parent
|
|
/// task -- we may point to a super-parent if we know / that the parent
|
|
/// does not "contribute" any task local values. This is to speed up
|
|
/// lookups by skipping empty parent tasks during get(), and explained
|
|
/// in depth in `createParentLink`.
|
|
IsParent = 0b01,
|
|
};
|
|
|
|
class Item {
|
|
private:
|
|
/// Mask used for the low status bits in a task local chain item.
|
|
static const uintptr_t statusMask = 0x03;
|
|
|
|
/// Pointer to one of the following:
|
|
/// - next task local item as OpaqueValue* if it is task-local allocated
|
|
/// - next task local item as HeapObject* if it is heap allocated "heavy"
|
|
/// - the parent task's TaskLocal::Storage
|
|
///
|
|
/// Low bits encode `NextLinkType`, based on which the type of the pointer
|
|
/// is determined.
|
|
uintptr_t next;
|
|
|
|
public:
|
|
/// The type of the key with which this value is associated.
|
|
const HeapObject *key;
|
|
/// The type of the value stored by this item.
|
|
const Metadata *valueType;
|
|
|
|
// Trailing storage for the value itself. The storage will be
|
|
// uninitialized or contain an instance of \c valueType.
|
|
|
|
/// Returns true if this item is a 'parent pointer'.
|
|
///
|
|
/// A parent pointer is special kind of `Item` is created when pointing at
|
|
/// the parent storage, forming a chain of task local items spanning multiple
|
|
/// tasks.
|
|
bool isParentPointer() const {
|
|
return !valueType;
|
|
}
|
|
|
|
protected:
|
|
explicit Item()
|
|
: next(0),
|
|
key(nullptr),
|
|
valueType(nullptr) {}
|
|
|
|
explicit Item(const HeapObject *key, const Metadata *valueType)
|
|
: next(0),
|
|
key(key),
|
|
valueType(valueType) {}
|
|
|
|
public:
|
|
/// Item which does not by itself store any value, but only points
|
|
/// to the nearest task-local-value containing parent's first task item.
|
|
///
|
|
/// This item type is used to link to the appropriate parent task's item,
|
|
/// when the current task itself does not have any task local values itself.
|
|
///
|
|
/// When a task actually has its own task locals, it should rather point
|
|
/// to the parent's *first* task-local item in its *last* item, extending
|
|
/// the Item linked list into the appropriate parent.
|
|
static Item *createParentLink(AsyncTask *task, AsyncTask *parent);
|
|
|
|
static Item *createLink(AsyncTask *task,
|
|
const HeapObject *key,
|
|
const Metadata *valueType);
|
|
|
|
void destroy(AsyncTask *task);
|
|
|
|
Item *getNext() {
|
|
return reinterpret_cast<Item *>(next & ~statusMask);
|
|
}
|
|
|
|
NextLinkType getNextLinkType() const {
|
|
return static_cast<NextLinkType>(next & statusMask);
|
|
}
|
|
|
|
/// Item does not contain any actual value, and is only used to point at
|
|
/// a specific parent item.
|
|
bool isEmpty() const {
|
|
return !valueType;
|
|
}
|
|
|
|
/// Retrieve a pointer to the storage of the value.
|
|
OpaqueValue *getStoragePtr() {
|
|
return reinterpret_cast<OpaqueValue *>(
|
|
reinterpret_cast<char *>(this) + storageOffset(valueType));
|
|
}
|
|
|
|
void copyTo(AsyncTask *task);
|
|
|
|
/// Compute the offset of the storage from the base of the item.
|
|
static size_t storageOffset(const Metadata *valueType) {
|
|
size_t offset = sizeof(Item);
|
|
|
|
if (valueType) {
|
|
size_t alignment = valueType->vw_alignment();
|
|
return (offset + alignment - 1) & ~(alignment - 1);
|
|
} else {
|
|
return offset;
|
|
}
|
|
}
|
|
|
|
/// Determine the size of the item given a particular value type.
|
|
static size_t itemSize(const Metadata *valueType) {
|
|
size_t offset = storageOffset(valueType);
|
|
if (valueType) {
|
|
offset += valueType->vw_size();
|
|
}
|
|
return offset;
|
|
}
|
|
};
|
|
|
|
class Storage {
|
|
friend class TaskLocal::Item;
|
|
private:
|
|
/// A stack (single-linked list) of task local values.
|
|
///
|
|
/// Once task local values within this task are traversed, the list continues
|
|
/// to the "next parent that contributes task local values," or if no such
|
|
/// parent exists it terminates with null.
|
|
///
|
|
/// If the TaskLocalValuesFragment was allocated, it is expected that this
|
|
/// value should be NOT null; it either has own values, or at least one
|
|
/// parent that has values. If this task does not have any values, the head
|
|
/// pointer MAY immediately point at this task's parent task which has values.
|
|
///
|
|
/// ### Concurrency
|
|
/// Access to the head is only performed from the task itself, when it
|
|
/// creates child tasks, the child during creation will inspect its parent's
|
|
/// task local value stack head, and point to it. This is done on the calling
|
|
/// task, and thus needs not to be synchronized. Subsequent traversal is
|
|
/// performed by child tasks concurrently, however they use their own
|
|
/// pointers/stack and can never mutate the parent's stack.
|
|
///
|
|
/// The stack is only pushed/popped by the owning task, at the beginning and
|
|
/// end a `body` block of `withLocal(_:boundTo:body:)` respectively.
|
|
///
|
|
/// Correctness of the stack strongly relies on the guarantee that tasks
|
|
/// never outline a scope in which they are created. Thanks to this, if
|
|
/// tasks are created inside the `body` of `withLocal(_:,boundTo:body:)`
|
|
/// all tasks created inside the `withLocal` body must complete before it
|
|
/// returns, as such, any child tasks potentially accessing the value stack
|
|
/// are guaranteed to be completed by the time we pop values off the stack
|
|
/// (after the body has completed).
|
|
TaskLocal::Item *head = nullptr;
|
|
|
|
public:
|
|
|
|
void initializeLinkParent(AsyncTask *task, AsyncTask *parent);
|
|
|
|
void pushValue(AsyncTask *task,
|
|
const HeapObject *key,
|
|
/* +1 */ OpaqueValue *value, const Metadata *valueType);
|
|
|
|
OpaqueValue* getValue(AsyncTask *task, const HeapObject *key);
|
|
|
|
/// Returns `true` of more bindings remain in this storage,
|
|
/// and `false` if the just popped value was the last one and the storage
|
|
/// can be safely disposed of.
|
|
bool popValue(AsyncTask *task);
|
|
|
|
/// Copy all task-local bindings to the target task.
|
|
///
|
|
/// The new bindings allocate their own items and can out-live the current task.
|
|
///
|
|
/// ### Optimizations
|
|
/// Only the most recent binding of a value is copied over, i.e. given
|
|
/// a key bound to `A` and then `B`, only the `B` binding will be copied.
|
|
/// This is safe and correct because the new task would never have a chance
|
|
/// to observe the `A` value, because it semantically will never observe a
|
|
/// "pop" of the `B` value - it was spawned from a scope where only B was observable.
|
|
void copyTo(AsyncTask *target);
|
|
|
|
/// Destroy and deallocate all items stored by this specific task.
|
|
///
|
|
/// Items owned by a parent task are left untouched, since we do not own them.
|
|
void destroy(AsyncTask *task);
|
|
};
|
|
};
|
|
|
|
} // end namespace swift
|
|
|
|
#endif // SWIFT_ABI_TASKLOCAL_BACKDEPLOY56_H
|