Files
swift-mirror/stdlib/public/runtime/ExclusivityPrivate.h
Michael Gottesman 3e64d03a52 [exclusivity] Split out of Exclusivity.cpp FunctionReplacement and the Global TLSContext.
Some notes:

1. Even though I refactored out AccessSet/Access from Exclusivity.cpp ->
ExclusivityPrivate.h, I left the actual implementations of insert/remove in
Exclusivity.cpp to allow for the most aggressive optimization for use in
Exclusivity.cpp without exposing a bunch of internal details to other parts of
the runtime. Smaller routines like getHead() and manipulating the linked list
directly I left as methods that can be used by other parts of the runtime. I am
going to use these methods to enable backwards deployment of exclusivity support
for concurrency.

2. I moved function replacements out of the Exclusivity header/cpp files since
it has nothing to do with Exclusivity beyond taking advantage of the TLS context
that we are already using.
2021-07-26 12:26:08 -07:00

140 lines
4.5 KiB
C++

//===--- ExclusivityPrivate.h ---------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2021 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
//
//===----------------------------------------------------------------------===//
#ifndef SWIFT_RUNTIME_EXCLUSIVITYPRIVATE_H
#define SWIFT_RUNTIME_EXCLUSIVITYPRIVATE_H
#include "swift/Runtime/Exclusivity.h"
#include "swift/Runtime/Metadata.h"
#include <cinttypes>
namespace swift {
namespace runtime {
/// A single access that we're tracking.
///
/// The following inputs are accepted by the begin_access runtime entry
/// point. This table show the action performed by the current runtime to
/// convert those inputs into stored fields in the Access scratch buffer.
///
/// Pointer | Runtime | Access | PC | Reported| Access
/// Argument| Behavior | Pointer| Arg | PC | PC
/// -------- ------------- -------- ------- --------- ----------
/// null | [trap or missing enforcement]
/// nonnull | [nontracked]| null | null | caller | [discard]
/// nonnull | [nontracked]| null | valid | <same> | [discard]
/// nonnull | [tracked] | <same> | null | caller | caller
/// nonnull | [tracked] | <same> | valid | <same> | <same>
///
/// [nontracked] means that the Access scratch buffer will not be added to the
/// runtime's list of tracked accesses. However, it may be passed to a
/// subsequent call to end_unpaired_access. The null Pointer field then
/// identifies the Access record as nontracked.
///
/// The runtime owns the contents of the scratch buffer, which is allocated by
/// the compiler but otherwise opaque. The runtime may later reuse the Pointer
/// or PC fields or any spare bits for additional flags, and/or a pointer to
/// out-of-line storage.
struct Access {
void *Pointer;
void *PC;
uintptr_t NextAndAction;
enum : uintptr_t {
ActionMask = (uintptr_t)ExclusivityFlags::ActionMask,
NextMask = ~ActionMask
};
Access *getNext() const {
return reinterpret_cast<Access *>(NextAndAction & NextMask);
}
void setNext(Access *next) {
NextAndAction =
reinterpret_cast<uintptr_t>(next) | (NextAndAction & ActionMask);
}
ExclusivityFlags getAccessAction() const {
return ExclusivityFlags(NextAndAction & ActionMask);
}
void initialize(void *pc, void *pointer, Access *next,
ExclusivityFlags action) {
Pointer = pointer;
PC = pc;
NextAndAction = reinterpret_cast<uintptr_t>(next) | uintptr_t(action);
}
};
static_assert(sizeof(Access) <= sizeof(ValueBuffer) &&
alignof(Access) <= alignof(ValueBuffer),
"Access doesn't fit in a value buffer!");
/// A set of accesses that we're tracking. Just a singly-linked list.
///
/// NOTE: Please keep all implementations of methods of AccessSet within
/// Exclusivity.cpp. We want this to ensure that when compiled in the runtime
/// directly, the definitions of these methods are immediately available in that
/// file for inlining.
class AccessSet {
Access *Head = nullptr;
public:
constexpr AccessSet() {}
constexpr AccessSet(Access *Head) : Head(Head) {}
constexpr operator bool() const { return bool(Head); }
constexpr Access *getHead() const { return Head; }
void setHead(Access *newHead) { Head = newHead; }
constexpr bool isHead(Access *access) const { return Head == access; }
bool insert(Access *access, void *pc, void *pointer, ExclusivityFlags flags);
void remove(Access *access);
/// Return the parent access of \p childAccess in the list.
Access *findParentAccess(Access *childAccess) const {
auto cur = Head;
Access *last = cur;
for (cur = cur->getNext(); cur != nullptr;
last = cur, cur = cur->getNext()) {
assert(last->getNext() == cur);
if (cur == childAccess) {
return last;
}
}
return nullptr;
}
Access *getTail() const {
auto cur = Head;
if (!cur)
return nullptr;
while (auto *next = cur->getNext()) {
cur = next;
}
assert(cur != nullptr);
return cur;
}
#ifndef NDEBUG
/// Only available with asserts. Intended to be used with
/// swift_dumpTrackedAccess().
void forEach(std::function<void(Access *)> action);
#endif
};
} // namespace runtime
} // namespace swift
#endif