Add code back

This commit is contained in:
Vishesh Yadav 2024-07-10 16:04:28 -07:00
parent e31d92458f
commit 6cd5ad2ffe
89 changed files with 6205 additions and 62 deletions

View File

@ -27,7 +27,7 @@ project(foundationdb
VERSION 7.4.0
DESCRIPTION "FoundationDB is a scalable, fault-tolerant, ordered key-value store with full ACID transactions."
HOMEPAGE_URL "http://www.foundationdb.org/"
LANGUAGES C CXX ASM)
LANGUAGES C CXX ASM Swift)
list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake")
@ -196,7 +196,7 @@ add_subdirectory(fdbclient)
add_subdirectory(fdbserver)
add_subdirectory(fdbcli)
if(NOT WIN32)
if (NOT FOUNDATIONDB_CROSS_COMPILING)
if (NOT FOUNDATIONDB_CROSS_COMPILING) # FIXME(swift): make this work when x-compiling.
add_subdirectory(fdbmonitor)
endif()
else()
@ -205,11 +205,11 @@ endif()
add_subdirectory(fdbbackup)
add_subdirectory(metacluster)
add_subdirectory(tests)
if (NOT FOUNDATIONDB_CROSS_COMPILING)
if (NOT FOUNDATIONDB_CROSS_COMPILING) # FIXME(swift): make this work when x-compiling.
add_subdirectory(flowbench EXCLUDE_FROM_ALL)
endif()
if(WITH_PYTHON AND WITH_C_BINDING)
if (NOT FOUNDATIONDB_CROSS_COMPILING)
if (NOT FOUNDATIONDB_CROSS_COMPILING) # FIXME(swift): make this work when x-compiling.
add_subdirectory(bindings)
endif()
endif()
@ -240,6 +240,11 @@ if (CMAKE_EXPORT_COMPILE_COMMANDS AND WITH_PYTHON)
COMMENT "Build compile commands for IDE"
)
add_custom_target(processed_compile_commands ALL DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/compile_commands.json ${CMAKE_CURRENT_BINARY_DIR}/compile_commands.json)
# A prebuild target ensures that all actors, Swift-generated headers, and Swift modules are built.
if (WITH_SWIFT)
add_custom_target(prebuild_for_ide ALL DEPENDS fdbserver_swift processed_compile_commands)
endif()
endif()
################################################################################
@ -249,3 +254,5 @@ endif()
print_components()
message(STATUS "CPACK_COMPONENTS_ALL ${CPACK_COMPONENTS_ALL}")

308
SWIFT_GUIDE.md Normal file
View File

@ -0,0 +1,308 @@
# Swift in FoundationDB
The optional Swift support allows piecewise adoption of Swift in implementing FoundationDB.
Swift offers a unique modern type-safe low-ceremony approach taking the best of both worlds that scales from mobile
apps to high-performance systems where previously memory-unsafe languages would be used. It also interoperates
seamlessly with C and C++.
## Building with Swift
Since FoundationDB is largely implemented in C++ and Flow, large pieces of Swift is built using the same CMake build as the rest of the project.
To configure the build such that `clang` and `swiftc` are used, use the following:
```swift
cd build
cmake -G 'Ninja' \
-DCMAKE_C_COMPILER=clang \
-DCMAKE_CXX_COMPILER=clang++ \
-DCMAKE_Swift_COMPILER=swiftc \
-DWITH_SWIFT=ON \
-DUSE_LIBCXX=OFF \
-DCMAKE_Swift_COMPILER_EXTERNAL_TOOLCHAIN=/opt/rh/devtoolset-11/root/usr \
../src/foundationdb/
```
Then, build using `ninja` as usual.
## IDE Integration
A full guide on setting up IDEs with FoundationDB, including cross language autocomplete support and code navigation
is available here: [SWIFT_IDE_SETUP.md](SWIFT_IDE_SETUP.md)
## How Swift interoperates with C++
The CMake build has been prepared with Swift interop in the following modules:
- flow
- fdbrpc
- fdbclient
- fdbserver
The integration works "both ways", i.e. Swift can call into Flow/C++ code, as well as Flow/C++ can call into Swift.
Swift generates clang modules which can be consumed in C++. For example, the module `fdbserver_swift` contains all swift code in `fdbserver/`.
> Note: you can check, and add new files to the `_swift` targets by locating the command, e.g. `add_library(fdbserver_swft` in [fdbserver/CMakeLists.txt](fdbserver/CMakeLists.txt).
Then, you can then include the generated module in C++:
```cpp
// ...
#include "SwiftModules/FDBServer"
#include "flow/actorcompiler.h" // This must be the last #include.
```
## Swift Basics
When in doubt about Swift syntax, refer to https://docs.swift.org/swift-book/ or reach out to the team, we're here to help.
## Swift → C++
Swift can import clang modules, and does so using the `import` statement, like so:
```swift
import Flow
import flow_swift
import FDBServer
import FDBClient
import fdbclient_swift
import FlowFutureSupport
import flow_swift_future
// ...
```
The module has to have dependencies set up properly in `CMake` as well, check `CMakeLists.txt` for examples.
### Futures and other templates
Swift's C++ interop cannot currently instantiate class templates in Swift, but can use specialized templates if they are declared so in C++. For example, in order to use Flow futures and promises in Swift, we currently need to type alias them on the C++ side, and then use the type alias name in Swift, like so:
```swift
// flow/include/flow/swift_future_support.h
using PromiseCInt = Promise<int>;
using FutureCInt = Future<int>;
using PromiseVoid = Promise<Void>;
using FutureVoid = Future<Void>;
```
To use them in Swift make sure to use the type-aliases:
```swift
public func getVersion(cxxState: MasterData, req: GetCommitVersionRequest, result promise: PromiseVoid) {
// ...
}
```
### Sendable
Sendable is Swift's mechanism to ensure compile time thread-safety. It does so by marking types known to be safe to send across task/actor/thread boundaries with `Sendable` (i.e. `struct Something: Sendable {}` and checks related to it).
In order to declare a C++ type as Sendable you can use the @Sendable attribute:
```cpp
#define SWIFT_SENDABLE \
__attribute__((swift_attr("@Sendable")))
```
which is used like this:
```cpp
template <class T>
class SWIFT_SENDABLE Future {
// ...
};
```
### Importing types
Swift can import copyable C++ structs and classes as Swift value types. Non-copyable types with value semantics require special wrappers to make them available in Swift (see the "Non-copyable types" section).
Swift can also import some C++ types as reference types, if they're appropriately annotated. The sections "Bridging Singleton-like values with immortal reference types" and "Bridging reference types" describes how that can be done.
Swift will avoid importing "unsafe projections", which means a type which contains pointers to something "outside" the type, as they may end up pointing at unsafe memory.
#### Non-copyable types
Swift's C++ interop currently cannot import non-copyable C++ types. If you have a type that's non-copyable that you want to use as a value type in Swift (i.e. it's typically used as its own type in C++, not through a pointer type), you most likely want to wrap it in a copyable type which is then made available to Swift. For types that have reference semantics (i.e. you always pass it around using a raw pointer or a custom smart pointer type in C++), see the "Bridging reference types" section.
For example, Flow's `Counter` type is non-copyable. We can make it available to
Swift so that it can be used as a stored property in a Swift actor by creating a value
type wrapper for it in C++, that stores the counter in a shared pointer value:
```cpp
// A type with Swift value semantics for working with `Counter` types.
class CounterValue {
public:
using Value = Counter::Value;
CounterValue(std::string const& name, CounterCollection& collection);
void operator+=(Value delta);
void operator++();
void clear();
private:
std::shared_ptr<Counter> value;
};
```
We want to implement the required interface for this type that's needed from Swift.
#### Bridging Singleton-like values with immortal reference types
Certain C++ types act as singletons which have one value referenced throughout codebase. That value is expected to be alive throughout the lifetime of the program. Such types can be bridged to Swift using the `SWIFT_CXX_IMMORTAL_SINGLETON_TYPE` annotation. This annotation will instruct Swift to import such type as reference type that doesn't need any reference counting, i.e. it's assumed to be immortal.
For instance, the `INetwork` interface type:
```cpp
class SWIFT_CXX_IMMORTAL_SINGLETON_TYPE ServerKnobs : public KnobsImpl<ServerKnobs> {
public:
```
Gets bridged over to an immortal reference type in Swift:
```Swift
let knobs = getServerKnobs() // knobs type is `ServerKnobs` in Swift, identical to `ServerKnobs *` C++ type.
knobs.MAX_VERSION_RATE_MODIFIER
```
#### Bridging reference types
Some C++ types have reference counting and referential semantics, i.e. they're passed around using raw or smart pointers that point to an instance. That instance typically has its own reference count, that keeps track of when the instance should be released. Such types can be bridged over to Swift reference types, and Swift's automatic reference counting (ARC) will automatically retain and release them using their C++ reference counting implementation.
You can use the `SWIFT_CXX_REF` annotation for that. Right now `SWIFT_CXX_REF` does not work (due to https://github.com/apple/swift/issues/61620), so you have to make a custom annotation for each class you want to bridge with reference semantics to Swift. For example, the `MasterData` class receives the following annotation:
```cpp
#define SWIFT_CXX_REF_MASTERDATA \
__attribute__((swift_attr("import_reference"))) \
__attribute__((swift_attr("retain:addrefMasterData"))) \
__attribute__((swift_attr("release:delrefMasterData")))
struct SWIFT_CXX_REF_MASTERDATA MasterData : NonCopyable, ReferenceCounted<MasterData> {
```
This annotation then makes Swift's' `MasterData` type behave like C++/Flow's `Reference<MasterData>` type, i.e. it is automatically reference counted in Swift.
### Awaiting Flow concurrency types
Flow **Futures** can be awaited on in Swift, like this:
```swift
var f: FutureCInt = ...
await f.value()
```
to avoid name clashes with `value` it's currently called `waitValue` though we should probably rename this.
You can also await a next value of a stream:
```swift
var ps = PromiseStream<CInt>()
var fs: FutureStream<CInt> = ps.getFuture()
// ...
let element = try? await fs.waitNext // suspends until value is sent into `ps`
```
It is also possible to use the `async for-loop` syntax on `FutureStream`s:
```swift
for try await num in fs {
// ...
}
```
This future will loop until an "end" is sent to the stream.
Sending an "end" element is currently done the same way as in Flow itself:
```swift
var i: CInt = 10
ps.send(&i)
ps.sendError(end_of_stream())
```
## C++ → Swift
Calling Swift from C++ is relatively simple, you can write new Swift code and `@_expose(Cxx)` them, like this free function in Swift:
```swift
@_expose(Cxx)
public func swiftyTestRunner(p: PromiseVoid) { }
```
This can be called from C++ as expected:
```swift
fdbserver_swift::swiftyTestRunner(p);
```
### Exposing actors and async functions
Actors in Swift have strong isolation properties and cannot be accessed synchronously, as such, every method declared on an actor is implicitly `async` if called from the outside. For example:
```swift
@_expose(Cxx)
actor Example {
func hi() -> String { "hi!" }
}
```
this `hi()` method is not imported into C++, so you'd get an error when trying to call it on an instance of `Example` from C++:
```
<<error not imported>>
```
This is because, calls "into" an actor are implicitly async, so the method is effectively async:
```swift
@_expose(Cxx)
actor Example {
func hi() async -> CInt { 42 }
}
```
Since C++ does not understand Swift's async calling convention, we don't import such methods today. This is why today we implement a `nonisolated` wrapper method in order to bridge Flow/C++ and the Swift actor method, like this:
```swift
@_expose(Cxx)
actor Example {
func hi() async -> CInt { 42 }
nonisolated func hi(/*other params*/ result: PromiseCInt) {
Task { // schedule a new Task
var i = await self.hi() // inside that task, await the hi() method
result.send(&i)
}
}
}
```
And since the `Example` was `_expose`-d the `nonisolated func hi(result:)` is as well, and that can be called from C++:
```swift
// C++
auto promise = Promise<int>();
actor.hi(promise);
wait(p.getFuture());
```
> Note: We hope to simplify this, so you could just `wait(actor.hi())`. And the nonisolated wrapper can also be implemented using a Swift macro once they land (Swift macros are work in progress, and operate on AST, so we're not too worried about using them -- i.e. they are not just textual macros).
## Swift tests
We have prepared a ver simple test suite runner since we cannot use the usual Swift's XCTest framework (maybe we could, didn't investigate yet). To run `swift` tests inside `fdbserver` run like this:
```bash
FDBSWIFTTEST=1 bin/fdbserver -p AUTO
```
you can also `--filter-test future` etc.

153
SWIFT_IDE_SETUP.md Normal file
View File

@ -0,0 +1,153 @@
# FoundationDB/Swift IDE Setup Guide
Swift provides a language server implementation (https://github.com/apple/sourcekit-lsp) that should work with any LSP compatible editor.
In this guide we'll cover how to set up VSCode for local development on a Mac, however other setups should
work in the same way.
## Swift Versions
Note that Swift 5.9 (or higher) is required for the build at the time of writing this guide.
You can download Swift toolchains from [https://www.swift.org/download/](https://www.swift.org/download/),
or by using the experimental https://github.com/swift-server/swiftly which simplifies this process.
## VSCode + Cross Compilation (MacOS to Linux)
## Host toolchain setup
Download and install latest Swift toolchain: https://www.swift.org/download/
Use the following version:
* **Snapshots / Trunk Development (5.9)**
Note the path of the installed toolchain suffixed with `/usr` as **`FOUNDATIONDB_SWIFT_TOOLCHAIN_ROOT`**, e.g.:
```bash
export FOUNDATIONDB_SWIFT_TOOLCHAIN_ROOT=/Library/Developer/Toolchains/swift-5.9-DEVELOPMENT-SNAPSHOT-2023-05-01-a.xctoolchain/usr
```
For linker and object file tools:
* Download LLVM toolchain:
* arm64: https://github.com/llvm/llvm-project/releases/download/llvmorg-15.0.7/clang+llvm-15.0.7-arm64-apple-darwin22.0.tar.xz
* from: https://github.com/llvm/llvm-project/releases/tag/llvmorg-15.0.7
Note the path of the installed LLVM toolchain + /usr as **`FOUNDATIONDB_LLVM_TOOLCHAIN_ROOT`**, e.g.
```bash
cd ~/Downloads
wget https://github.com/llvm/llvm-project/releases/download/llvmorg-15.0.7/clang+llvm-15.0.7-arm64-apple-darwin22.0.tar.xz
tar xzf clang+llvm-15.0.7-arm64-apple-darwin22.0.tar.xz
export FOUNDATIONDB_LLVM_TOOLCHAIN_ROOT=~/Downloads/clang+llvm-15.0.7-arm64-apple-darwin22.0
```
For actor compiler: Download and install mono: [https://www.mono-project.com](https://www.mono-project.com/), e.g.
```bash
brew install mono
```
### Host container setup
The Foundation DB will be used on the host as the system SDK/root against which to build.
This is done by using docker and extracting the image
You may need to make sure that you have the latest foundationdb docker image pulled:
```bash
docker pull foundationdb/build:centos7-latest
```
```bash
cd
mkdir fdb-build && cd fdb-build
docker run -ti foundationdb/build:centos7-latest
# and in another terminal session (without terminating centos container):
# $ docker ps
# CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
# 5fe40defb063 07d50e07570b "/bin/bash" 9 seconds ago Up 8 seconds vigilant_gauss
# $ docker export 07d50e07570b -o container-root.tar
mkdir container-root && cd container-root
tar -xzf ../container-root.tar
```
This
Note that **`FOUNDATIONDB_LINUX_CONTAINER_ROOT`** becomes `~/fdb-build/container-root`.
```bash
export FOUNDATIONDB_LINUX_CONTAINER_ROOT=~/fdb-build/container-root
```
### CMake
Now that you have setup your host, and obtained `FOUNDATIONDB_SWIFT_TOOLCHAIN_ROOT` , `FOUNDATIONDB_LLVM_TOOLCHAIN_ROOT`, `FOUNDATIONDB_LINUX_CONTAINER_ROOT` you can run cmake.
You need to ensure you pass in these three flags, and `-C<foundation-db-source>/cmake/toolchain/macos-to-linux.cmake` after the flags.
For example:
```bash
cd ~/fdb-build
mkdir build && cd build
xcrun cmake -G Ninja -DCMAKE_MAKE_PROGRAM=$(xcrun --find ninja) \
-DFOUNDATIONDB_SWIFT_TOOLCHAIN_ROOT=$FOUNDATIONDB_SWIFT_TOOLCHAIN_ROOT \
-DFOUNDATIONDB_LLVM_TOOLCHAIN_ROOT=$FOUNDATIONDB_LLVM_TOOLCHAIN_ROOT \
-DFOUNDATIONDB_LINUX_CONTAINER_ROOT=$FOUNDATIONDB_LINUX_CONTAINER_ROOT \
-C$HOME/src/foundationdb/cmake/toolchain/macos-to-linux.cmake \
$HOME/src/foundationdb
# Which would be this with all the parameters substituted:
#
# xcrun cmake -G Ninja -DCMAKE_MAKE_PROGRAM=/opt/homebrew/bin/ninja \
# -DFOUNDATIONDB_SWIFT_TOOLCHAIN_ROOT=/Library/Developer/Toolchains/swift-5.9-DEVELOPMENT-SNAPSHOT-2023-05-01-a.xctoolchain/usr \
# -DFOUNDATIONDB_LLVM_TOOLCHAIN_ROOT=$HOME/Downloads/clang1507 \
# -DFOUNDATIONDB_LINUX_CONTAINER_ROOT=$HOME/fdb-build/container-root \
# -C$HOME/src/foundationdb/cmake/toolchain/macos-to-linux.cmake \
# $HOME/src/foundationdb
```
If you get a warning about not being able to execute `lld` or other binaries from the toolchain because they're not trusted, open `Privacy & Security` and find a button "**Allow Anyway**" and click it once.
[Image: Screenshot 2023-04-17 at 12.47.01.png]
## Prebuild project for IDE
After configuration, make sure things get pre build to be usable in IDE:
```bash
xcrun cmake --build . -- prebuild_for_ide
```
Now you should see that your source directory has a `compile_commands.json` file.
## VSCode setup
Now you can open your source directory in VSCode.
Setup:
* Install the official **Swift plugin**
* it's the one *maintained by* **Swift Server Work Group**
* For extension settings
* Update `Swift: Path` to point to the Swift toolchain installed in the first step (e.g. `/Library/Developer/Toolchains/swift-DEVELOPMENT-SNAPSHOT-2023-05-01-a.xctoolchain/usr/bin`)
* Do not omit the /usr/bin in the path (!)
[Image: Screenshot 2023-03-16 at 7.25.21 PM.png]
* Disable the default "C++" plugin
* To make sure sourcekit-lsp is used for C++ files.
## Known issues
* jump-to-definition fails to open actor header files.
* jump-to-definition from C++ to Swift does not work.
* Code completion for semantic responses in Swift can be slow sometimes especially in files that import both FDBServer and FDBClient

View File

@ -54,3 +54,9 @@ function(static_link_libcxx out)
set("${out}" ON PARENT_SCOPE)
endif()
endfunction()
function(check_swift_source_compiles SOURCE VAR)
file(WRITE "${CMAKE_BINARY_DIR}/CMakeTmp/src.swift" "${SOURCE}")
try_compile(build_result "${CMAKE_BINARY_DIR}/CMakeTmp" "${CMAKE_BINARY_DIR}/CMakeTmp/src.swift")
set(${VAR} ${build_result} PARENT_SCOPE)
endfunction()

View File

@ -28,7 +28,9 @@ env_set(PROFILE_INSTR_USE "" STRING "If set, build FDB with profile")
env_set(FULL_DEBUG_SYMBOLS OFF BOOL "Generate full debug symbols")
env_set(ENABLE_LONG_RUNNING_TESTS OFF BOOL "Add a long running tests package")
set(is_swift_compile "$<COMPILE_LANGUAGE:Swift>")
set(is_cxx_compile "$<OR:$<COMPILE_LANGUAGE:CXX>,$<COMPILE_LANGUAGE:C>>")
set(is_swift_link "$<LINK_LANGUAGE:Swift>")
set(is_cxx_link "$<OR:$<LINK_LANGUAGE:CXX>,$<LINK_LANGUAGE:C>>")
set(USE_SANITIZER OFF)
@ -177,7 +179,7 @@ else()
if(CLANG)
# The default DWARF 5 format does not play nicely with GNU Binutils 2.39 and earlier, resulting
# in tools like addr2line omitting line numbers. We can consider removing this once we are able
# in tools like addr2line omitting line numbers. We can consider removing this once we are able
# to use a version that has a fix.
add_compile_options("$<${is_cxx_compile}:-gdwarf-4>")
endif()
@ -194,13 +196,13 @@ else()
if(CLANG)
# The default DWARF 5 format does not play nicely with GNU Binutils 2.39 and earlier, resulting
# in tools like addr2line omitting line numbers. We can consider removing this once we are able
# in tools like addr2line omitting line numbers. We can consider removing this once we are able
# to use a version that has a fix.
add_compile_options("$<${is_cxx_compile}:-gdwarf-4>")
endif()
if(NOT FDB_RELEASE)
# Enable compression of the debug sections. This reduces the size of the binaries several times.
# Enable compression of the debug sections. This reduces the size of the binaries several times.
# We do not enable it release builds, because CPack fails to generate debuginfo packages when
# compression is enabled
add_compile_options("$<${is_cxx_compile}:-gz>")
@ -380,6 +382,7 @@ else()
$<${is_cxx_link}:-lc++abi>
$<${is_cxx_link}:-Wl,-Bdynamic>
)
add_link_options($<${is_swift_link}:-static-stdlib>)
endif()
add_link_options(
$<${is_cxx_link}:-stdlib=libc++>
@ -554,3 +557,63 @@ else()
endif()
endif()
endif()
#####################################
## Setup the Swift compiler options.
#####################################
if (WITH_SWIFT)
set(SwiftOptions "")
# Let Swift know where to find the external GCC toolchain if such toolchain is used.
if (CMAKE_Swift_COMPILER_EXTERNAL_TOOLCHAIN)
# FIXME: adopt driver flag once it lands:
# https://github.com/apple/swift-driver/pull/1307.
set(SwiftOptions "${SwiftOptions} -Xcc --gcc-toolchain=${CMAKE_Swift_COMPILER_EXTERNAL_TOOLCHAIN}")
set(SwiftOptions "${SwiftOptions} -Xclang-linker --gcc-toolchain=${CMAKE_Swift_COMPILER_EXTERNAL_TOOLCHAIN}")
endif()
# Set the module cache path.
set(SWIFT_MODULE_CACHE_PATH ${CMAKE_BINARY_DIR}/module-cache)
set(SwiftOptions "${SwiftOptions} -module-cache-path ${SWIFT_MODULE_CACHE_PATH}")
# Enable Swift <-> C++ interoperability.
set(SwiftOptions "${SwiftOptions} -cxx-interoperability-mode=swift-5.9")
set(SwiftOptions "${SwiftOptions} -Xcc -DWITH_SWIFT")
# Suppress noisy C++ warnings from Swift.
set(SwiftOptions "${SwiftOptions} -Xcc -Wno-deprecated -Xcc -Wno-undefined-var-template")
# Suppress rapidjson noisy GCC pragma diagnostics.
set(SwiftOptions "${SwiftOptions} -Xcc -Wno-unknown-warning-option")
if (FOUNDATIONDB_CROSS_COMPILING)
# Cross-compilation options.
# For some reason we need to specify -sdk explicitly to pass config-time
# cmake checks, even though Swift does tend to pass it by itself for the
# actual compilation.
string(TOLOWER ${CMAKE_SYSTEM_PROCESSOR} TripleArch)
set(SwiftOptions "${SwiftOptions} -target ${TripleArch}-unknown-linux-gnu -sdk ${CMAKE_SYSROOT} -resource-dir ${CMAKE_SYSROOT}/usr/lib/swift")
if (CMAKE_LINKER)
set(SwiftOptions "${SwiftOptions} -use-ld=${CMAKE_LINKER}")
endif()
# C++ files need path to swift resources to find C++ interoperability
# Swift builtin headers.
add_compile_options($<${is_cxx_compile}:-I${CMAKE_SYSROOT}/usr/lib/swift>)
endif()
set(CMAKE_Swift_FLAGS "${SwiftOptions}" CACHE STRING "" FORCE)
if (FOUNDATIONDB_CROSS_COMPILING)
# For cross-compilation: make sure the Stdlib .swiftmodule files are available.
include(SwiftCrossCompileForceModuleRebuild)
swift_force_import_rebuild_of_stdlib()
endif()
# Verify that Swift can import C++ standard library.
include(CompilerChecks)
check_swift_source_compiles("import CxxStdlib" CanImportCxxStdlibIntoSwift)
if (NOT CanImportCxxStdlibIntoSwift)
message(FATAL_ERROR "Swift compiler: can not import C++ standard library into Swift; did you forget to set 'CMAKE_Swift_COMPILER_EXTERNAL_TOOLCHAIN'?")
endif()
endif()

View File

@ -31,6 +31,19 @@ endif()
find_package(OpenSSL REQUIRED)
add_compile_options(-DHAVE_OPENSSL)
################################################################################
# Swift Support
################################################################################
if (WITH_SWIFT)
message(DEBUG "Building with Swift")
add_definitions(-DWITH_SWIFT)
set(WITH_SWIFT ON)
else()
message(DEBUG "Not building with Swift")
set(WITH_SWIFT OFF)
endif()
################################################################################
# Python Bindings
################################################################################
@ -170,7 +183,7 @@ if(toml11_FOUND)
add_library(toml11_target INTERFACE)
target_link_libraries(toml11_target INTERFACE toml11::toml11)
else()
include(ExternalProject)
include(ExternalProject)
ExternalProject_add(toml11Project
URL "https://github.com/ToruNiina/toml11/archive/v3.4.0.tar.gz"
URL_HASH SHA256=bc6d733efd9216af8c119d8ac64a805578c79cc82b813e4d1d880ca128bd154d
@ -227,6 +240,7 @@ function(print_components)
message(STATUS "Build Java Bindings: ${WITH_JAVA_BINDING}")
message(STATUS "Build Go bindings: ${WITH_GO_BINDING}")
message(STATUS "Build Ruby bindings: ${WITH_RUBY_BINDING}")
message(STATUS "Build Swift (depends on Swift): ${WITH_SWIFT}")
message(STATUS "Build Documentation (make html): ${WITH_DOCUMENTATION}")
message(STATUS "Build Python sdist (make package): ${WITH_PYTHON_BINDING}")
message(STATUS "Configure CTest (depends on Python): ${WITH_PYTHON}")

54
cmake/FindSwiftLibs.cmake Normal file
View File

@ -0,0 +1,54 @@
function(swift_get_linker_search_paths var)
if(APPLE)
set(SDK_FLAGS "-sdk" "${CMAKE_OSX_SYSROOT}")
endif()
execute_process(
COMMAND ${CMAKE_Swift_COMPILER} ${SDK_FLAGS} -print-target-info
OUTPUT_VARIABLE SWIFT_TARGET_INFO
)
string(JSON SWIFT_TARGET_PATHS GET ${SWIFT_TARGET_INFO} "paths")
string(JSON SWIFT_TARGET_LIBRARY_PATHS GET ${SWIFT_TARGET_PATHS} "runtimeLibraryPaths")
string(JSON SWIFT_TARGET_LIBRARY_PATHS_LENGTH LENGTH ${SWIFT_TARGET_LIBRARY_PATHS})
math(EXPR SWIFT_TARGET_LIBRARY_PATHS_LENGTH "${SWIFT_TARGET_LIBRARY_PATHS_LENGTH} - 1 ")
string(JSON SWIFT_TARGET_LIBRARY_IMPORT_PATHS GET ${SWIFT_TARGET_PATHS} "runtimeLibraryImportPaths")
string(JSON SWIFT_TARGET_LIBRARY_IMPORT_PATHS_LENGTH LENGTH ${SWIFT_TARGET_LIBRARY_IMPORT_PATHS})
math(EXPR SWIFT_TARGET_LIBRARY_IMPORT_PATHS_LENGTH "${SWIFT_TARGET_LIBRARY_IMPORT_PATHS_LENGTH} - 1 ")
string(JSON SWIFT_SDK_IMPORT_PATH ERROR_VARIABLE errno GET ${SWIFT_TARGET_PATHS} "sdkPath")
foreach(JSON_ARG_IDX RANGE ${SWIFT_TARGET_LIBRARY_PATHS_LENGTH})
string(JSON SWIFT_LIB GET ${SWIFT_TARGET_LIBRARY_PATHS} ${JSON_ARG_IDX})
list(APPEND SWIFT_LIBRARY_SEARCH_PATHS ${SWIFT_LIB})
endforeach()
foreach(JSON_ARG_IDX RANGE ${SWIFT_TARGET_LIBRARY_IMPORT_PATHS_LENGTH})
string(JSON SWIFT_LIB GET ${SWIFT_TARGET_LIBRARY_IMPORT_PATHS} ${JSON_ARG_IDX})
list(APPEND SWIFT_LIBRARY_SEARCH_PATHS ${SWIFT_LIB})
endforeach()
if(SWIFT_SDK_IMPORT_PATH)
list(APPEND SWIFT_LIBRARY_SEARCH_PATHS ${SWIFT_SDK_IMPORT_PATH})
endif()
set(${var} ${SWIFT_LIBRARY_SEARCH_PATHS} PARENT_SCOPE)
endfunction()
# FIXME: share code with the function above
function(swift_get_resource_path var)
if(APPLE)
set(SDK_FLAGS "-sdk" "${CMAKE_OSX_SYSROOT}")
endif()
execute_process(
COMMAND ${CMAKE_Swift_COMPILER} ${SDK_FLAGS} -print-target-info
OUTPUT_VARIABLE SWIFT_TARGET_INFO
)
string(JSON SWIFT_TARGET_PATHS GET ${SWIFT_TARGET_INFO} "paths")
string(JSON SWIFT_RUNTIME_RESOURCE_PATH GET ${SWIFT_TARGET_PATHS} "runtimeResourcePath")
set(${var} ${SWIFT_RUNTIME_RESOURCE_PATH} PARENT_SCOPE)
endfunction()

256
cmake/FindThreads.cmake Normal file
View File

@ -0,0 +1,256 @@
# Distributed under the OSI-approved BSD 3-Clause License. See accompanying
# file Copyright.txt or https://cmake.org/licensing for details.
#[=======================================================================[.rst:
FindThreads
-----------
This module determines the thread library of the system.
Imported Targets
^^^^^^^^^^^^^^^^
.. versionadded:: 3.1
This module defines the following :prop_tgt:`IMPORTED` target:
``Threads::Threads``
The thread library, if found.
Result Variables
^^^^^^^^^^^^^^^^
The following variables are set:
``Threads_FOUND``
If a supported thread library was found.
``CMAKE_THREAD_LIBS_INIT``
The thread library to use. This may be empty if the thread functions
are provided by the system libraries and no special flags are needed
to use them.
``CMAKE_USE_WIN32_THREADS_INIT``
If the found thread library is the win32 one.
``CMAKE_USE_PTHREADS_INIT``
If the found thread library is pthread compatible.
``CMAKE_HP_PTHREADS_INIT``
If the found thread library is the HP thread library.
Variables Affecting Behavior
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. variable:: THREADS_PREFER_PTHREAD_FLAG
.. versionadded:: 3.1
If the use of the -pthread compiler and linker flag is preferred then
the caller can set this variable to TRUE. The compiler flag can only be
used with the imported target. Use of both the imported target as well
as this switch is highly recommended for new code.
This variable has no effect if the system libraries provide the
thread functions, i.e. when ``CMAKE_THREAD_LIBS_INIT`` will be empty.
#]=======================================================================]
include (CheckLibraryExists)
set(Threads_FOUND FALSE)
set(CMAKE_REQUIRED_QUIET_SAVE ${CMAKE_REQUIRED_QUIET})
set(CMAKE_REQUIRED_QUIET ${Threads_FIND_QUIETLY})
if(CMAKE_C_COMPILER_LOADED)
include (CheckIncludeFile)
include (CheckCSourceCompiles)
elseif(CMAKE_CXX_COMPILER_LOADED)
include (CheckIncludeFileCXX)
include (CheckCXXSourceCompiles)
else()
message(FATAL_ERROR "FindThreads only works if either C or CXX language is enabled")
endif()
# simple pthread test code
set(PTHREAD_C_CXX_TEST_SOURCE [====[
#include <pthread.h>
static void* test_func(void* data)
{
return data;
}
int main(void)
{
pthread_t thread;
pthread_create(&thread, NULL, test_func, NULL);
pthread_detach(thread);
pthread_cancel(thread);
pthread_join(thread, NULL);
pthread_atfork(NULL, NULL, NULL);
pthread_exit(NULL);
return 0;
}
]====])
# Internal helper macro.
# Do NOT even think about using it outside of this file!
macro(_threads_check_libc)
if(NOT Threads_FOUND)
if(CMAKE_C_COMPILER_LOADED)
CHECK_C_SOURCE_COMPILES("${PTHREAD_C_CXX_TEST_SOURCE}" CMAKE_HAVE_LIBC_PTHREAD)
elseif(CMAKE_CXX_COMPILER_LOADED)
CHECK_CXX_SOURCE_COMPILES("${PTHREAD_C_CXX_TEST_SOURCE}" CMAKE_HAVE_LIBC_PTHREAD)
endif()
if(CMAKE_HAVE_LIBC_PTHREAD)
set(CMAKE_THREAD_LIBS_INIT "")
set(Threads_FOUND TRUE)
endif()
endif ()
endmacro()
# Internal helper macro.
# Do NOT even think about using it outside of this file!
macro(_threads_check_lib LIBNAME FUNCNAME VARNAME)
if(NOT Threads_FOUND)
CHECK_LIBRARY_EXISTS(${LIBNAME} ${FUNCNAME} "" ${VARNAME})
if(${VARNAME})
set(CMAKE_THREAD_LIBS_INIT "-l${LIBNAME}")
set(Threads_FOUND TRUE)
endif()
endif ()
endmacro()
# Internal helper macro.
# Do NOT even think about using it outside of this file!
macro(_threads_check_flag_pthread)
if(NOT Threads_FOUND)
# If we did not find -lpthreads, -lpthread, or -lthread, look for -pthread
# except on compilers known to not have it.
if(MSVC)
# Compilers targeting the MSVC ABI do not have a -pthread flag.
set(THREADS_HAVE_PTHREAD_ARG FALSE)
elseif(NOT DEFINED THREADS_HAVE_PTHREAD_ARG)
message(CHECK_START "Check if compiler accepts -pthread")
if(CMAKE_C_COMPILER_LOADED)
set(_threads_src ${CMAKE_CURRENT_LIST_DIR}/CheckForPthreads.c)
elseif(CMAKE_CXX_COMPILER_LOADED)
set(_threads_src ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/FindThreads/CheckForPthreads.cxx)
configure_file(${CMAKE_CURRENT_LIST_DIR}/CheckForPthreads.c "${_threads_src}" COPYONLY)
endif()
try_compile(THREADS_HAVE_PTHREAD_ARG
${CMAKE_BINARY_DIR}
${_threads_src}
CMAKE_FLAGS -DLINK_LIBRARIES:STRING=-pthread
OUTPUT_VARIABLE _cmake_check_pthreads_output)
string(APPEND _cmake_find_threads_output "${_cmake_check_pthreads_output}")
unset(_cmake_check_pthreads_output)
unset(_threads_src)
if(THREADS_HAVE_PTHREAD_ARG)
set(Threads_FOUND TRUE)
message(CHECK_PASS "yes")
else()
message(CHECK_FAIL "no")
endif()
endif()
if(THREADS_HAVE_PTHREAD_ARG)
set(Threads_FOUND TRUE)
#XXX: Swift Change Here!
set(CMAKE_THREAD_LIBS_INIT "$<$<NOT:$<LINK_LANGUAGE:Swift>>:-pthread>$<$<LINK_LANGUAGE:Swift>:-lpthread>")
#XXX: Swift Change Here!
endif()
endif()
endmacro()
# Check if pthread functions are in normal C library.
# We list some pthread functions in PTHREAD_C_CXX_TEST_SOURCE test code.
# If the pthread functions already exist in C library, we could just use
# them instead of linking to the additional pthread library.
_threads_check_libc()
# Check for -pthread first if enabled. This is the recommended
# way, but not backwards compatible as one must also pass -pthread
# as compiler flag then.
if (THREADS_PREFER_PTHREAD_FLAG)
_threads_check_flag_pthread()
endif ()
if(CMAKE_SYSTEM MATCHES "GHS-MULTI")
_threads_check_lib(posix pthread_create CMAKE_HAVE_PTHREADS_CREATE)
endif()
_threads_check_lib(pthreads pthread_create CMAKE_HAVE_PTHREADS_CREATE)
_threads_check_lib(pthread pthread_create CMAKE_HAVE_PTHREAD_CREATE)
if (NOT THREADS_PREFER_PTHREAD_FLAG)
_threads_check_flag_pthread()
endif()
if(CMAKE_THREAD_LIBS_INIT OR CMAKE_HAVE_LIBC_PTHREAD)
set(CMAKE_USE_PTHREADS_INIT 1)
set(Threads_FOUND TRUE)
endif()
if(CMAKE_SYSTEM_NAME MATCHES "Windows")
set(CMAKE_USE_WIN32_THREADS_INIT 1)
set(Threads_FOUND TRUE)
endif()
if(CMAKE_USE_PTHREADS_INIT)
if(CMAKE_SYSTEM_NAME MATCHES "HP-UX")
# Use libcma if it exists and can be used. It provides more
# symbols than the plain pthread library. CMA threads
# have actually been deprecated:
# http://docs.hp.com/en/B3920-90091/ch12s03.html#d0e11395
# http://docs.hp.com/en/947/d8.html
# but we need to maintain compatibility here.
# The CMAKE_HP_PTHREADS setting actually indicates whether CMA threads
# are available.
CHECK_LIBRARY_EXISTS(cma pthread_attr_create "" CMAKE_HAVE_HP_CMA)
if(CMAKE_HAVE_HP_CMA)
set(CMAKE_THREAD_LIBS_INIT "-lcma")
set(CMAKE_HP_PTHREADS_INIT 1)
set(Threads_FOUND TRUE)
endif()
set(CMAKE_USE_PTHREADS_INIT 1)
endif()
if(CMAKE_SYSTEM MATCHES "OSF1-V")
set(CMAKE_USE_PTHREADS_INIT 0)
set(CMAKE_THREAD_LIBS_INIT )
endif()
if(CMAKE_SYSTEM MATCHES "CYGWIN_NT" OR CMAKE_SYSTEM MATCHES "MSYS_NT")
set(CMAKE_USE_PTHREADS_INIT 1)
set(Threads_FOUND TRUE)
set(CMAKE_THREAD_LIBS_INIT )
set(CMAKE_USE_WIN32_THREADS_INIT 0)
endif()
endif()
set(CMAKE_REQUIRED_QUIET ${CMAKE_REQUIRED_QUIET_SAVE})
include(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(Threads DEFAULT_MSG Threads_FOUND)
if(THREADS_FOUND AND NOT TARGET Threads::Threads)
add_library(Threads::Threads INTERFACE IMPORTED)
if(THREADS_HAVE_PTHREAD_ARG)
#XXX: Swift Change Here!
set_property(TARGET Threads::Threads
PROPERTY INTERFACE_COMPILE_OPTIONS
"$<$<COMPILE_LANG_AND_ID:CUDA,NVIDIA>:SHELL:-Xcompiler -pthread>"
"$<$<AND:$<NOT:$<COMPILE_LANG_AND_ID:CUDA,NVIDIA>>,$<NOT:$<COMPILE_LANGUAGE:Swift>>>:-pthread>"
"$<$<COMPILE_LANGUAGE:Swift>:-lpthread>")
#XXX: Swift Change Here!
endif()
if(CMAKE_THREAD_LIBS_INIT)
set_property(TARGET Threads::Threads PROPERTY INTERFACE_LINK_LIBRARIES "${CMAKE_THREAD_LIBS_INIT}")
endif()
elseif(NOT THREADS_FOUND AND _cmake_find_threads_output)
file(APPEND
${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log
"Determining if compiler accepts -pthread failed with the following output:\n${_cmake_find_threads_output}\n\n")
endif()
unset(_cmake_find_threads_output)

View File

@ -0,0 +1,50 @@
# Generate flow modulemap
function(generate_modulemap out module target)
cmake_parse_arguments(ARG "" "" "OMIT;HEADERS" ${ARGN})
set(MODULE_NAME "${module}")
string(TOLOWER ${module} module_lower)
string(TOLOWER ${target} target_lower)
get_target_property(MODULE_HEADERS ${target} HEADER_FILES)
if(ARG_HEADERS)
set(MODULE_HEADERS ${ARG_HEADERS})
endif()
foreach(header ${MODULE_HEADERS})
get_filename_component(fname ${header} NAME)
if(NOT ${fname} IN_LIST ARG_OMIT)
get_filename_component(headerdir ${header} DIRECTORY)
get_filename_component(dirname ${headerdir} NAME)
set(topdirname ${dirname})
# FIXME: we need to account for headers in subdirectories.
# THIS is a hack for single level directory.
if (NOT ${dirname} MATCHES ${module_lower} AND
NOT ${dirname} MATCHES ${target_lower})
get_filename_component(headerdir2 ${headerdir} DIRECTORY)
get_filename_component(dirname2 ${headerdir2} NAME)
set(topdirname ${dirname2})
set(dirname "${dirname2}/${dirname}")
if (NOT ${dirname2} MATCHES ${module_lower} AND
NOT ${dirname2} MATCHES ${target_lower})
get_filename_component(headerdir3 ${headerdir2} DIRECTORY)
get_filename_component(dirname3 ${headerdir3} NAME)
set(topdirname ${dirname3})
set(dirname "${dirname3}/${dirname}")
endif()
endif()
if(ARG_HEADERS)
set(header_list "${header_list} header \"include/${dirname}/${fname}\"\n")
else()
set(header_list "${header_list} header \"${dirname}/${fname}\"\n")
endif()
cmake_path(IS_PREFIX CMAKE_BINARY_DIR ${headerdir} NORMALIZE isGenerated)
if (NOT ${isGenerated})
set(vfs_roots "${vfs_roots} {\"type\": \"file\", \"name\": \"${CMAKE_BINARY_DIR}/${topdirname}/include/${dirname}/${fname}\", \"external-contents\": \"${header}\"},\n")
endif()
endif()
endforeach()
set(MODULE_HEADERS "${header_list}")
configure_file("${CMAKE_SOURCE_DIR}/swift_build_support/empty.modulemap" "${out}/module.modulemap" @ONLY)
set(VFS_ROOTS "${vfs_roots}")
configure_file("${CMAKE_SOURCE_DIR}/swift_build_support/headeroverlay.yaml" "${out}/headeroverlay.yaml" @ONLY)
endfunction()

View File

@ -0,0 +1,36 @@
# This function forces the Swift compiler to rebuild 'Swift' and 'CxxStdlib'
# from .swiftinterface files. This is useful when cross-compiling, when the
# host compiler version is different than the Swift version on the linux
# target container.
function(swift_force_import_rebuild_of_stdlib)
message(STATUS "Making sure Swift builtin modules are up-to-date...")
string(REPLACE " " ";" Swift_FLAGS_LIST ${CMAKE_Swift_FLAGS})
set(Rebuilt_Swift_FLAGS_LIST)
foreach (flag ${Swift_FLAGS_LIST})
if ("${flag}" STREQUAL "-resource-dir")
list(APPEND Rebuilt_Swift_FLAGS_LIST "-Xfrontend" ${flag} "-Xfrontend")
else()
list(APPEND Rebuilt_Swift_FLAGS_LIST ${flag})
endif()
endforeach()
list(APPEND Rebuilt_Swift_FLAGS_LIST "-Xfrontend" "-strict-implicit-module-context")
file(WRITE "${CMAKE_BINARY_DIR}/CMakeTmp/rebuildStdlib.swift" "import Swift")
execute_process(COMMAND "${CMAKE_Swift_COMPILER}" -c -o "${CMAKE_BINARY_DIR}/CMakeTmp/rebuildStdlib.o" ${Rebuilt_Swift_FLAGS_LIST} "${CMAKE_BINARY_DIR}/CMakeTmp/rebuildStdlib.swift"
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
RESULT_VARIABLE
CanSwiftImportSwift
)
if (NOT ${CanSwiftImportSwift} EQUAL 0)
message(FATAL_ERROR "Swift couldn't import/rebuild standard library.")
endif()
file(WRITE "${CMAKE_BINARY_DIR}/CMakeTmp/rebuildCxxStdlib.swift" "import CxxStdlib")
execute_process(COMMAND "${CMAKE_Swift_COMPILER}" -c -o "${CMAKE_BINARY_DIR}/CMakeTmp/rebuildCxxStdlib.o" ${Rebuilt_Swift_FLAGS_LIST} "${CMAKE_BINARY_DIR}/CMakeTmp/rebuildCxxStdlib.swift"
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
RESULT_VARIABLE
CanSwiftImportSwiftCxxStdlib
)
if (NOT ${CanSwiftImportSwiftCxxStdlib} EQUAL 0)
message(FATAL_ERROR "Swift couldn't import/rebuild C++ standard library.")
endif()
endfunction()

View File

@ -0,0 +1,84 @@
include(CompilerChecks)
include(FindSwiftLibs)
function(add_swift_to_cxx_header_gen_target target_name header_target_name header_path)
cmake_parse_arguments(ARG "" "" "SOURCES;FLAGS" ${ARGN})
# Verify toolchain support.
get_filename_component(SwiftBinPath ${CMAKE_Swift_COMPILER} DIRECTORY)
set (SwiftInteropVersionFile ${SwiftBinPath}/../lib/swift/swiftToCxx/experimental-interoperability-version.json)
if (EXISTS ${SwiftInteropVersionFile})
file(READ ${SwiftInteropVersionFile} SwiftInteropVersion)
message(STATUS "Swift: Experimental C++ interop version is ${SwiftInteropVersion}")
if (${SwiftInteropVersion} VERSION_LESS 16)
message(FATAL_ERROR "Swift: reverse interop support is too old. Update your toolchain.")
endif()
else()
message(FATAL_ERROR "Swift: reverse interop is required, but not supported. Update your toolchain.")
endif()
set(target_includes_expr "$<TARGET_PROPERTY:${target_name},INCLUDE_DIRECTORIES>")
if(ARG_SOURCES)
set(target_sources ${ARG_SOURCES})
else()
get_target_property(target_sources ${target_name} SOURCES)
get_target_property(target_source_dir ${target_name} SOURCE_DIR)
list(TRANSFORM target_sources PREPEND "${target_source_dir}/")
endif()
set (SwiftFrontendOpts )
string(REGEX MATCHALL "-Xcc [-=/a-zA-Z0-9_.]+" SwiftXccOptionsFlags "${CMAKE_Swift_FLAGS}")
string(REGEX MATCHALL "-target [-=/a-zA-Z0-9_.]+" SwiftTargetFlags "${CMAKE_Swift_FLAGS}")
string(REGEX MATCHALL "-sdk [-=/a-zA-Z0-9_.]+" SwiftSDKFlags "${CMAKE_Swift_FLAGS}")
string(REGEX MATCHALL "-module-cache-path [-=/a-zA-Z0-9_.]+" SwiftMCFlags "${CMAKE_Swift_FLAGS}")
foreach (flag ${SwiftXccOptionsFlags})
string(SUBSTRING ${flag} 5 -1 clangFlag)
list(APPEND SwiftFrontendOpts "-Xcc")
list(APPEND SwiftFrontendOpts "${clangFlag}")
endforeach()
set(FlagName "-cxx-interoperability-mode")
string(REGEX MATCHALL "${FlagName}=[-_/a-zA-Z0-9.]+" SwiftEqFlags "${CMAKE_Swift_FLAGS}")
foreach (flag ${SwiftEqFlags})
list(APPEND SwiftFrontendOpts "${flag}")
endforeach()
foreach (flag ${SwiftTargetFlags})
string(SUBSTRING ${flag} 8 -1 clangFlag)
list(APPEND SwiftFrontendOpts "-target")
list(APPEND SwiftFrontendOpts "${clangFlag}")
endforeach()
foreach (flag ${SwiftSDKFlags})
string(SUBSTRING ${flag} 5 -1 clangFlag)
list(APPEND SwiftFrontendOpts "-sdk")
list(APPEND SwiftFrontendOpts "${clangFlag}")
endforeach()
foreach (flag ${SwiftMCFlags})
string(SUBSTRING ${flag} 19 -1 clangFlag)
list(APPEND SwiftFrontendOpts "-module-cache-path")
list(APPEND SwiftFrontendOpts "${clangFlag}")
endforeach()
add_custom_command(
OUTPUT
"${header_path}"
COMMAND
${CMAKE_Swift_COMPILER} -frontend -typecheck
${target_sources}
-module-name "${target_name}"
-emit-clang-header-path "${header_path}"
"$<$<BOOL:${target_includes_expr}>:-I$<JOIN:${target_includes_expr},;-I>>"
${ARG_FLAGS}
${SwiftFrontendOpts}
DEPENDS
"${target_sources}"
COMMAND_EXPAND_LISTS
COMMENT
"Generating '${header_path}'"
)
add_custom_target(${header_target_name}
DEPENDS
"${header_path}"
)
endfunction()

View File

@ -0,0 +1,52 @@
set(FOUNDATIONDB_CROSS_COMPILING ON CACHE STRING "" FORCE)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON CACHE STRING "" FORCE)
# The target is x86_64 Linux.
set(CMAKE_SYSTEM_NAME Linux CACHE STRING "" FORCE)
set(CMAKE_SYSTEM_PROCESSOR X86_64 CACHE STRING "" FORCE)
# Setup the host tools.
if (NOT FOUNDATIONDB_SWIFT_TOOLCHAIN_ROOT)
message(FATAL_ERROR "Cross compilation: Missing FoundationDB swift toolchain root (FOUNDATIONDB_SWIFT_TOOLCHAIN_ROOT=<toolchain>/usr).")
endif()
set(CMAKE_Swift_COMPILER ${FOUNDATIONDB_SWIFT_TOOLCHAIN_ROOT}/bin/swiftc CACHE STRING "" FORCE)
set(CMAKE_C_COMPILER ${FOUNDATIONDB_SWIFT_TOOLCHAIN_ROOT}/bin/clang CACHE STRING "" FORCE)
set(CMAKE_CXX_COMPILER ${FOUNDATIONDB_SWIFT_TOOLCHAIN_ROOT}/bin/clang++ CACHE STRING "" FORCE)
if (NOT FOUNDATIONDB_LLVM_TOOLCHAIN_ROOT)
message(FATAL_ERROR "Cross compilation: Missing FoundationDB LLVM toolchain root (FOUNDATIONDB_LLVM_TOOLCHAIN_ROOT=<toolchain>/usr).")
endif()
set(CMAKE_LINKER ${FOUNDATIONDB_LLVM_TOOLCHAIN_ROOT}/bin/ld.lld CACHE STRING "" FORCE)
set(CMAKE_AR ${FOUNDATIONDB_LLVM_TOOLCHAIN_ROOT}/bin/llvm-ar CACHE STRING "" FORCE)
set(CMAKE_RANLIB ${FOUNDATIONDB_LLVM_TOOLCHAIN_ROOT}/bin/llvm-ranlib CACHE STRING "" FORCE)
set(MONO_EXECUTABLE /Library/Frameworks/Mono.framework/Versions/Current/bin/mono CACHE STRING "" FORCE)
if(NOT EXISTS ${MONO_EXECUTABLE})
message(FATAL_ERROR "Cross compilation: Mono is not installed.")
endif()
set(MCS_EXECUTABLE /Library/Frameworks/Mono.framework/Versions/Current/bin/mcs CACHE STRING "" FORCE)
execute_process(COMMAND which python3
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
OUTPUT_VARIABLE
WhichPython3
)
string(STRIP ${WhichPython3} WhichPython3Trimmed)
set(Python3_EXECUTABLE ${WhichPython3Trimmed} CACHE STRING "" FORCE)
# Set up the SDK root and compiler flags.
if (NOT FOUNDATIONDB_LINUX_CONTAINER_ROOT)
message(FATAL_ERROR "Cross compilation: Missing FoundationDB linux container path (FOUNDATIONDB_LINUX_CONTAINER_ROOT).")
endif()
set(CMAKE_SYSROOT "${FOUNDATIONDB_LINUX_CONTAINER_ROOT}" CACHE STRING "" FORCE)
# FIXME: Do not hardcode 11.
set(CMAKE_Swift_COMPILER_EXTERNAL_TOOLCHAIN "${CMAKE_SYSROOT}/opt/rh/devtoolset-11/root/usr" CACHE STRING "" FORCE)
string(TOLOWER ${CMAKE_SYSTEM_PROCESSOR} TripleArch)
set(CMAKE_C_FLAGS "-target ${TripleArch}-unknown-linux-gnu -fuse-ld=${CMAKE_LINKER} --gcc-toolchain=${CMAKE_Swift_COMPILER_EXTERNAL_TOOLCHAIN}" CACHE STRING "" FORCE)
set(CMAKE_CXX_FLAGS "-target ${TripleArch}-unknown-linux-gnu -fuse-ld=${CMAKE_LINKER} --gcc-toolchain=${CMAKE_Swift_COMPILER_EXTERNAL_TOOLCHAIN}" CACHE STRING "" FORCE)
set(COMPILE_BOOST_CXXFLAGS "-target;${TripleArch}-linux-gnu;-fuse-ld=${CMAKE_LINKER};--gcc-toolchain=${CMAKE_Swift_COMPILER_EXTERNAL_TOOLCHAIN};--sysroot;${CMAKE_SYSROOT}" CACHE STRING "" FORCE)
# CMake might think it's linking MachO files and pass this flag, so avoid it.
set(HAVE_FLAG_SEARCH_PATHS_FIRST OFF CACHE BOOL "" FORCE)

View File

@ -42,6 +42,33 @@ print("transform {} with build directory {}".format(args.input, args.builddir))
with open(args.input) as f:
cmds = json.load(f)
swiftCompilationCommands = {}
if len(args.ninjatool) > 0:
print("aquiring Swift compile commands using {}".format(args.ninjatool))
try:
ninjaInvocation = subprocess.run([args.ninjatool, "-t", "compdb"], cwd=args.builddir, capture_output=True)
ninjaCMDs = json.loads(ninjaInvocation.stdout.decode('utf-8'))
for fileCmd in ninjaCMDs:
file = fileCmd['file']
if not file.endswith('.swift'):
continue
cmd = fileCmd['command']
# Skip invocations that are used to emit the header file for C++.
if cmd.find('-emit-clang-header') != -1:
continue
# Cleanup '&&' and ':' tokens from command generated by Ninja.
transformedSubCmd = [x for x in shlex.split(cmd) if x != '&&' and x != ':']
cmd = shlex.join(transformedSubCmd)
# Figure out which files are actually using this command.
filesInCmd = [x for x in transformedSubCmd if x.endswith('.swift')]
for cmdFile in filesInCmd:
swiftCompilationCommands[cmdFile] = {'file': cmdFile, 'command': cmd, 'directory': fileCmd['directory']}
except:
print("error: failed to aquire Swift compilation commands")
result = []
for cmd in cmds:
@ -54,6 +81,9 @@ for cmd in cmds:
cmd["command"] = actorCommand(cmd["command"], args.builddir, args.srcdir)
cmd["file"] = actorFile(cmd["file"], args.builddir, args.srcdir)
result.append(cmd)
elif cmd['file'].endswith('.swift'):
if cmd['file'] in swiftCompilationCommands:
result.append(swiftCompilationCommands[cmd['file']])
else:
result.append(cmd)

View File

@ -74,6 +74,46 @@ configure_file(${CMAKE_CURRENT_SOURCE_DIR}/versions.h.cmake ${CMAKE_CURRENT_BINA
add_dependencies(fdbclient fdboptions)
target_link_libraries(fdbclient PUBLIC fdbrpc msgpack PRIVATE rapidxml)
# Setup the Swift sources in FDBClient.
if(WITH_SWIFT)
include(FindSwiftLibs)
add_library(fdbclient_swift STATIC
notified_support.swift
)
target_include_directories(fdbclient_swift PUBLIC
"${CMAKE_BINARY_DIR}/flow/include"
"${CMAKE_BINARY_DIR}/flow/"
"${CMAKE_BINARY_DIR}/fdbclient/include"
"${CMAKE_SOURCE_DIR}/fdbrpc/include"
"${CMAKE_BINARY_DIR}/fdbrpc/include"
"${CMAKE_SOURCE_DIR}/contrib/fmt-8.1.1/include"
"${CMAKE_SOURCE_DIR}/contrib/md5/include"
"${CMAKE_SOURCE_DIR}/contrib/libb64/include"
"${CMAKE_SOURCE_DIR}/contrib/sqlite"
"${Boost_DIR}/../../../include"
"${msgpack_DIR}/include"
)
# Generate the module map for FDBClient.
include(GenerateModulemap)
set(FDBCLIENT_BINARY_DIR "${CMAKE_BINARY_DIR}/fdbclient")
generate_modulemap("${CMAKE_BINARY_DIR}/fdbclient/include" "FDBClient" fdbclient)
# TODO: the TBD validation skip is because of swift_job_run_generic, though it seems weird why we need to do that?
target_compile_options(fdbclient_swift PRIVATE "$<$<COMPILE_LANGUAGE:Swift>:SHELL:-Xcc -std=c++20 -Xfrontend -validate-tbd-against-ir=none -Xcc -DNO_INTELLISENSE -Xcc -ivfsoverlay${CMAKE_BINARY_DIR}/flow/include/headeroverlay.yaml -Xcc -ivfsoverlay${CMAKE_BINARY_DIR}/fdbclient/include/headeroverlay.yaml -Xcc -ivfsoverlay${CMAKE_BINARY_DIR}/fdbclient/include/headeroverlay.yaml>")
add_dependencies(fdbclient_swift flow_swift fdbclient_actors fdbrpc_actors fdboptions)
# This does not work! (see rdar://99107402)
# target_link_libraries(flow PRIVATE flow_swift)
add_dependencies(fdbclient fdbclient_swift)
target_link_options(fdbclient_swift PUBLIC "$<TARGET_OBJECTS:flow_swift>")
target_link_options(fdbclient PRIVATE "$<TARGET_OBJECTS:flow_swift>")
target_link_options(fdbclient PRIVATE "$<TARGET_OBJECTS:fdbclient_swift>")
endif()
# Create a separate fdbclient library with sampling enabled. This lets
# fdbserver retain sampling functionality in client code while disabling
# sampling for pure clients.
@ -98,3 +138,4 @@ if(WITH_AWS_BACKUP)
target_link_libraries(fdbclient PUBLIC awssdk_target)
target_link_libraries(fdbclient_sampling PUBLIC awssdk_target)
endif()

View File

@ -555,14 +555,14 @@ void ServerKnobs::initialize(Randomize randomize, ClientKnobs* clientKnobs, IsSi
init( ROCKSDB_VERIFY_CHECKSUM_BEFORE_RESTORE, true );
init( ROCKSDB_ENABLE_CHECKPOINT_VALIDATION, false ); if ( randomize && BUGGIFY ) ROCKSDB_ENABLE_CHECKPOINT_VALIDATION = deterministicRandom()->coinflip();
init( ROCKSDB_RETURN_OVERLOADED_ON_TIMEOUT, true );
init( ROCKSDB_COMPACTION_PRI, 3 ); // kMinOverlappingRatio, RocksDB default.
init( ROCKSDB_COMPACTION_PRI, 3 ); // kMinOverlappingRatio, RocksDB default.
init( ROCKSDB_WAL_RECOVERY_MODE, 2 ); // kPointInTimeRecovery, RocksDB default.
init( ROCKSDB_TARGET_FILE_SIZE_BASE, 0 ); // If 0, pick RocksDB default.
init( ROCKSDB_TARGET_FILE_SIZE_MULTIPLIER, 1 ); // RocksDB default.
init( ROCKSDB_USE_DIRECT_READS, false );
init( ROCKSDB_USE_DIRECT_IO_FLUSH_COMPACTION, false );
init( ROCKSDB_MAX_OPEN_FILES, -1 ); // RocksDB default.
init( ROCKSDB_USE_POINT_DELETE_FOR_SYSTEM_KEYS, false );
init( ROCKSDB_USE_POINT_DELETE_FOR_SYSTEM_KEYS, false );
init( ROCKSDB_CF_RANGE_DELETION_LIMIT, 0 );
init( ROCKSDB_MEMTABLE_MAX_RANGE_DELETIONS, 0 );
init( ROCKSDB_WAIT_ON_CF_FLUSH, false );
@ -1320,6 +1320,12 @@ void ServerKnobs::initialize(Randomize randomize, ClientKnobs* clientKnobs, IsSi
init( CONSISTENCY_SCAN_ACTIVE_THROTTLE_RATIO, 0.5 ); if( randomize && BUGGIFY ) CONSISTENCY_SCAN_ACTIVE_THROTTLE_RATIO = deterministicRandom()->random01();
init( FLOW_WITH_SWIFT, false);
#ifndef WITH_SWIFT
ASSERT(!FLOW_WITH_SWIFT); // cannot enable FLOW_WITH_SWIFT server knob without compiling Swift
#endif
// Drop in-memory state associated with an idempotency id after this many seconds. Once dropped, this id cannot be
// expired proactively, but will eventually get cleaned up by the idempotency id cleaner.
init( IDEMPOTENCY_ID_IN_MEMORY_LIFETIME, 10);

View File

@ -29,7 +29,7 @@
FDB_BOOLEAN_PARAM(Randomize);
FDB_BOOLEAN_PARAM(IsSimulated);
class ClientKnobs : public KnobsImpl<ClientKnobs> {
class SWIFT_CXX_IMMORTAL_SINGLETON_TYPE ClientKnobs : public KnobsImpl<ClientKnobs> {
public:
int TOO_MANY; // FIXME: this should really be split up so we can control these more specifically

View File

@ -529,7 +529,7 @@ struct GetBlobGranuleLocationsRequest {
}
};
struct GetRawCommittedVersionReply {
struct SWIFT_CXX_IMPORT_OWNED GetRawCommittedVersionReply {
constexpr static FileIdentifier file_identifier = 1314732;
Optional<UID> debugID;
Version version;
@ -548,7 +548,7 @@ struct GetRawCommittedVersionReply {
}
};
struct GetRawCommittedVersionRequest {
struct SWIFT_CXX_IMPORT_OWNED GetRawCommittedVersionRequest {
constexpr static FileIdentifier file_identifier = 12954034;
SpanContext spanContext;
Optional<UID> debugID;
@ -567,7 +567,7 @@ struct GetRawCommittedVersionRequest {
}
};
struct GetStorageServerRejoinInfoReply {
struct SWIFT_CXX_IMPORT_OWNED GetStorageServerRejoinInfoReply {
constexpr static FileIdentifier file_identifier = 9469225;
Version version;
Tag tag;

View File

@ -24,6 +24,7 @@
#include "fdbclient/FDBTypes.h"
#include "flow/TDMetric.actor.h"
#include "flow/swift_support.h"
#include <queue>
template <class T>

View File

@ -22,6 +22,7 @@
#include "flow/BooleanParam.h"
#include "flow/Knobs.h"
#include "flow/swift_support.h"
#include "fdbrpc/fdbrpc.h"
#include "fdbrpc/Locality.h"
#include "fdbclient/ClientKnobs.h"
@ -29,7 +30,7 @@
// Disk queue
static constexpr int _PAGE_SIZE = 4096;
class ServerKnobs : public KnobsImpl<ServerKnobs> {
class SWIFT_CXX_IMMORTAL_SINGLETON_TYPE ServerKnobs : public KnobsImpl<ServerKnobs> {
public:
bool ALLOW_DANGEROUS_KNOBS;
// Versions
@ -1351,6 +1352,9 @@ public:
double IDEMPOTENCY_IDS_CLEANER_POLLING_INTERVAL;
double IDEMPOTENCY_IDS_MIN_AGE_SECONDS;
// Swift: Enable the Swift runtime hooks and use Swift implementations where possible
bool FLOW_WITH_SWIFT;
ServerKnobs(Randomize, ClientKnobs*, IsSimulated);
void initialize(Randomize, ClientKnobs*, IsSimulated);
};

View File

@ -0,0 +1,14 @@
import Flow
import flow_swift
import FDBClient
// ==== ---------------------------------------------------------------------------------------------------------------
/*extension NotifiedVersion {
/// async version of `whenAtLeast`
public func atLeast(_ limit: VersionMetricHandle.ValueType) async throws {
var f: FutureVoid = self.whenAtLeast(limit)
_ = try await f.value()
}
}*/

View File

@ -86,4 +86,4 @@ if(WIN32)
add_dependencies(tokensign_actors fdbrpc_actors)
endif()
add_subdirectory(tests)
add_subdirectory(tests)

View File

@ -257,6 +257,7 @@ struct YieldMockNetwork final : INetwork, ReferenceCounted<YieldMockNetwork> {
ActorLineageSet& getActorLineageSet() override { throw std::exception(); }
#endif
ProtocolVersion protocolVersion() const override { return baseNetwork->protocolVersion(); }
void _swiftEnqueue(void* task) override { baseNetwork->_swiftEnqueue(task); }
};
Future<Void> testCancelled(bool* exits, Future<Void> f) {
@ -1956,4 +1957,4 @@ TEST_CASE("/flow/coro/chooseRepeatedCancel") {
intPromise.send(3);
ASSERT(chooseFuture.getError().code() == error_code_actor_cancelled);
return Void();
}
}

View File

@ -244,6 +244,8 @@ struct YieldMockNetwork final : INetwork, ReferenceCounted<YieldMockNetwork> {
Future<class Void> orderedDelay(double seconds, TaskPriority taskID) override { return nextTick.getFuture(); }
void _swiftEnqueue(void* task) override { abort(); }
Future<class Void> yield(TaskPriority taskID) override {
if (check_yield(taskID))
return delay(0, taskID);

View File

@ -27,6 +27,7 @@
#include "flow/serialize.h"
#include <string>
#include <type_traits>
#include "flow/swift_support.h"
#pragma once
// Yet another performance statistics interface

View File

@ -30,6 +30,10 @@
#include "fdbrpc/networksender.actor.h"
#include "fdbrpc/simulator.h"
#ifdef WITH_SWIFT
#include <swift/bridging>
#endif /* WITH_SWIFT */
// Common endpoint code for NetSAV<> and NetNotifiedQueue<>
class FlowReceiver : public NetworkMessageReceiver, public NonCopyable {
Optional<PeerCompatibilityPolicy> peerCompatibilityPolicy_;
@ -131,12 +135,19 @@ public:
void send(U&& value) const {
sav->send(std::forward<U>(value));
}
// Swift can't call method that takes in a universal references (U&&),
// so provide a callable `send` method that copies the value.
void sendCopy(const T& valueCopy) const SWIFT_NAME(send(_:)) {
sav->send(valueCopy);
}
template <class E>
void sendError(const E& exc) const {
sav->sendError(exc);
}
void send(Never) { sendError(never_reply()); }
// SWIFT: Convenience method, since there is also a Swift.Never, so Never() could be confusing
void sendNever() const { send(Never()); }
Future<T> getFuture() const {
sav->addFutureRef();
@ -886,7 +897,7 @@ public:
explicit RequestStream(const Endpoint& endpoint) : queue(new NetNotifiedQueue<T, IsPublic>(0, 1, endpoint)) {}
FutureStream<T> getFuture() const {
SWIFT_CXX_IMPORT_UNSAFE FutureStream<T> getFuture() const {
queue->addFutureRef();
return FutureStream<T>(queue);
}

View File

@ -52,6 +52,9 @@
#include "crc32/crc32c.h"
#include "fdbrpc/TraceFileIO.h"
#include "flow/flow.h"
#include "flow/swift.h"
#include "flow/swift_concurrency_hooks.h"
#include "flow/swift/ABI/Task.h"
#include "flow/genericactors.actor.h"
#include "flow/network.h"
#include "flow/TLSConfig.actor.h"
@ -1065,6 +1068,20 @@ public:
return delay(seconds, taskID, currentProcess, true);
}
void _swiftEnqueue(void* _job) override {
#ifdef WITH_SWIFT
ASSERT(getCurrentProcess());
swift::Job* job = (swift::Job*)_job;
TaskPriority priority = swift_priority_to_net2(job->getPriority());
ASSERT(priority >= TaskPriority::Min && priority <= TaskPriority::Max);
ISimulator::ProcessInfo* machine = currentProcess;
auto t = new PromiseTask(machine, job);
taskQueue.addReady(priority, t);
#endif /* WITH_SWIFT */
}
Future<class Void> delay(double seconds, TaskPriority taskID, ProcessInfo* machine, bool ordered = false) {
ASSERT(seconds >= -0.0001);
@ -2599,9 +2616,12 @@ public:
struct PromiseTask final : public FastAllocated<PromiseTask> {
Promise<Void> promise;
ProcessInfo* machine;
swift::Job* _Nullable swiftJob = nullptr;
explicit PromiseTask(ProcessInfo* machine) : machine(machine) {}
PromiseTask(ProcessInfo* machine, Promise<Void>&& promise) : machine(machine), promise(std::move(promise)) {}
explicit PromiseTask(ProcessInfo* machine) : machine(machine), swiftJob(nullptr) {}
explicit PromiseTask(ProcessInfo* machine, swift::Job* swiftJob) : machine(machine), swiftJob(swiftJob) {}
PromiseTask(ProcessInfo* machine, Promise<Void>&& promise)
: machine(machine), promise(std::move(promise)), swiftJob(nullptr) {}
};
void execTask(struct PromiseTask& t) {
@ -2610,7 +2630,15 @@ public:
} else {
this->currentProcess = t.machine;
try {
#ifdef WITH_SWIFT
if (t.swiftJob) {
swift_job_run(t.swiftJob, ExecutorRef::generic());
} else {
t.promise.send(Void());
}
#else
t.promise.send(Void());
#endif
ASSERT(this->currentProcess == t.machine);
} catch (Error& e) {
TraceEvent(SevError, "UnhandledSimulationEventError").errorUnsuppressed(e);

View File

@ -0,0 +1,38 @@
/*
* swift_sim2_hooks.cpp
*
* This source file is part of the FoundationDB open source project
*
* Copyright 2013-2022 Apple Inc. and the FoundationDB project authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "fdbrpc/simulator.h"
#include "flow/flow.h"
#include "flow/network.h"
#include "flow/TLSConfig.actor.h"
#include "flow/swift_concurrency_hooks.h"
#include "flow/swift.h"
#include "flow/swift/ABI/Task.h"
//// ==== --------------------------------------------------------------------------------------------------------------
//// ==== Sim2 hooks
SWIFT_CC(swift)
void sim2_enqueueGlobal_hook_impl(swift::Job* _Nonnull job, void (*_Nonnull)(swift::Job*) __attribute__((swiftcall))) {
ISimulator* sim = g_simulator;
ASSERT(sim);
sim->_swiftEnqueue(job);
}

View File

@ -22,6 +22,118 @@ file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/workloads)
add_flow_target(EXECUTABLE NAME fdbserver SRCS ${FDBSERVER_SRCS})
if (WITH_SWIFT)
# Setup the Swift sources in FDBServer.
include(FindSwiftLibs)
add_definitions(-DWITH_SWIFT)
add_library(fdbserver_swift STATIC
masterserver.swift
swift_fdbserver_cxx_swift_value_conformance.swift
swift_fdbserver_collections.swift
swift_fdbserver_stream_support.swift
UID.swift
# tests
swift/tests/Rainbow.swift
swift/tests/SimpleSwiftTestSuite.swift
swift/tests/swift_tests.swift
swift/tests/swift_test_task.swift
swift/tests/swift_test_streams.swift
)
target_include_directories(fdbserver_swift PUBLIC
"${CMAKE_BINARY_DIR}/flow/include"
"${CMAKE_BINARY_DIR}/flow/"
"${CMAKE_BINARY_DIR}/fdbclient"
"${CMAKE_BINARY_DIR}/fdbclient/include"
"${CMAKE_BINARY_DIR}/fdbserver/include"
"${CMAKE_SOURCE_DIR}/fdbrpc/include"
"${CMAKE_BINARY_DIR}/fdbrpc/include"
"${CMAKE_SOURCE_DIR}/metacluster/include"
"${CMAKE_SOURCE_DIR}/contrib/fmt-8.1.1/include"
"${CMAKE_SOURCE_DIR}/contrib/md5/include"
"${CMAKE_SOURCE_DIR}/contrib/libb64/include"
"${CMAKE_SOURCE_DIR}/contrib/sqlite"
"${Boost_DIR}/../../../include"
"${msgpack_DIR}/include"
"${CMAKE_SOURCE_DIR}/contrib/rapidjson"
)
# TODO: the TBD validation skip is because of swift_job_run_generic, though it seems weird why we need to do that?
target_compile_options(fdbserver_swift PRIVATE "$<$<COMPILE_LANGUAGE:Swift>:SHELL:-Xcc -std=c++20 -Xfrontend -validate-tbd-against-ir=none -Xcc -DNO_INTELLISENSE -Xcc -ivfsoverlay${CMAKE_BINARY_DIR}/flow/include/headeroverlay.yaml -Xcc -ivfsoverlay${CMAKE_BINARY_DIR}/fdbclient/include/headeroverlay.yaml -Xcc -ivfsoverlay${CMAKE_BINARY_DIR}/fdbserver/include/headeroverlay.yaml>")
# Ensure that C++ code in fdbserver can import Swift using a compatibility header.
include(SwiftToCXXInterop)
add_swift_to_cxx_header_gen_target(
fdbserver_swift
fdbserver_swift_to_cxx_conformance_header
"${CMAKE_CURRENT_BINARY_DIR}/include/SwiftModules/FDBServer_CxxTypeConformances.h"
SOURCES
"${CMAKE_CURRENT_SOURCE_DIR}/swift_fdbserver_cxx_swift_value_conformance.swift"
FLAGS
-Xcc -fmodules-cache-path=${CLANG_MODULE_CACHE_PATH}
-Xcc -std=c++20 -Xcc -DNO_INTELLISENSE -Xcc -ivfsoverlay${CMAKE_BINARY_DIR}/flow/include/headeroverlay.yaml -Xcc -ivfsoverlay${CMAKE_BINARY_DIR}/fdbclient/include/headeroverlay.yaml -Xcc -ivfsoverlay${CMAKE_BINARY_DIR}/fdbserver/include/headeroverlay.yaml
# Important: This is needed to avoid including header that depends on this generated header.
-Xcc -DFDBSERVER_FORWARD_DECLARE_SWIFT_APIS
-Xcc -DFOUNDATIONDB_FDBSERVER_STREAM_SUPPORT_H
)
add_swift_to_cxx_header_gen_target(
fdbserver_swift
fdbserver_swift_header
"${CMAKE_CURRENT_BINARY_DIR}/include/SwiftModules/FDBServer"
FLAGS
-Xcc -fmodules-cache-path=${CLANG_MODULE_CACHE_PATH}
-Xcc -std=c++20 -Xcc -DNO_INTELLISENSE -Xcc -ivfsoverlay${CMAKE_BINARY_DIR}/flow/include/headeroverlay.yaml -Xcc -ivfsoverlay${CMAKE_BINARY_DIR}/fdbclient/include/headeroverlay.yaml -Xcc -ivfsoverlay${CMAKE_BINARY_DIR}/fdbserver/include/headeroverlay.yaml
# Important: This is needed to avoid including the generated header while generating it.
-DFDBSERVER_FORWARD_DECLARE_SWIFT_APIS
-Xcc -DFDBSERVER_FORWARD_DECLARE_SWIFT_APIS
)
add_dependencies(fdbserver_swift_to_cxx_conformance_header flow_swift flow_swift_header flow_actors fdbclient_actors fdbrpc_actors fdbserver_actors fdboptions)
add_dependencies(fdbserver_swift_header fdbserver_swift_to_cxx_conformance_header)
add_dependencies(fdbserver_swift fdbserver_swift_header)
add_dependencies(fdbserver_swift flow_swift)
add_dependencies(fdbserver_swift flow_actors)
add_dependencies(fdbserver_swift fdbclient_swift)
add_dependencies(fdbserver_swift fdbserver_actors)
# This does not work! (see rdar://99107402)
# target_link_libraries(flow PRIVATE flow_swift)
add_dependencies(fdbserver fdbserver_swift)
# TODO(swift): This probably should already work, try using link_libraries again
# target_link_libraries(fdbserver PRIVATE flow_swift fdbserver_swift)
add_dependencies(fdbserver flow_swift fdbclient_swift fdbserver_swift)
add_custom_target(show_fdbserver_swift_objs COMMAND ${CMAKE_COMMAND} -E echo "$<TARGET_OBJECTS:fdbserver_swift>")
target_link_options(fdbserver_swift PUBLIC "$<TARGET_OBJECTS:flow_swift>")
target_link_options(fdbserver PRIVATE "$<TARGET_OBJECTS:flow_swift>")
target_link_options(fdbserver_swift PUBLIC "$<TARGET_OBJECTS:fdbclient_swift>")
target_link_options(fdbserver PRIVATE "$<TARGET_OBJECTS:fdbclient_swift>")
# This dependency ensures that fdbserver depends on fdbclient_swift at link time.
set_source_files_properties(IConfigConsumer.cpp PROPERTIES OBJECT_DEPENDS "${CMAKE_BINARY_DIR}/lib/libfdbclient_swift.a")
target_link_options(fdbserver PRIVATE "$<TARGET_OBJECTS:fdbserver_swift>")
# This dependency ensures that fdbserver depends on fdbserver_swift at link time.
set_source_files_properties(IConfigConsumer.cpp PROPERTIES OBJECT_DEPENDS "${CMAKE_BINARY_DIR}/lib/libfdbserver_swift.a")
swift_get_linker_search_paths(SWIFT_LINK_PATHS)
target_link_directories(fdbserver PRIVATE "${SWIFT_LINK_PATHS}")
# Generate the module map for FDBServer.
include(GenerateModulemap)
set(FDBSERVER_BINARY_DIR "${CMAKE_BINARY_DIR}/fdbserver")
generate_modulemap("${CMAKE_BINARY_DIR}/fdbserver/include" "FDBServer" fdbserver
OMIT
ArtMutationBuffer.h # actually a textual include
art_impl.h # actually a textual include
)
endif()
target_include_directories(fdbserver PRIVATE
${CMAKE_SOURCE_DIR}/bindings/c
${CMAKE_BINARY_DIR}/bindings/c
@ -51,6 +163,11 @@ if(WITH_ROCKSDB)
target_compile_definitions(fdbserver PRIVATE WITH_ROCKSDB)
endif()
if (WITH_SWIFT)
target_link_libraries(fdbserver PRIVATE swiftCxx swiftCxxStdlib)
endif()
# target_compile_definitions(fdbserver PRIVATE -DENABLE_SAMPLING)
if(GPERFTOOLS_FOUND)
target_link_libraries(fdbserver PRIVATE gperftools)
endif()

14
fdbserver/UID.swift Normal file
View File

@ -0,0 +1,14 @@
import Flow
import FDBServer
extension Flow.UID: Hashable {
public func hash(into hasher: inout Swift.Hasher) {
self.first().hash(into: &hasher)
self.second().hash(into: &hasher)
}
public static func ==(lhs: UID, rhs: UID) -> Swift.Bool {
lhs.first() == rhs.first() && lhs.second() == rhs.second()
}
}

View File

@ -86,6 +86,9 @@
#include "flow/flow.h"
#include "flow/network.h"
#include "flow/swift.h"
#include "flow/swift_concurrency_hooks.h"
#if defined(__linux__) || defined(__FreeBSD__)
#include <execinfo.h>
#include <signal.h>
@ -105,8 +108,21 @@
#include <Windows.h>
#endif
#if __has_include("SwiftModules/FDBServer")
class MasterData;
#include "SwiftModules/FDBServer"
#define SWIFT_REVERSE_INTEROP_SUPPORTED
#endif
#if __has_include("SwiftModules/Flow")
#include "SwiftModules/Flow"
#endif
#include "flow/actorcompiler.h" // This must be the last #include.
// FIXME(swift): remove those
extern "C" void swiftCallMeFuture(void* _Nonnull opaqueResultPromisePtr) noexcept;
using namespace std::literals;
// clang-format off
@ -373,6 +389,16 @@ void failAfter(Future<Void> trigger, Endpoint e) {
failAfter(trigger, g_simulator->getProcess(e));
}
#ifdef WITH_SWIFT
ACTOR void swiftTestRunner() {
auto p = PromiseVoid();
fdbserver_swift::swiftyTestRunner(p);
wait(p.getFuture());
flushAndExit(0);
}
#endif
ACTOR Future<Void> histogramReport() {
loop {
wait(delay(SERVER_KNOBS->HISTOGRAM_REPORT_INTERVAL));
@ -2038,11 +2064,38 @@ int main(int argc, char* argv[]) {
// startOldSimulator();
opts.buildNetwork(argv[0]);
startNewSimulator(opts.printSimTime);
if (SERVER_KNOBS->FLOW_WITH_SWIFT) {
// TODO (Swift): Make it TraceEvent
// printf("[%s:%d](%s) Installed Swift concurrency hooks: sim2 (g_network)\n",
// __FILE_NAME__,
// __LINE__,
// __FUNCTION__);
installSwiftConcurrencyHooks(role == ServerRole::Simulation, g_network);
}
openTraceFile({}, opts.rollsize, opts.maxLogsSize, opts.logFolder, "trace", opts.logGroup);
openTracer(TracerType(deterministicRandom()->randomInt(static_cast<int>(TracerType::DISABLED),
static_cast<int>(TracerType::SIM_END))));
} else {
g_network = newNet2(opts.tlsConfig, opts.useThreadPool, true);
if (SERVER_KNOBS->FLOW_WITH_SWIFT) {
installSwiftConcurrencyHooks(role == ServerRole::Simulation, g_network);
// TODO (Swift): Make it TraceEvent
// printf("[%s:%d](%s) Installed Swift concurrency hooks: net2 (g_network)\n",
// __FILE_NAME__,
// __LINE__,
// __FUNCTION__);
}
#if WITH_SWIFT
// Set FDBSWIFTTEST env variable to execute some simple Swift/Flow interop tests.
if (SERVER_KNOBS->FLOW_WITH_SWIFT && getenv("FDBSWIFTTEST")) {
swiftTestRunner(); // spawns actor that will call Swift functions
}
#endif
g_network->addStopCallback(Net2FileSystem::stop);
FlowTransport::createInstance(false, 1, WLTOKEN_RESERVED_COUNT, &opts.allowList);
opts.buildNetwork(argv[0]);

View File

@ -31,8 +31,18 @@
#include "fdbserver/ServerDBInfo.h"
#include "flow/ActorCollection.h"
#include "flow/Trace.h"
#include "flow/swift_support.h"
#include "fdbclient/VersionVector.h"
#ifdef WITH_SWIFT
#ifndef FDBSERVER_FORWARD_DECLARE_SWIFT_APIS
// Forward declare C++ MasterData type.
struct MasterData;
#include "SwiftModules/FDBServer"
#endif
#endif // WITH_SWIFT
// When actually compiled (NO_INTELLISENSE), include the generated version of this file. In intellisense use the source
// version.
#if defined(NO_INTELLISENSE) && !defined(FDBSERVER_MASTERDATA_ACTOR_G_H)
@ -42,8 +52,14 @@
#define FDBSERVER_MASTERDATA_ACTOR_H
#include "flow/actorcompiler.h" // This must be the last #include
// FIXME(swift): Remove once https://github.com/apple/swift/issues/61620 is fixed.
#define SWIFT_CXX_REF_MASTERDATA \
__attribute__((swift_attr("import_reference"))) __attribute__((swift_attr("retain:addrefMasterData"))) \
__attribute__((swift_attr("release:delrefMasterData")))
// A type with Swift value semantics for working with `Counter` types.
class CounterValue {
// FIXME(swift): Delete immortal annotation from `Counter`.
public:
using Value = Counter::Value;
@ -57,7 +73,20 @@ private:
std::shared_ptr<Counter> value;
};
struct MasterData : NonCopyable, ReferenceCounted<MasterData> {
// A concrete Optional<Version> type that can be referenced in Swift.
using OptionalVersion = Optional<Version>;
#ifdef WITH_SWIFT
#ifdef FDBSERVER_FORWARD_DECLARE_SWIFT_APIS
// Forward declare the Swift actor.
namespace fdbserver_swift {
class MasterDataActor;
}
#endif
#endif
// FIXME (after the one below): Use SWIFT_CXX_REF once https://github.com/apple/swift/issues/61620 is fixed.
struct SWIFT_CXX_REF_MASTERDATA MasterData : NonCopyable, ReferenceCounted<MasterData> {
UID dbgid;
Version lastEpochEnd, // The last version in the old epoch not (to be) rolled back in this recovery
@ -76,6 +105,7 @@ struct MasterData : NonCopyable, ReferenceCounted<MasterData> {
double lastVersionTime;
Optional<Version> referenceVersion;
// When using Swift master server impl this is declared in Swift.
std::map<UID, CommitProxyVersionReplies> lastCommitProxyVersionReplies;
MasterInterface myInterface;
@ -108,6 +138,19 @@ struct MasterData : NonCopyable, ReferenceCounted<MasterData> {
Future<Void> logger;
Future<Void> balancer;
#ifdef WITH_SWIFT
std::unique_ptr<fdbserver_swift::MasterDataActor> swiftImpl;
#ifndef FDBSERVER_FORWARD_DECLARE_SWIFT_APIS
// This function is not available to Swift as the FDBServer header is
// being generated, as we do not yet have `MasterDataActor` type definition
// in C++.
inline fdbserver_swift::MasterDataActor getSwiftImpl() const { return *swiftImpl; }
#endif
void setSwiftImpl(fdbserver_swift::MasterDataActor* impl);
#endif
MasterData(Reference<AsyncVar<ServerDBInfo> const> const& dbInfo,
MasterInterface const& myInterface,
ServerCoordinators const& coordinators,
@ -122,5 +165,22 @@ struct MasterData : NonCopyable, ReferenceCounted<MasterData> {
inline ResolutionBalancer* getResolutionBalancer() { return &resolutionBalancer; }
};
// FIXME(swift): Remove once https://github.com/apple/swift/issues/61620 is fixed.
inline void addrefMasterData(MasterData* ptr) {
addref(ptr);
}
// FIXME: Remove once https://github.com/apple/swift/issues/61620 is fixed.
inline void delrefMasterData(MasterData* ptr) {
delref(ptr);
}
using ReferenceMasterData = Reference<MasterData>;
using StdVectorOfUIDs = std::vector<UID>;
// FIXME: Workaround for linker issue (rdar://101092732).
void swift_workaround_setLatestRequestNumber(NotifiedVersion& latestRequestNum, Version v);
#include "flow/unactorcompiler.h"
#endif

View File

@ -32,9 +32,15 @@
#include "fdbclient/StorageServerInterface.h"
#include "fdbserver/ResolverInterface.h"
#include "fdbserver/TLogInterface.h"
#include "flow/swift_support.h"
#ifdef WITH_SWIFT
#include "flow/swift_future_support.h"
#endif /* WITH_SWIFT */
using DBRecoveryCount = uint64_t;
// A concrete type that can be referenced (in the context of Optional<set<Tag>>) in Swift.
using SetTag = std::set<Tag>;
using OptionalSetTag = Optional<SetTag>;
using OptionalInt64 = Optional<int64_t>;
@ -140,7 +146,7 @@ struct GetCommitVersionReply {
}
};
struct GetCommitVersionRequest {
struct SWIFT_CXX_IMPORT_OWNED GetCommitVersionRequest {
constexpr static FileIdentifier file_identifier = 16683181;
SpanContext spanContext;
uint64_t requestNum;
@ -171,7 +177,7 @@ struct GetTLogPrevCommitVersionReply {
}
};
struct UpdateRecoveryDataRequest {
struct SWIFT_CXX_IMPORT_OWNED UpdateRecoveryDataRequest {
constexpr static FileIdentifier file_identifier = 13605417;
Version recoveryTransactionVersion;
Version lastEpochEnd;
@ -205,7 +211,7 @@ struct UpdateRecoveryDataRequest {
}
};
struct ReportRawCommittedVersionRequest {
struct SWIFT_CXX_IMPORT_OWNED ReportRawCommittedVersionRequest {
constexpr static FileIdentifier file_identifier = 1853148;
Version version;
bool locked;
@ -264,7 +270,9 @@ struct NotifiedValue {
using ValueType = decltype(std::declval<T>().get());
explicit NotifiedValue(ValueType v = 0) : value(std::make_shared<T>(v)) {}
[[nodiscard]] Future<Void> whenAtLeast(const ValueType& limit) { return value->whenAtLeast(limit); }
[[nodiscard]] __attribute__((swift_attr("import_unsafe"))) Future<Void> whenAtLeast(const ValueType& limit) {
return value->whenAtLeast(limit);
}
[[nodiscard]] ValueType get() const { return value->get(); }

View File

@ -35,7 +35,8 @@
#include "flow/genericactors.actor.h"
#include "flow/actorcompiler.h" // must be last include
struct ResolutionBalancer {
struct __attribute__((swift_attr("import_reference"))) __attribute__((swift_attr("retain:immortal")))
__attribute__((swift_attr("release:immortal"))) ResolutionBalancer {
AsyncVar<Standalone<VectorRef<ResolverMoveRef>>> resolverChanges;
Version resolverChangesVersion = invalidVersion;
std::set<UID> resolverNeedingChanges;

View File

@ -158,7 +158,7 @@ struct WorkerDetails {
// This interface and its serialization depend on slicing, since the client will deserialize only the first part of this
// structure
struct ClusterControllerFullInterface {
struct SWIFT_CXX_IMPORT_OWNED ClusterControllerFullInterface {
constexpr static FileIdentifier file_identifier = ClusterControllerClientInterface::file_identifier;
ClusterInterface clientInterface;
RequestStream<struct RecruitFromConfigurationRequest> recruitFromConfiguration;

View File

@ -21,4 +21,35 @@
#ifndef FOUNDATIONDB_FDBSERVER_STREAM_SUPPORT_H
#define FOUNDATIONDB_FDBSERVER_STREAM_SUPPORT_H
#include "flow/swift.h"
#include "flow/flow.h"
#include "flow/unsafe_swift_compat.h"
#include "flow/swift_stream_support.h"
#include "pthread.h"
#include <stdint.h>
#include "MasterInterface.h"
#include "SwiftModules/FDBServer_CxxTypeConformances.h"
// ==== ----------------------------------------------------------------------------------------------------------------
// MARK: type aliases
#define SWIFT_FUTURE_STREAM(TYPE) \
using CONCAT3(FutureStream, _, TYPE) = FutureStream<struct TYPE>; \
using CONCAT3(FlowSingleCallbackForSwiftContinuation, _, TYPE) = FlowSingleCallbackForSwiftContinuation<TYPE>;
#define SWIFT_REQUEST_STREAM(TYPE) using CONCAT3(RequestStream, _, TYPE) = RequestStream<struct TYPE>;
SWIFT_FUTURE_STREAM(UpdateRecoveryDataRequest)
SWIFT_REQUEST_STREAM(UpdateRecoveryDataRequest)
SWIFT_FUTURE_STREAM(GetCommitVersionRequest)
SWIFT_REQUEST_STREAM(GetCommitVersionRequest)
SWIFT_FUTURE_STREAM(GetRawCommittedVersionRequest)
SWIFT_REQUEST_STREAM(GetRawCommittedVersionRequest)
SWIFT_FUTURE_STREAM(ReportRawCommittedVersionRequest)
SWIFT_REQUEST_STREAM(ReportRawCommittedVersionRequest)
#endif // FOUNDATIONDB_FDBSERVER_STREAM_SUPPORT_H

View File

@ -30,9 +30,14 @@
#include "fdbserver/ServerDBInfo.h"
#include "flow/ActorCollection.h"
#include "flow/Trace.h"
#include "flow/swift_support.h"
#include "fdbclient/VersionVector.h"
#include "fdbserver/MasterData.actor.h"
#ifdef WITH_SWIFT
#include "SwiftModules/FDBServer"
#endif
#include "flow/actorcompiler.h" // This must be the last #include.
// Instantiate MasterInterface related templates
@ -66,6 +71,17 @@ Version figureVersionCxx(Version current,
return std::clamp(expected, current + toAdd - maxOffset, current + toAdd + maxOffset);
}
#ifdef WITH_SWIFT
Version figureVersion(Version current,
double now,
Version reference,
int64_t toAdd,
double maxVersionRateModifier,
int64_t maxVersionRateOffset) {
auto impl = SERVER_KNOBS->FLOW_WITH_SWIFT ? fdbserver_swift::figureVersion : figureVersionCxx;
return impl(current, now, reference, toAdd, maxVersionRateModifier, maxVersionRateOffset);
}
#else
Version figureVersion(Version current,
double now,
Version reference,
@ -74,7 +90,26 @@ Version figureVersion(Version current,
int64_t maxVersionRateOffset) {
return figureVersionCxx(current, now, reference, toAdd, maxVersionRateModifier, maxVersionRateOffset);
}
#endif
#ifdef WITH_SWIFT
SWIFT_ACTOR Future<Void> waitForPrev(Reference<MasterData> self, ReportRawCommittedVersionRequest req) {
if (SERVER_KNOBS->FLOW_WITH_SWIFT) {
auto future = self->swiftImpl->waitForPrev(self.getPtr(), req);
wait(future);
} else {
state double startTime = now();
wait(self->liveCommittedVersion.whenAtLeast(req.prevVersion.get()));
double latency = now() - startTime;
self->waitForPrevLatencies->addMeasurement(latency);
++self->waitForPrevCommitRequests;
updateLiveCommittedVersion(self, req);
req.reply.send(Void());
}
return Void();
}
#else
ACTOR Future<Void> waitForPrev(Reference<MasterData> self, ReportRawCommittedVersionRequest req) {
state double startTime = now();
wait(self->liveCommittedVersion.whenAtLeast(req.prevVersion.get()));
@ -85,6 +120,15 @@ ACTOR Future<Void> waitForPrev(Reference<MasterData> self, ReportRawCommittedVer
return Void();
}
#endif
#ifdef WITH_SWIFT
SWIFT_ACTOR Future<Void> getVersionSwift(Reference<MasterData> self, GetCommitVersionRequest req) {
auto future = self->swiftImpl->getVersion(self.getPtr(), req);
wait(future);
return Void();
}
#endif
ACTOR Future<Void> getVersionCxx(Reference<MasterData> self, GetCommitVersionRequest req) {
state Span span("M:getVersion"_loc, req.spanContext);
@ -173,10 +217,22 @@ ACTOR Future<Void> getVersionCxx(Reference<MasterData> self, GetCommitVersionReq
return Void();
}
#ifdef WITH_SWIFT
ACTOR Future<Void> getVersion(Reference<MasterData> self, GetCommitVersionRequest req) {
if (SERVER_KNOBS->FLOW_WITH_SWIFT) {
wait(getVersionSwift(self, req));
return Void();
} else {
wait(getVersionCxx(self, req));
return Void();
}
}
#else
ACTOR Future<Void> getVersion(Reference<MasterData> self, GetCommitVersionRequest req) {
wait(getVersionCxx(self, req));
return Void();
}
#endif
CounterValue::CounterValue(std::string const& name, CounterCollection& collection)
: value(std::make_shared<Counter>(name, collection)) {}
@ -230,8 +286,20 @@ MasterData::MasterData(Reference<AsyncVar<ServerDBInfo> const> const& dbInfo,
SERVER_KNOBS->LATENCY_METRICS_LOGGING_INTERVAL,
SERVER_KNOBS->LATENCY_SKETCH_ACCURACY);
}
#ifdef WITH_SWIFT
using namespace fdbserver_swift;
// FIXME(swift): can we make a cleaner init?
swiftImpl.reset(new MasterDataActor((const MasterDataActor&)MasterDataActor::init()));
#endif
}
#ifdef WITH_SWIFT
void MasterData::setSwiftImpl(fdbserver_swift::MasterDataActor* impl) {
swiftImpl.reset(impl);
}
#endif
MasterData::~MasterData() {}
ACTOR Future<Void> provideVersionsCxx(Reference<MasterData> self) {
@ -245,10 +313,36 @@ ACTOR Future<Void> provideVersionsCxx(Reference<MasterData> self) {
}
}
#ifdef WITH_SWIFT
SWIFT_ACTOR Future<Void> provideVersionsSwift(Reference<MasterData> self) {
auto future = self->swiftImpl->provideVersions(self.getPtr());
wait(future);
return Void();
}
#endif
#ifdef WITH_SWIFT
ACTOR Future<Void> provideVersions(Reference<MasterData> self) {
if (SERVER_KNOBS->FLOW_WITH_SWIFT) {
wait(provideVersionsSwift(self));
} else {
wait(provideVersionsCxx(self));
}
return Void();
}
#else
ACTOR Future<Void> provideVersions(Reference<MasterData> self) {
wait(provideVersionsCxx(self));
return Void();
}
#endif
#ifdef WITH_SWIFT
void updateLiveCommittedVersionSwift(Reference<MasterData> self, ReportRawCommittedVersionRequest req) {
fdbserver_swift::updateLiveCommittedVersion(self.getPtr(), req);
}
#endif
void updateLiveCommittedVersionCxx(Reference<MasterData> self, ReportRawCommittedVersionRequest req) {
self->minKnownCommittedVersion = std::max(self->minKnownCommittedVersion, req.minKnownCommittedVersion);
@ -274,9 +368,27 @@ void updateLiveCommittedVersionCxx(Reference<MasterData> self, ReportRawCommitte
++self->reportLiveCommittedVersionRequests;
}
#ifdef WITH_SWIFT
void updateLiveCommittedVersion(Reference<MasterData> self, ReportRawCommittedVersionRequest req) {
if (SERVER_KNOBS->FLOW_WITH_SWIFT) {
return updateLiveCommittedVersionSwift(self, req);
} else {
return updateLiveCommittedVersionCxx(self, req);
}
}
#else
void updateLiveCommittedVersion(Reference<MasterData> self, ReportRawCommittedVersionRequest req) {
return updateLiveCommittedVersionCxx(self, req);
}
#endif
#ifdef WITH_SWIFT
SWIFT_ACTOR Future<Void> serveLiveCommittedVersionSwift(Reference<MasterData> self) {
auto future = self->swiftImpl->serveLiveCommittedVersion(self.getPtr());
wait(future);
return Void();
}
#endif
ACTOR Future<Void> serveLiveCommittedVersionCxx(Reference<MasterData> self) {
loop {
@ -318,10 +430,29 @@ ACTOR Future<Void> serveLiveCommittedVersionCxx(Reference<MasterData> self) {
}
}
#ifdef WITH_SWIFT
ACTOR Future<Void> serveLiveCommittedVersion(Reference<MasterData> self) {
if (SERVER_KNOBS->FLOW_WITH_SWIFT) {
wait(serveLiveCommittedVersionSwift(self));
} else {
wait(serveLiveCommittedVersionCxx(self));
}
return Void();
}
#else
ACTOR Future<Void> serveLiveCommittedVersion(Reference<MasterData> self) {
wait(serveLiveCommittedVersionCxx(self));
return Void();
}
#endif
#ifdef WITH_SWIFT
SWIFT_ACTOR Future<Void> updateRecoveryDataSwift(Reference<MasterData> self) {
auto future = self->swiftImpl->serveUpdateRecoveryData(self.getPtr());
wait(future);
return Void();
}
#endif
ACTOR Future<Void> updateRecoveryDataCxx(Reference<MasterData> self) {
loop {
@ -365,10 +496,21 @@ ACTOR Future<Void> updateRecoveryDataCxx(Reference<MasterData> self) {
}
}
#ifdef WITH_SWIFT
ACTOR Future<Void> updateRecoveryData(Reference<MasterData> self) {
if (SERVER_KNOBS->FLOW_WITH_SWIFT) {
wait(updateRecoveryDataSwift(self));
} else {
wait(updateRecoveryDataCxx(self));
}
return Void();
}
#else
ACTOR Future<Void> updateRecoveryData(Reference<MasterData> self) {
wait(updateRecoveryDataCxx(self));
return Void();
}
#endif
static std::set<int> const& normalMasterErrors() {
static std::set<int> s;
@ -477,6 +619,36 @@ ACTOR Future<Void> masterServerCxx(MasterInterface mi,
}
}
#ifdef WITH_SWIFT
ACTOR Future<Void> masterServerImpl(MasterInterface mi,
Reference<AsyncVar<ServerDBInfo> const> db,
Reference<AsyncVar<Optional<ClusterControllerFullInterface>> const> ccInterface,
ServerCoordinators coordinators,
LifetimeToken lifetime,
bool forceRecovery) {
if (SERVER_KNOBS->FLOW_WITH_SWIFT) {
auto promise = Promise<Void>();
state PromiseStream<Future<Void>> addActor;
state Reference<MasterData> self(
new MasterData(db, mi, coordinators, db->get().clusterInterface, ""_sr, addActor, forceRecovery));
fdbserver_swift::masterServerSwift(
mi,
const_cast<AsyncVar<ServerDBInfo>*>(db.getPtr()),
const_cast<AsyncVar<Optional<ClusterControllerFullInterface>>*>(ccInterface.getPtr()),
coordinators,
lifetime,
forceRecovery,
self.getPtr(),
/*result=*/promise);
Future<Void> f = promise.getFuture();
wait(f);
return Void();
} else {
wait(masterServerCxx(mi, db, ccInterface, coordinators, lifetime, forceRecovery));
return Void();
}
}
#else
ACTOR Future<Void> masterServerImpl(MasterInterface mi,
Reference<AsyncVar<ServerDBInfo> const> db,
Reference<AsyncVar<Optional<ClusterControllerFullInterface>> const> ccInterface,
@ -486,6 +658,7 @@ ACTOR Future<Void> masterServerImpl(MasterInterface mi,
wait(masterServerCxx(mi, db, ccInterface, coordinators, lifetime, forceRecovery));
return Void();
}
#endif
ACTOR Future<Void> masterServer(MasterInterface mi,
Reference<AsyncVar<ServerDBInfo> const> db,

View File

@ -0,0 +1,494 @@
import Flow
import flow_swift
@preconcurrency import FDBServer
import FDBClient
import fdbclient_swift
import CxxStdlib
func clamp(_ v: Version, lowerBound: Version, upperBound: Version) -> Version {
return max(min(v, upperBound), lowerBound)
}
@_expose(Cxx)
public func figureVersion(current: Version,
now: Double,
reference: Version,
toAdd: Int64,
maxVersionRateModifier: Double,
maxVersionRateOffset: Int64) -> Version {
// Versions should roughly follow wall-clock time, based on the
// system clock of the current machine and an FDB-specific epoch.
// Calculate the expected version and determine whether we need to
// hand out versions faster or slower to stay in sync with the
// clock.
let expected = Version(now * Double(getServerKnobs().VERSIONS_PER_SECOND)) - reference
// Attempt to jump directly to the expected version. But make
// sure that versions are still being handed out at a rate
// around VERSIONS_PER_SECOND. This rate is scaled depending on
// how far off the calculated version is from the expected
// version.
let maxOffset = min(Int64(Double(toAdd) * maxVersionRateModifier), maxVersionRateOffset)
return clamp(expected, lowerBound: current + toAdd - maxOffset,
upperBound: current + toAdd + maxOffset)
}
@_expose(Cxx)
public func updateLiveCommittedVersion(myself: MasterData, req: ReportRawCommittedVersionRequest) {
myself.minKnownCommittedVersion = max(myself.minKnownCommittedVersion, req.minKnownCommittedVersion)
if req.version > myself.liveCommittedVersion.get() {
if getServerKnobs().ENABLE_VERSION_VECTOR,
let writtenTags = Swift.Optional(cxxOptional: req.writtenTags) {
let primaryLocality = getServerKnobs().ENABLE_VERSION_VECTOR_HA_OPTIMIZATION ?
myself.locality : Int8(tagLocalityInvalid)
myself.ssVersionVector.setVersion(writtenTags, req.version, primaryLocality)
myself.versionVectorTagUpdates.addMeasurement(Double(writtenTags.size()))
}
let curTime = now()
// add debug here to change liveCommittedVersion to time bound of now()
debug_advanceVersionTimestamp(Int64(myself.liveCommittedVersion.get()), curTime + getClientKnobs().MAX_VERSION_CACHE_LAG)
// also add req.version but with no time bound
debug_advanceVersionTimestamp(Int64(req.version), Double.greatestFiniteMagnitude)
myself.databaseLocked = req.locked;
myself.proxyMetadataVersion = req.metadataVersion
// Note the set call switches context to any waiters on liveCommittedVersion before continuing.
myself.liveCommittedVersion.set(Int(req.version))
}
myself.reportLiveCommittedVersionRequests += 1
}
extension NotifiedVersionValue {
mutating func atLeast(_ limit: VersionMetricHandle.ValueType) async throws {
try await self.whenAtLeast(limit).value()
}
}
public class CommitProxyVersionReplies {
var replies: [UInt64: GetCommitVersionReply] = [:]
var latestRequestNum: NotifiedVersionValue
public init() {
latestRequestNum = NotifiedVersionValue(0)
}
}
@_expose(Cxx)
public actor MasterDataActor {
var lastCommitProxyVersionReplies: [Flow.UID: CommitProxyVersionReplies] = [:]
public init() {
}
func registerLastCommitProxyVersionReplies(uids: [Flow.UID]) async {
lastCommitProxyVersionReplies = [:]
for uid in uids {
lastCommitProxyVersionReplies[uid] = CommitProxyVersionReplies()
}
}
nonisolated public func registerLastCommitProxyVersionReplies(uids: [Flow.UID], result promise: PromiseVoid) {
Task {
await registerLastCommitProxyVersionReplies(uids: uids)
promise.send(Flow.Void())
}
}
nonisolated public func registerLastCommitProxyVersionReplies(uids: [Flow.UID]) -> FutureVoid {
let promise = PromiseVoid()
registerLastCommitProxyVersionReplies(uids: uids, result: promise)
return promise.getFuture()
}
func getVersion(myself: MasterData, req: GetCommitVersionRequest) async -> GetCommitVersionReply? {
myself.getCommitVersionRequests += 1
guard let lastVersionReplies = self.lastCommitProxyVersionReplies[req.requestingProxy] else {
// Request from invalid proxy (e.g. from duplicate recruitment request)
return nil
}
// CODE_PROBE(lastVersionReplies.latestRequestNum.get() < req.requestNum - 1, "Commit version request queued up")
try! await lastVersionReplies.latestRequestNum
.atLeast(VersionMetricHandle.ValueType(req.requestNum - UInt64(1)))
if let lastReply = lastVersionReplies.replies[req.requestNum] {
// NOTE: CODE_PROBE is macro, won't be imported
// CODE_PROBE(true, "Duplicate request for sequence")
return lastReply
} else if (req.requestNum <= lastVersionReplies.latestRequestNum.get()) {
// NOTE: CODE_PROBE is macro, won't be imported
// CODE_PROBE(true, "Old request for previously acknowledged sequence - may be impossible with current FlowTransport");
assert(req.requestNum < lastVersionReplies.latestRequestNum.get())
// The latest request can never be acknowledged
return nil
}
var rep = GetCommitVersionReply()
if (myself.version == invalidVersion) {
myself.lastVersionTime = now()
myself.version = myself.recoveryTransactionVersion
rep.prevVersion = myself.lastEpochEnd
} else {
var t1 = now()
if BUGGIFY() {
t1 = myself.lastVersionTime
}
let toAdd = max(Version(1), min(
getServerKnobs().MAX_READ_TRANSACTION_LIFE_VERSIONS,
Version(Double(getServerKnobs().VERSIONS_PER_SECOND) * (t1 - myself.lastVersionTime))))
rep.prevVersion = myself.version
if let referenceVersion = Swift.Optional(cxxOptional: myself.referenceVersion) {
myself.version = figureVersion(current: myself.version,
now: SwiftGNetwork.timer(),
reference: Version(referenceVersion),
toAdd: toAdd,
maxVersionRateModifier: getServerKnobs().MAX_VERSION_RATE_MODIFIER,
maxVersionRateOffset: getServerKnobs().MAX_VERSION_RATE_OFFSET)
assert(myself.version > rep.prevVersion)
} else {
myself.version += toAdd
}
// NOTE: CODE_PROBE is macro, won't be imported
// CODE_PROBE(self.version - rep.prevVersion == 1, "Minimum possible version gap");
let _ /*maxVersionGap*/ = myself.version - rep.prevVersion == getServerKnobs().MAX_READ_TRANSACTION_LIFE_VERSIONS
// CODE_PROBE(maxVersionGap, "Maximum possible version gap");
myself.lastVersionTime = t1
myself.getResolutionBalancer().setChangesInReply(req.requestingProxy, &rep)
}
rep.version = myself.version
rep.requestNum = req.requestNum
// TODO: highlight some collection use (c++ collection, find on it etc)
lastVersionReplies.replies = lastVersionReplies.replies.filter { $0.0 > req.mostRecentProcessedRequestNum }
lastVersionReplies.replies[req.requestNum] = rep
assert(rep.prevVersion >= 0)
assert(lastVersionReplies.latestRequestNum.get() == req.requestNum - 1)
lastVersionReplies.latestRequestNum.set(Int(req.requestNum))
return rep
}
/// Promise type must match result type of the target function.
/// If missing, please declare new `using PromiseXXX = Promise<XXX>;` in `swift_<MODULE>_future_support.h` files.
nonisolated public func getVersion(myself: MasterData, req: GetCommitVersionRequest,
result promise: PromiseVoid) {
Task {
if let rep = await getVersion(myself: myself, req: req) {
req.reply.send(rep)
} else {
req.reply.sendNever()
}
promise.send(Flow.Void())
}
}
nonisolated public func getVersion(myself: MasterData, req: GetCommitVersionRequest) -> FutureVoid {
let promise = PromiseVoid()
getVersion(myself: myself, req: req, result: promise)
return promise.getFuture()
}
// ACTOR Future<Void> waitForPrev(Reference<MasterData> self, ReportRawCommittedVersionRequest req) {
@discardableResult
func waitForPrev(myself: MasterData, req: ReportRawCommittedVersionRequest) async -> Flow.Void {
let startTime = now()
// TODO: typealias the optional
if let prevVersion = Swift.Optional(cxxOptional: req.prevVersion) {
try! await myself.liveCommittedVersion.atLeast(Int(prevVersion)) // TODO: try to not need the conversion here
}
let latency = now() - startTime
myself.waitForPrevLatencies.addMeasurement(latency)
myself.waitForPrevCommitRequests += 1
updateLiveCommittedVersion(myself: myself, req: req)
return Void()
}
nonisolated public func waitForPrev(myself: MasterData, req: ReportRawCommittedVersionRequest,
result promise: PromiseVoid) {
Task {
let rep = await waitForPrev(myself: myself, req: req)
req.reply.send(rep)
promise.send(rep)
}
}
nonisolated public func waitForPrev(myself: MasterData, req: ReportRawCommittedVersionRequest) -> FutureVoid {
let promise = PromiseVoid()
waitForPrev(myself: myself, req: req, result: promise)
return promise.getFuture()
}
@discardableResult
public func provideVersions(myself: MasterData) async throws -> Flow.Void {
// state ActorCollection versionActors(false);
//
// loop choose {
// when(GetCommitVersionRequest req = waitNext(myself.myInterface.getCommitVersion.getFuture())) {
// versionActors.add(getVersion(self, req));
// }
// when(wait(versionActors.getResult())) {}
// }
try await withThrowingTaskGroup(of: Swift.Void.self) { group in
// TODO(swift): the only throw here is from the Sequence, but can it actually ever happen...?
for try await req in myself.myInterface.getCommitVersion.getFuture() {
_ = group.addTaskUnlessCancelled {
if let rep = await self.getVersion(myself: myself, req: req) {
req.reply.send(rep)
} else {
req.reply.sendNever()
}
}
if Task.isCancelled {
return Flow.Void()
}
}
return Flow.Void()
}
}
nonisolated public func provideVersions(myself: MasterData, result promise: PromiseVoid) {
Task {
// TODO(swift): handle the error
let void = try await self.provideVersions(myself: myself)
promise.send(void)
}
}
nonisolated public func provideVersions(myself: MasterData) -> FutureVoid {
let promise = PromiseVoid()
provideVersions(myself: myself, result: promise)
return promise.getFuture()
}
public func getLiveCommittedVersion(myself: MasterData, _ req: GetRawCommittedVersionRequest) -> GetRawCommittedVersionReply {
if req.debugID.present() {
// TODO: trace event here
// g_traceBatch.addEvent("TransactionDebug",
// req.debugID.get().first(),
// "MasterServer.serveLiveCommittedVersion.GetRawCommittedVersion");
}
if myself.liveCommittedVersion.get() == invalidVersion {
myself.liveCommittedVersion.set(Int(myself.recoveryTransactionVersion));
}
myself.getLiveCommittedVersionRequests += 1
var reply = GetRawCommittedVersionReply()
let liveCommittedVersion: Int = myself.liveCommittedVersion.get()
reply.version = Version(liveCommittedVersion)
reply.locked = myself.databaseLocked
reply.metadataVersion = myself.proxyMetadataVersion
reply.minKnownCommittedVersion = myself.minKnownCommittedVersion
if (getServerKnobs().ENABLE_VERSION_VECTOR) {
myself.versionVectorSizeOnCVReply.addMeasurement(Double(reply.ssVersionVectorDelta.size()))
}
return reply
}
func reportLiveCommittedVersion(myself: MasterData, req: ReportRawCommittedVersionRequest) async -> Void {
if let prevVersion = Swift.Optional(cxxOptional: req.prevVersion),
getServerKnobs().ENABLE_VERSION_VECTOR &&
(myself.liveCommittedVersion.get() != invalidVersion) &&
(myself.liveCommittedVersion.get() < prevVersion) {
return await waitForPrev(myself: myself, req: req)
} else {
updateLiveCommittedVersion(myself: myself, req: req)
myself.nonWaitForPrevCommitRequests += 1
return Void()
}
}
@discardableResult
public func serveLiveCommittedVersion(myself: MasterData) async -> Flow.Void {
// TODO: use TaskPool
await withThrowingTaskGroup(of: Swift.Void.self) { group in
// getLiveCommittedVersion
group.addTask {
for try await req in myself.myInterface.getLiveCommittedVersion.getFuture() {
let rep = await self.getLiveCommittedVersion(myself: myself, req)
req.reply.send(rep)
}
}
// reportLiveCommittedVersion
group.addTask {
for try await req in myself.myInterface.reportLiveCommittedVersion.getFuture() {
let rep = await self.reportLiveCommittedVersion(myself: myself, req: req)
req.reply.send(rep)
}
}
}
return Flow.Void()
}
nonisolated public func serveLiveCommittedVersion(myself: MasterData, result promise: PromiseVoid) {
Task {
let void = await self.serveLiveCommittedVersion(myself: myself)
promise.send(void)
}
}
nonisolated public func serveLiveCommittedVersion(myself: MasterData) -> FutureVoid {
let promise = PromiseVoid()
serveLiveCommittedVersion(myself: myself, result: promise)
return promise.getFuture()
}
func updateRecoveryData(myself: MasterData, req: UpdateRecoveryDataRequest) async -> Flow.Void {
STraceEvent("UpdateRecoveryData", myself.dbgid)
.detail("ReceivedRecoveryTxnVersion", req.recoveryTransactionVersion)
.detail("ReceivedLastEpochEnd", req.lastEpochEnd)
.detail("CurrentRecoveryTxnVersion", myself.recoveryTransactionVersion)
.detail("CurrentLastEpochEnd", myself.lastEpochEnd)
.detail("NumCommitProxies", req.commitProxies.size())
.detail("VersionEpoch", req.versionEpoch)
.detail("PrimaryLocality", Int(req.primaryLocality)) // TODO: seems we have trouble with Int8->char mapping, so wrap in Int for now
myself.recoveryTransactionVersion = req.recoveryTransactionVersion
myself.lastEpochEnd = req.lastEpochEnd
if (req.commitProxies.size() > 0) {
var registeredUIDs = [UID]()
registeredUIDs.reserveCapacity(req.commitProxies.size())
for j in 0..<req.commitProxies.size() { // TODO(swift): can we make this be a Sequence?
registeredUIDs.append(req.commitProxies[j].id())
}
await self.registerLastCommitProxyVersionReplies(uids: registeredUIDs)
}
if let versionEpoch = Swift.Optional(cxxOptional: req.versionEpoch) {
myself.referenceVersion = OptionalVersion(versionEpoch)
} else if BUGGIFY() {
// Cannot use a positive version epoch in simulation because of the
// clock starting at 0. A positive version epoch would mean the initial
// cluster version was negative.
// TODO: Increase the size of this interval after fixing the issue
// with restoring ranges with large version gaps.
let v = swift_get_randomInt64(deterministicRandom(), Int64(-1e6), 0)
myself.referenceVersion = OptionalVersion(v)
}
myself.getResolutionBalancer().setCommitProxies(req.commitProxies)
myself.getResolutionBalancer().setResolvers(req.resolvers)
myself.locality = req.primaryLocality
return Void()
}
@discardableResult
public func serveUpdateRecoveryData(myself: MasterData) async throws -> Flow.Void {
try await withThrowingTaskGroup(of: Swift.Void.self) { group in
// Note: this is an example of one-by-one handling requests, notice the group.next() below.
for try await req in myself.myInterface.updateRecoveryData.getFuture() {
group.addTask {
let rep = await self.updateRecoveryData(myself: myself, req: req)
req.reply.send(rep)
}
try await group.next()
}
}
return Flow.Void()
}
nonisolated public func serveUpdateRecoveryData(myself: MasterData, result promise: PromiseVoid) {
Task {
// TODO(swift): handle the error
let void = try await self.serveUpdateRecoveryData(myself: myself)
promise.send(void)
}
}
nonisolated public func serveUpdateRecoveryData(myself: MasterData) -> FutureVoid {
let promise = PromiseVoid()
serveUpdateRecoveryData(myself: myself, result: promise)
return promise.getFuture()
}
}
extension MasterData {
var swiftActorImpl: MasterDataActor {
#if FDBSERVER_FORWARD_DECLARE_SWIFT_APIS
// During the generationg of the C++ header for this module, we do not
// yet have access to `getSwiftImpl` API.
return MasterDataActor()
#else
return self.getSwiftImpl()
#endif
}
}
@_expose(Cxx)
public func masterServerSwift(
mi: MasterInterface,
db: AsyncVar_ServerDBInfo,
ccInterface: AsyncVar_Optional_ClusterControllerFullInterface,
coordinators: ServerCoordinators,
lifetime: LifetimeToken,
forceRecovery: Bool,
masterData: MasterData,
promise: PromiseVoid) {
Task {
let myself = masterData.swiftActorImpl
do {
try await withThrowingTaskGroup(of: Swift.Void.self) { group in
group.addTask {
try await traceRole(Role.MASTER, mi.id()).value()
}
group.addTask {
try await myself.provideVersions(myself: masterData)
}
group.addTask {
await myself.serveLiveCommittedVersion(myself: masterData)
}
group.addTask {
try await myself.serveUpdateRecoveryData(myself: masterData)
}
// CODE_PROBE(!lifetime.isStillValid(db->get().masterLifetime, mi.id() == db->get().master.id()),
// "Master born doomed");
STraceEvent("MasterLifetime", masterData.dbgid)
.detail("LifetimeToken", lifetime.toString())
while true {
guard (try? await db.onChange().value()) != nil else {
return
}
guard lifetime.isStillValid(db.getCopy().masterLifetime, mi.id() == db.getCopy().master.id()) else {
// CODE_PROBE(true, "Master replaced, dying")
if BUGGIFY() {
try? await FlowClock.sleep(for: .seconds(5))
}
// throwing out of here, cancels all the other tasks in the group as well.
throw WorkerRemovedError()
}
}
}
} catch {
print("Failure: \(error)")
}
promise.send(Void())
}
}
struct WorkerRemovedError: Swift.Error {}

View File

@ -0,0 +1,87 @@
/*
* swift_test_streams.swift
*
* This source file is part of the FoundationDB open source project
*
* Copyright 2013-2022 Apple Inc. and the FoundationDB project authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
enum RainbowColor: String {
case black = "\u{001B}[0;30m"
case red = "\u{001B}[0;31m"
case green = "\u{001B}[0;32m"
case yellow = "\u{001B}[0;33m"
case blue = "\u{001B}[0;34m"
case magenta = "\u{001B}[0;35m"
case cyan = "\u{001B}[0;36m"
case white = "\u{001B}[0;37m"
case `default` = "\u{001B}[0;0m"
func name() -> String {
switch self {
case .black: return "Black"
case .red: return "Red"
case .green: return "Green"
case .yellow: return "Yellow"
case .blue: return "Blue"
case .magenta: return "Magenta"
case .cyan: return "Cyan"
case .white: return "White"
case .default: return "Default"
}
}
}
extension String {
var black: String {
self.colored(as: .black)
}
var red: String {
self.colored(as: .red)
}
var green: String {
self.colored(as: .green)
}
var yellow: String {
self.colored(as: .yellow)
}
var blue: String {
self.colored(as: .blue)
}
var magenta: String {
self.colored(as: .magenta)
}
var cyan: String {
self.colored(as: .cyan)
}
var white: String {
self.colored(as: .white)
}
var `default`: String {
self.colored(as: .default)
}
func colored(as color: RainbowColor) -> String {
"\(color.rawValue)\(self)\(RainbowColor.default.rawValue)"
}
}

View File

@ -0,0 +1,146 @@
/*
* SimpleSwiftTestSuite.swift
*
* This source file is part of the FoundationDB open source project
*
* Copyright 2013-2022 Apple Inc. and the FoundationDB project authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// ==== ---------------------------------------------------------------------------------------------------------------
protocol SimpleSwiftTestSuite {
init()
typealias TestsCases = [TestCase]
@TestCasesBuilder
var tests: [TestCase] { get }
}
extension SimpleSwiftTestSuite {
public static var key: String {
"\(Self.self)".split(separator: ".").last.map(String.init) ?? ""
}
}
@resultBuilder
struct TestCasesBuilder {
static func buildBlock(_ tests: TestCase...) -> [TestCase] {
return tests
}
}
struct TestCase {
var _testSuiteName: String = ""
let name: String
let file: String
let line: UInt
var block: () async throws -> ()
init(_ name: String, block: @escaping @Sendable () async throws -> (),
file: String = #fileID, line: UInt = #line) {
self.name = name
self.block = block
self.file = file
self.line = line
}
/// Run a specific unit-test.
public func run() async throws {
try await self.block()
}
}
final class SimpleSwiftTestRunner {
struct TestFilter {
private let matcher: String?
init(parse arguments: ArraySlice<String>) {
let optName = "--test-filter"
if let filterIdx = arguments.firstIndex(of: optName) {
let filterValueIdx = arguments.index(after: filterIdx)
if filterValueIdx >= arguments.endIndex {
fatalError("No value for `\(optName)` given! Arguments: \(arguments)")
}
self.matcher = arguments[filterValueIdx]
} else {
self.matcher = nil
}
}
public func matches(suiteName: String, testName: String?) -> Bool {
guard let matcher = self.matcher else {
return true // always match if no matcher
}
if suiteName.contains(matcher) {
return true
} else if let testName, testName.contains(matcher) {
return true
} else {
return false
}
}
}
func run() async throws {
let filter = TestFilter(parse: CommandLine.arguments.dropFirst())
for suite in SimpleSwiftTestSuites {
let tests = self.allTestsForSuite("\(suite)")
for (testName, testCase) in tests {
guard filter.matches(suiteName: "\(suite)", testName: testName) else {
print("[swift] [skip] \(suite).'\(testName)' @ \(testCase.file):\(testCase.line) -------------------------------------------".yellow)
continue
}
print("[swift] [test] \(suite).'\(testName)' @ \(testCase.file):\(testCase.line) -------------------------------------------".yellow)
do {
try await testCase.run()
print("[swift] [pass] Finished: \(suite).'\(testName)' ------------------------------------------------------------".green)
} catch {
print("[swift] [fail] Failed \(suite).'\(testName)' ------------------------------------------------------------".red)
}
}
}
}
func allTestsForSuite(_ testSuite: String) -> [(String, TestCase)] {
guard let suiteType = SimpleSwiftTestSuites.first(where: { testSuite == "\($0)" }) else {
return []
}
let suiteInstance = suiteType.init()
var tests: [(String, TestCase)] = []
for test in suiteInstance.tests {
tests.append((test.name, test))
}
return tests
}
func findTestCase(suite: String, testName: String) -> TestCase? {
allTestsForSuite(suite)
.first(where: { $0.0 == testName })?
.1
}
}

View File

@ -0,0 +1,253 @@
/*
* swift_test_streams.swift
*
* This source file is part of the FoundationDB open source project
*
* Copyright 2013-2022 Apple Inc. and the FoundationDB project authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import Flow
import flow_swift
struct StreamTests: SimpleSwiftTestSuite {
var tests: [TestCase] {
/// Corresponds to FlowTests.actor.cpp "/flow/flow/trivial promisestreams"
TestCase("PromiseStream: await stream.waitNext") {
var ps = PromiseStreamCInt()
var fs: FutureStreamCInt = ps.getFuture()
var i: CInt = 1
ps.send(i)
precondition(ps.getFuture().isReady())
precondition(fs.pop() == 1)
i += 1
ps.send(i)
let element = try? await fs.waitNext
precondition(element == 2)
}
TestCase("PromiseStream: as Swift AsyncSequence") {
var ps = PromiseStreamCInt()
let fs: FutureStreamCInt = ps.getFuture()
Task { [ps] in
var ps = ps
var i: CInt = 1
await sleepALittleBit()
ps.send(i)
i = 2
await sleepALittleBit()
ps.send(i)
i = 3
await sleepALittleBit()
ps.send(i)
ps.sendError(end_of_stream())
}
pprint("[stream] waiting async for loop on FutureStream")
var sum: CInt = 0
let expected: CInt = 1 + 2 + 3
for try await num in fs {
pprint("[stream] got num = \(num)")
sum += num
}
pprint("[stream] done")
precondition(sum == expected, "Expected \(expected) but got \(sum)")
}
/// This test showcases a semantic 1:1 equivalent of a "loop choose {}"
/// It should be more efficient since we're using child tasks inside a task group,
/// which can benefit from being allocated inside the parent task.
///
/// Equivalent to `tutorial.actor.cpp::someFuture(Future<int> ready)`
TestCase("1:1 simulate a 'loop choose {}' in Swift, with TaskGroup") {
// ACTOR Future<Void> someFuture(Future<int> ready) {
// loop choose {
// when(wait(delay(0.5))) { std::cout << "Still waiting...\n"; }
// when(int r = wait(ready)) {
// std::cout << format("Ready %d\n", r);
// wait(delay(double(r)));
// std::cout << "Done\n";
// return Void();
// }
// }
// }
let promise = PromiseVoid()
Task {
try? await FlowClock.sleep(for: .seconds(1))
pprint("[stream] Complete promise...")
promise.send(Void())
}
enum Action {
case wait
case ready(CInt)
case _ignore
var isWait: Bool {
switch self {
case .wait: return true
default: return false
}
}
var isReady: Bool {
switch self {
case .ready: return true
default: return false
}
}
}
let out = await withTaskGroup(of: Action.self) { group in
var lastCollected: Action? = nil
while true {
if lastCollected?.isWait ?? true {
group.addTask {
// When the group/task gets cancelled, the sleep will throw
// we ignore the throw though
try? await FlowClock.sleep(for: .seconds(1))
return .wait
}
}
if lastCollected?.isReady ?? true {
group.addTask {
try! await promise.getFuture().value()
return .ready(12)
}
}
lastCollected = await group.next()
switch lastCollected {
case .wait:
pprint("[stream] Still waiting...")
case .ready(let r):
pprint("[stream] Ready: \(r)")
group.cancelAll()
return r
default:
fatalError("should not happen, was: \(String(describing: lastCollected))")
}
}
}
// assert the value we got out of the group is what we sent into it
precondition(out == 12)
}
TestCase("Tasks consuming multiple streams, send requests into actor") {
let p = PromiseVoid()
actor Cook {
let p: PromiseVoid
var expected: Int
var completed: Int
init(p: PromiseVoid, expectedTasks expected: Int) {
self.p = p
self.expected = expected
self.completed = 0
}
func cook(task: CInt, delay: Duration) async throws -> String {
pprint("Inside Cook actor to handle: \(task)")
defer { pprint("Finished Cook actor to handle: \(task)") }
try await FlowClock.sleep(for: delay)
completed += 1
if completed >= expected {
p.send(Flow.Void())
}
return "done:\(task)"
}
}
let expectedTasks = 4
let cook = Cook(p: p, expectedTasks: expectedTasks)
let ps1 = PromiseStreamCInt()
let ps2 = PromiseStreamCInt()
// Spawn 2 tasks, one for consuming each of the streams.
// These tasks will keep looping on the future streams "forever" - until the tasks are cancelled.
let t1 = Task { [ps1] in
var ps1_var = ps1
let fs: FutureStreamCInt = ps1_var.getFuture()
for try await t in fs {
pprint("send Task to cook: \(t) (from ps1)")
Task {
pprint("Inside Task to cook: \(t) (from ps1)")
let res = try await cook.cook(task: t, delay: .milliseconds(500))
pprint("Inside Task to cook: \(t) (from ps1), returned: \(res)")
precondition(res == "done:\(t)")
}
}
}
let t2 = Task { [ps2] in
var ps2_var = ps2
let fs: FutureStreamCInt = ps2_var.getFuture()
for try await t in fs {
pprint("send Task to cook: \(t) (from ps2)")
Task {
pprint("Inside Task to cook: \(t) (from ps2)")
let res = try await cook.cook(task: t, delay: .milliseconds(500))
pprint("Inside Task to cook: \(t) (from ps1), returned: \(res)")
precondition(res == "done:\(t)")
}
}
}
// When we exit this scope, cancel all the tasks
// TODO: we need to implement swift cancellation causing interruption of consuming a stream
defer {
t1.cancel()
t2.cancel()
}
// Start another task that will feed events into the streams
await Task { [ps1, ps2] in
var ps1 = ps1
var ps2 = ps2
try! await FlowClock.sleep(for: .milliseconds(120))
ps1.sendCopy(1)
try! await FlowClock.sleep(for: .milliseconds(120))
ps2.sendCopy(10)
try! await FlowClock.sleep(for: .milliseconds(120))
ps1.sendCopy(2)
ps2.sendCopy(20)
}.value
try await p.getFuture().value()
pprint("All done")
}
}
}
fileprivate func sleepALittleBit() async {
try? await Task.sleep(until: .now.advanced(by: .milliseconds(200)), clock: .flow)
}

View File

@ -0,0 +1,103 @@
/*
* swift_test_streams.swift
*
* This source file is part of the FoundationDB open source project
*
* Copyright 2013-2022 Apple Inc. and the FoundationDB project authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import Flow
import flow_swift
struct TaskTests: SimpleSwiftTestSuite {
var tests: [TestCase] {
TestCase("await \(FutureVoid.self)") {
let p = PromiseVoid()
let voidF = p.getFuture()
p.send(Flow.Void())
_ = try await voidF.value()
}
TestCase("await \(FutureCInt.self)") {
let p = PromiseCInt()
let intF: FutureCInt = p.getFuture()
let value: CInt = 42
p.send(value)
let got = try await intF.value()
precondition(got == value, "\(got) did not equal \(value)")
}
TestCase("more Flow task await tests") {
let p: PromiseCInt = PromiseCInt()
let f: FutureCInt = p.getFuture()
pprint("got PromiseCInt")
precondition(!f.isReady(), "Future should not be ready yet")
let num: CInt = 1111
pprint("send \(num)")
p.send(num)
pprint("without wait, f.get(): \(f.__getUnsafe().pointee)")
pprint("wait...")
let value: CInt = try await f.value()
assertOnNet2EventLoop() // hopped back to the right executor, yay
precondition(f.isReady(), "Future should be ready by now")
pprint("await value = \(value)")
precondition((value) == num, "Value obtained from await did not match \(num), was: \(String(describing: value))!")
pprint("[swift][tid:\(_tid())][\(#fileID):\(#line)](\(#function)) future 2 --------------------")
let p2 = PromiseCInt()
let f2: FutureCInt = p2.getFuture()
let num2: CInt = 2222
Task { [num2] in
assertOnNet2EventLoop()
pprint("[swift][tid:\(_tid())][\(#fileID):\(#line)](\(#function)) future 2: send \(num2)")
p2.send(num2)
}
pprint("[swift][tid:\(_tid())][\(#fileID):\(#line)](\(#function)) future 2: waiting...")
let got2: CInt? = try? await f2.value()
pprint("[swift][tid:\(_tid())][\(#fileID):\(#line)](\(#function)) future 2, got: \(String(describing: got2))")
precondition(got2! == num2, "Value obtained from send after await did not match \(num2), was: \(String(describing: got2))!")
// assert that we hopped back and are again on the Net2 event loop thread:
assertOnNet2EventLoop()
}
TestCase("Flow task priorities in Task.priority") {
await Task { assertOnNet2EventLoop() }.value
assertOnNet2EventLoop()
// Note that we can assign Flow priorities to tasks explicitly:
pprint("Parent task priority: \(Task.currentPriority)")
pprint("Execute task with priority: \(_Concurrency.TaskPriority.Worker)")
precondition(_Concurrency.TaskPriority.Worker.rawValue == 60, "WAS: \(_Concurrency.TaskPriority.Worker.rawValue) wanted: 60")
await Task(priority: .Worker) {
pprint("Task executed, with priority: \(Task.currentPriority)")
precondition(Task.currentPriority == .Worker)
assertOnNet2EventLoop()
}.value
assertOnNet2EventLoop()
pprint("Properly resumed...")
}
}
}

View File

@ -0,0 +1,52 @@
/*
* swift_test_streams.swift
*
* This source file is part of the FoundationDB open source project
*
* Copyright 2013-2022 Apple Inc. and the FoundationDB project authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import Flow
import flow_swift
// Don't do this at home;
// We assume we run single-threadedly in Net2 in these tests,
// as such, we access this global variable without synchronization and it is safe.
var _mainThreadID = _tid()
let SimpleSwiftTestSuites: [any SimpleSwiftTestSuite.Type] = [
TaskTests.self,
StreamTests.self,
]
@_expose(Cxx)
public func swiftyTestRunner(p: PromiseVoid) {
Task {
do {
try await SimpleSwiftTestRunner().run()
} catch {
print("[swift] Test suite failed: \(error)".red)
}
p.send(Flow.Void())
}
}
// ==== ---------------------------------------------------------------------------------------------------------------
func assertOnNet2EventLoop() {
precondition(_tid() == _mainThreadID) // we're on the main thread, which the Net2 runloop runs on
}

View File

@ -0,0 +1,26 @@
import Flow
import FDBServer
import Cxx
#if NOTNEEDED
// FIXME(swift): remove this as the fixes for Map come in the new swift toolchain
extension Map_UID_CommitProxyVersionReplies.const_iterator: UnsafeCxxInputIterator {
public typealias Pointee = MAP_UInt64_GetCommitVersionReply_Iterator_Pointee
}
extension Map_UID_CommitProxyVersionReplies: CxxSequence {
public typealias RawIterator = Map_UID_CommitProxyVersionReplies.const_iterator
}
public func ==(lhs: Map_UID_CommitProxyVersionReplies.const_iterator,
rhs: Map_UID_CommitProxyVersionReplies.const_iterator) -> Bool {
true
}
extension MAP_UInt64_GetCommitVersionReply.const_iterator: UnsafeCxxInputIterator {}
extension MAP_UInt64_GetCommitVersionReply: CxxSequence {}
public func ==(lhs: MAP_UInt64_GetCommitVersionReply.const_iterator,
rhs: MAP_UInt64_GetCommitVersionReply.const_iterator) -> Bool {
true
}
#endif

View File

@ -0,0 +1,58 @@
/*
* swift_fdbserver_cxx_swift_value_conformance.swift
*
* This source file is part of the FoundationDB open source project
*
* Copyright 2013-2022 Apple Inc. and the FoundationDB project authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import FDBClient
import FDBServer
import flow_swift
@_expose(Cxx)
public struct ExposeTypeConf<T> {
let x: CInt
}
/*****************************************************************************/
/* Whenever you see a 'type cannot be used in a Swift generic context' error */
/* you need to expose the the type in question via an expose... function. */
/* */
/* These functions function ensures that the value witness table for `T` */
/* to C++ is exposed in the generated C++ header. */
/*****************************************************************************/
// TODO(swift): we should somehow simplify this process, so we don't have to expose manually.
// FIXME: return ExposeTypeConf? for conformance instead once header supports stdlib types.
@_expose(Cxx)
public func exposeConformanceToCxx_UpdateRecoveryDataRequest(
_: ExposeTypeConf<UpdateRecoveryDataRequest>) {}
// FIXME: return ExposeTypeConf? for conformance instead once header supports stdlib types.
@_expose(Cxx)
public func exposeConformanceToCxx_GetCommitVersionRequest(
_: ExposeTypeConf<GetCommitVersionRequest>) {}
// FIXME: return ExposeTypeConf? for conformance instead once header supports stdlib types.
@_expose(Cxx)
public func exposeConformanceToCxx_GetRawCommittedVersionRequest(
_: ExposeTypeConf<GetRawCommittedVersionRequest>) {}
// FIXME: return ExposeTypeConf? for conformance instead once header supports stdlib types.
@_expose(Cxx)
public func exposeConformanceToCxx_ReportRawCommittedVersionRequest(
_: ExposeTypeConf<ReportRawCommittedVersionRequest>) {}

View File

@ -0,0 +1,91 @@
/*
* swift_fdbserver_strem_support.swift
*
* This source file is part of the FoundationDB open source project
*
* Copyright 2013-2022 Apple Inc. and the FoundationDB project authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import Flow
import flow_swift
import FDBClient
import FDBServer
import Cxx
// ==== ----------------------------------------------------------------------------------------------------------------
// MARK: UpdateRecoveryDataRequest
//extension RequestStream_UpdateRecoveryDataRequest: _FlowStreamOps {
// public typealias Element = UpdateRecoveryDataRequest
// public typealias AsyncIterator = FutureStream_UpdateRecoveryDataRequest.AsyncIterator
// typealias SingleCB = FlowSingleCallbackForSwiftContinuation_UpdateRecoveryDataRequest
//}
extension FutureStream_UpdateRecoveryDataRequest: FlowStreamOps {
public typealias Element = UpdateRecoveryDataRequest
public typealias SingleCB = FlowSingleCallbackForSwiftContinuation_UpdateRecoveryDataRequest
public typealias AsyncIterator = FlowStreamOpsAsyncIteratorAsyncIterator<Self>
}
// This is a C++ type that we add the conformance to; so no easier way around it currently
extension FlowSingleCallbackForSwiftContinuation_UpdateRecoveryDataRequest:
FlowSingleCallbackForSwiftContinuationProtocol {
public typealias AssociatedFutureStream = FutureStream_UpdateRecoveryDataRequest
}
// ==== ----------------------------------------------------------------------------------------------------------------
// MARK: GetRawCommittedVersionRequest
extension FutureStream_GetRawCommittedVersionRequest: FlowStreamOps {
public typealias SelfStream = Self
public typealias Element = GetRawCommittedVersionRequest
public typealias SingleCB = FlowSingleCallbackForSwiftContinuation_GetRawCommittedVersionRequest
public typealias AsyncIterator = FlowStreamOpsAsyncIteratorAsyncIterator<Self>
}
// This is a C++ type that we add the conformance to; so no easier way around it currently
extension FlowSingleCallbackForSwiftContinuation_GetRawCommittedVersionRequest: FlowSingleCallbackForSwiftContinuationProtocol {
public typealias AssociatedFutureStream = FutureStream_GetRawCommittedVersionRequest
}
// ==== ----------------------------------------------------------------------------------------------------------------
// MARK: GetCommitVersionRequest
extension FutureStream_GetCommitVersionRequest: FlowStreamOps {
public typealias SelfStream = Self
public typealias Element = GetCommitVersionRequest
public typealias SingleCB = FlowSingleCallbackForSwiftContinuation_GetCommitVersionRequest
public typealias AsyncIterator = FlowStreamOpsAsyncIteratorAsyncIterator<Self>
}
// This is a C++ type that we add the conformance to; so no easier way around it currently
extension FlowSingleCallbackForSwiftContinuation_GetCommitVersionRequest: FlowSingleCallbackForSwiftContinuationProtocol {
public typealias AssociatedFutureStream = FutureStream_GetCommitVersionRequest
}
// ==== ----------------------------------------------------------------------------------------------------------------
// MARK: ReportRawCommittedVersionRequest
extension FutureStream_ReportRawCommittedVersionRequest: FlowStreamOps {
public typealias SelfStream = Self
public typealias Element = ReportRawCommittedVersionRequest
public typealias SingleCB = FlowSingleCallbackForSwiftContinuation_ReportRawCommittedVersionRequest
public typealias AsyncIterator = FlowStreamOpsAsyncIteratorAsyncIterator<Self>
}
// This is a C++ type that we add the conformance to; so no easier way around it currently
extension FlowSingleCallbackForSwiftContinuation_ReportRawCommittedVersionRequest: FlowSingleCallbackForSwiftContinuationProtocol {
public typealias AssociatedFutureStream = FutureStream_ReportRawCommittedVersionRequest
}

View File

@ -169,3 +169,104 @@ endif()
target_link_libraries(mkcert PUBLIC flow)
set(FLOW_BINARY_DIR "${CMAKE_BINARY_DIR}/flow")
if (WITH_SWIFT)
include(GenerateModulemap)
generate_modulemap("${CMAKE_BINARY_DIR}/flow/include" "Flow" flow
OMIT
sse2neon.h
ppc-asm.h
)
add_library(flow_swift STATIC
FlowCheckedContinuation.swift
stream_support.swift # general stream support types
flow_stream_support.swift # conformances for future types that we vend with this module (E.g. CInt, Void)
error_support.swift
task_priority_support.swift
SwiftBridging.swift
SwiftFileB.swift
future_support.swift # general support types
trace_support.swift # general support types
flow_future_support.swift # conformances for future types that we vend with this module (E.g. CInt, Void)
reply_support.swift
clock_support.swift
flow_optional_support.swift
)
target_include_directories(flow_swift PUBLIC
"${CMAKE_BINARY_DIR}/flow/include"
"${CMAKE_SOURCE_DIR}/flow/include"
"${CMAKE_BINARY_DIR}/flow/"
"${CMAKE_BINARY_DIR}/fdbclient/include"
"${CMAKE_BINARY_DIR}/fdbserver/include"
"${CMAKE_SOURCE_DIR}/fdbrpc/include"
"${CMAKE_BINARY_DIR}/fdbrpc/include"
"${CMAKE_SOURCE_DIR}/contrib/fmt-8.1.1/include"
"${CMAKE_SOURCE_DIR}/contrib/md5/include"
"${CMAKE_SOURCE_DIR}/contrib/libb64/include"
"${CMAKE_SOURCE_DIR}/contrib/sqlite"
"${Boost_DIR}/../../../include"
"${msgpack_DIR}/include"
)
include(FindSwiftLibs)
swift_get_linker_search_paths(SWIFT_LINK_PATHS)
target_link_directories(flow PUBLIC "${SWIFT_LINK_PATHS}")
# We need to make sure that Swift, and the concurrency library is linked
# in every module that uses flow, because we implement the Swift hooks in flow.
# TODO(swift): With upcoming CMake 3.26 we can get rid of this as it should realize
# that modules need Swift and use Swift for the linking which
# will do the right thing.
target_link_options(flow PUBLIC
"-lswiftCore"
"-lswift_Concurrency"
"-lswift_StringProcessing")
# Link with runtime initialization stub (not needed on Darwin).
if (NOT APPLE)
string(REPLACE " " ";" SWIFT_LINK_PATHS_LIST "${SWIFT_LINK_PATHS}")
list(GET SWIFT_LINK_PATHS_LIST -1 LastPath) # get the last element the list
target_link_options(flow PUBLIC "${LastPath}/swiftrt.o")
endif()
# TODO: the TBD validation skip is because of swift_job_run_generic, though it seems weird why we need to do that?
target_compile_options(flow_swift PRIVATE "$<$<COMPILE_LANGUAGE:Swift>:SHELL:-Xcc -std=c++20 -Xfrontend -validate-tbd-against-ir=none -Xcc -DNO_INTELLISENSE -Xcc -ivfsoverlay${CMAKE_BINARY_DIR}/flow/include/headeroverlay.yaml>")
# Ensure that C++ code in fdbserver can import Swift using a compatibility header.
include(SwiftToCXXInterop)
add_swift_to_cxx_header_gen_target(
flow_swift
flow_swift_checked_continuation_header
"${CMAKE_CURRENT_BINARY_DIR}/include/SwiftModules/Flow_CheckedContinuation.h"
SOURCES
"${CMAKE_CURRENT_SOURCE_DIR}/FlowCheckedContinuation.swift"
"${CMAKE_CURRENT_SOURCE_DIR}/flow_optional_support.swift"
FLAGS
-Xcc -std=c++20 -Xcc -DNO_INTELLISENSE -Xcc -ivfsoverlay${CMAKE_BINARY_DIR}/flow/include/headeroverlay.yaml
# Important: This is needed to avoid including headers that depends on this generated header.
-Xcc -DSWIFT_FUTURE_SUPPORT_H -Xcc -DSWIFT_STREAM_SUPPORT_H -Xcc -DSWIFT_HIDE_CHECKED_CONTINUTATION
)
add_swift_to_cxx_header_gen_target(
flow_swift
flow_swift_header
"${CMAKE_CURRENT_BINARY_DIR}/include/SwiftModules/Flow"
FLAGS
-Xcc -std=c++20 -Xcc -DNO_INTELLISENSE -Xcc -ivfsoverlay${CMAKE_BINARY_DIR}/flow/include/headeroverlay.yaml
)
add_dependencies(flow_swift_checked_continuation_header flow_actors boost_target ProtocolVersion)
add_dependencies(flow_swift_header flow_swift_checked_continuation_header)
add_dependencies(flow_swift flow_swift_header)
add_dependencies(flow_swift flow_actors)
add_dependencies(flow_swift boost_target)
# TODO(swift): rdar://99107402 - this will only work once CMake 3.25 is released:
# target_link_libraries(flow PRIVATE flow_swift)
add_dependencies(flow flow_swift)
add_dependencies(flow_sampling flow_swift)
endif() # WITH SWIFT

View File

@ -0,0 +1,56 @@
import Flow
@_expose(Cxx)
public struct ExposeVoidConf<T> {
let x: CInt
}
// FIXME: return Void? for conformance instead once header supports stdlib types.
@_expose(Cxx)
// This function ensures that the value witness table for `Void` to C++ is
// exposed in the generated C++ header.
public func _exposeVoidValueTypeConformanceToCpp(_ val: ExposeVoidConf<Void>) {
}
@_expose(Cxx)
public struct FlowCheckedContinuation<T> {
public typealias CC = CheckedContinuation<T, Swift.Error>
var cc: CC?
public init() {}
public init(_ cc: CC) {
self.cc = cc
}
public mutating func set(_ other: FlowCheckedContinuation<T>) {
// precondition: other continuation must be set.
assert(other.cc != nil)
cc = other.cc
}
public func resume(returning value: T) {
// precondition: continuation must be set.
assert(cc != nil)
cc!.resume(returning: value)
}
public func resumeThrowing(_ value: Flow.Error) {
// precondition: continuation must be set.
assert(cc != nil)
// TODO: map errors.
cc!.resume(throwing: GeneralFlowError(value))
}
}
public struct GeneralFlowError: Swift.Error {
let underlying: Flow.Error?
public init() {
self.underlying = nil
}
public init(_ underlying: Flow.Error) {
self.underlying = underlying
}
}

View File

@ -24,6 +24,8 @@
#include "flow/Arena.h"
#include "flow/Platform.h"
#include "flow/Trace.h"
#include "flow/swift.h"
#include "flow/swift_concurrency_hooks.h"
#include <algorithm>
#include <memory>
#include <string_view>
@ -171,6 +173,7 @@ public:
double timer_monotonic() override { return ::timer_monotonic(); };
Future<Void> delay(double seconds, TaskPriority taskId) override;
Future<Void> orderedDelay(double seconds, TaskPriority taskId) override;
void _swiftEnqueue(void* task) override;
Future<class Void> yield(TaskPriority taskID) override;
bool check_yield(TaskPriority taskId) override;
TaskPriority getCurrentTask() const override { return currentTaskID; }
@ -259,11 +262,21 @@ public:
struct PromiseTask final : public FastAllocated<PromiseTask> {
Promise<Void> promise;
swift::Job* _Nullable swiftJob = nullptr;
PromiseTask() {}
explicit PromiseTask(Promise<Void>&& promise) noexcept : promise(std::move(promise)) {}
explicit PromiseTask(swift::Job* swiftJob) : swiftJob(swiftJob) {}
void operator()() {
#ifdef WITH_SWIFT
if (auto job = swiftJob) {
swift_job_run(job, ExecutorRef::generic());
} else {
promise.send(Void());
}
#else
promise.send(Void());
#endif
delete this;
}
};
@ -1770,6 +1783,7 @@ Future<class Void> Net2::yield(TaskPriority taskID) {
return Void();
}
// TODO: can we wrap our swift task and insert it in here?
Future<Void> Net2::delay(double seconds, TaskPriority taskId) {
if (seconds >= 4e12) // Intervals that overflow an int64_t in microseconds (more than 100,000 years) are treated
// as infinite
@ -1790,6 +1804,15 @@ Future<Void> Net2::orderedDelay(double seconds, TaskPriority taskId) {
return delay(seconds, taskId);
}
void Net2::_swiftEnqueue(void* _job) {
#ifdef WITH_SWIFT
swift::Job* job = (swift::Job*)_job;
TaskPriority priority = swift_priority_to_net2(job->getPriority());
PromiseTask* t = new PromiseTask(job);
taskQueue.addReady(priority, t);
#endif
}
void Net2::onMainThread(Promise<Void>&& signal, TaskPriority taskID) {
if (stopped)
return;

View File

@ -3862,7 +3862,13 @@ void profileHandler(int sig) {
// We can only read the check thread time in a signal handler if the atomic is lock free.
// We can't get the time from a timer() call because it's not signal safe.
#ifndef WITH_SWIFT
ps->timestamp = checkThreadTime.is_lock_free() ? checkThreadTime.load() : 0;
#else
// FIXME: problem with Swift build: lib/libflow.a(Platform.actor.g.cpp.o):Platform.actor.g.cpp:function
// profileHandler(int): error: undefined reference to '__atomic_is_lock_free'
ps->timestamp = 0;
#endif /* WITH_SWIFT */
#if defined(USE_SANITIZER)
// In sanitizer builds the workaround implemented in SignalSafeUnwind.cpp is disabled

36
flow/SwiftBridging.swift Normal file
View File

@ -0,0 +1,36 @@
/*
* SwiftBridging.swift
*
* This source file is part of the FoundationDB open source project
*
* Copyright 2013-2022 Apple Inc. and the FoundationDB project authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import Flow
/// Swift equivalent for the BUGGIFY macro from flow.
public func BUGGIFY(file: @autoclosure () -> StaticString = #file,
line: @autoclosure () -> Int = #line) -> Bool {
let file_ = file()
assert(file_.hasPointerRepresentation)
return SwiftBridging.buggify(file_.utf8Start, CInt(line()))
}
public func pprint(_ message: String,
file: String = #fileID,
line: UInt = #line,
function: String = #function) {
print("[swift][\(file):\(line)](\(function)) \(message)")
}

5
flow/SwiftFileB.swift Normal file
View File

@ -0,0 +1,5 @@
import Flow
@_expose(Cxx)
public func swiftFileB() {
}

View File

@ -126,7 +126,7 @@ namespace actorcompiler
public int End { get { return endPos; } }
public Token First() {
if (beginPos == endPos) throw new InvalidOperationException("Empty TokenRange");
return tokens[beginPos];
return tokens[beginPos];
}
public Token Last() {
if (beginPos == endPos) throw new InvalidOperationException("Empty TokenRange");
@ -339,7 +339,7 @@ namespace actorcompiler
{
throw new Exception("Internal error: Invalid source line (0)");
}
if (tokens[i].Value == "ACTOR" || tokens[i].Value == "TEST_CASE")
if (tokens[i].Value == "ACTOR" || tokens[i].Value == "SWIFT_ACTOR" || tokens[i].Value == "TEST_CASE")
{
var actor = ParseActor(i, out int end);
if (classContextStack.Count > 0)
@ -457,9 +457,9 @@ namespace actorcompiler
}
void ParseDeclaration(TokenRange tokens,
out Token name,
out Token name,
out TokenRange type,
out TokenRange initializer,
out TokenRange initializer,
out bool constructorSyntax)
{
initializer = null;
@ -483,7 +483,7 @@ namespace actorcompiler
// type name(initializer);
constructorSyntax = true;
beforeInitializer = range(tokens.Begin, paren.Position);
initializer =
initializer =
range(paren.Position + 1, tokens.End)
.TakeWhile(t => t.ParenDepth > paren.ParenDepth);
} else {
@ -602,7 +602,7 @@ namespace actorcompiler
// Find the parameter list
TokenRange paramRange = toks.Last(NonWhitespace)
.Assert("Unexpected tokens after actor parameter list.",
.Assert("Unexpected tokens after actor parameter list.",
t => t.Value == ")" && t.ParenDepth == toks.First().ParenDepth)
.GetMatchingRangeIn(toks);
actor.parameters = SplitParameterList(paramRange, ",")
@ -660,7 +660,7 @@ namespace actorcompiler
LoopStatement ParseLoopStatement(TokenRange toks)
{
return new LoopStatement {
return new LoopStatement {
body = ParseCompoundStatement( toks.Consume("loop") )
};
}
@ -691,7 +691,7 @@ namespace actorcompiler
StateDeclarationStatement ParseStateDeclaration(TokenRange toks)
{
toks = toks.Consume("state").RevSkipWhile(t => t.Value == ";");
return new StateDeclarationStatement {
return new StateDeclarationStatement {
decl = ParseVarDeclaration(toks)
};
}
@ -756,7 +756,7 @@ namespace actorcompiler
if (initializer == null) throw new Error(ws.FirstSourceLine, "Wait statement must be a declaration or standalone statement");
var waitParams = initializer
.SkipWhile(Whitespace).Consume("Statement contains a wait, but is not a valid wait statement or a supported compound statement.1",
.SkipWhile(Whitespace).Consume("Statement contains a wait, but is not a valid wait statement or a supported compound statement.1",
t=> {
if (t.Value=="wait") return true;
if (t.Value=="waitNext") { ws.isWaitNext = true; return true; }
@ -787,13 +787,13 @@ namespace actorcompiler
Statement ParseForStatement(TokenRange toks)
{
var head =
var head =
toks.Consume("for")
.First(NonWhitespace)
.Assert("Expected (", t => t.Value == "(")
.GetMatchingRangeIn(toks);
Token[] delim =
Token[] delim =
head.Where(
t => t.ParenDepth == head.First().ParenDepth &&
t.BraceDepth == head.First().BraceDepth &&
@ -1002,7 +1002,7 @@ namespace actorcompiler
while (true)
{
Token delim = toks
.FirstOrDefault(
.FirstOrDefault(
t=> t.ParenDepth == toks.First().ParenDepth &&
t.BraceDepth == toks.First().BraceDepth &&
(t.Value==";" || t.Value == "}")
@ -1048,7 +1048,7 @@ namespace actorcompiler
actor.isForwardDeclaration = toSemicolon.Length < heading.Length;
if (actor.isForwardDeclaration) {
heading = toSemicolon;
if (head_token.Value == "ACTOR") {
if (head_token.Value == "ACTOR" || head_token.Value == "SWIFT_ACTOR") {
ParseActorHeading(actor, heading);
} else {
head_token.Assert("ACTOR expected!", t => false);
@ -1058,7 +1058,7 @@ namespace actorcompiler
var body = range(heading.End+1, tokens.Length)
.TakeWhile(t => t.BraceDepth > toks.First().BraceDepth);
if (head_token.Value == "ACTOR")
if (head_token.Value == "ACTOR" || head_token.Value == "SWIFT_ACTOR")
{
ParseActorHeading(actor, heading);
}

248
flow/clock_support.swift Normal file
View File

@ -0,0 +1,248 @@
/*
* clock_support.swift
*
* This source file is part of the FoundationDB open source project
*
* Copyright 2013-2022 Apple Inc. and the FoundationDB project authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import Flow
public struct FlowClock {
/// A flow point in time used for `FlowClock`.
public struct Instant: Codable, Sendable {
internal var _value: Swift.Duration
internal init(_value: Swift.Duration) {
self._value = _value
}
}
public init() {
}
}
extension Clock where Self == FlowClock {
/// A clock that measures time that always increments but does not stop
/// incrementing while the system is asleep.
///
/// try await Task.sleep(until: .now + .seconds(3), clock: .flow)
///
public static var flow: FlowClock {
return FlowClock()
}
}
extension FlowClock: Clock {
/// The current flow instant.
public var now: FlowClock.Instant {
FlowClock.now
}
/// The minimum non-zero resolution between any two calls to `now`.
public var minimumResolution: Swift.Duration {
let seconds = Int64(0)
var nanoseconds = Int64(0)
nanoseconds += Duration.milliseconds(100).nanoseconds
return .seconds(seconds) + .nanoseconds(nanoseconds)
}
/// The current flow instant.
public static var now: FlowClock.Instant {
var seconds = Int64(0)
var nanoseconds = Int64(0)
let nowDouble: Double = flow_gNetwork_now()
seconds += Duration.seconds(nowDouble).seconds
nanoseconds = Duration.seconds(nowDouble).nanoseconds
return FlowClock.Instant(_value: .seconds(seconds) + .nanoseconds(nanoseconds))
}
/// Suspend task execution until a given deadline within a tolerance.
/// If no tolerance is specified then the system may adjust the deadline
/// to coalesce CPU wake-ups to more efficiently process the wake-ups in
/// a more power efficient manner.
///
/// If the task is canceled before the time ends, this function throws
/// `CancellationError`.
///
/// This function doesn't block the underlying thread.
public func sleep(
until deadline: Instant,
tolerance: Swift.Duration? = nil
) async throws {
let (seconds, attoseconds) = deadline._value.components
_ = seconds // silence warning
let nanoseconds = attoseconds / 1_000_000_000
_ = nanoseconds // silence warning
let duration = FlowClock.now.duration(to: deadline)
let secondsDouble = Double(duration.seconds)
let nanosDouble = 0 // TODO: fix this handling of nanos from the deadline
_ = nanosDouble // silence warning
try await flow_gNetwork_delay(/*secondsDouble=*/secondsDouble, /*priority=*/TaskPriority.DefaultDelay).value()
}
public static func sleep(
for duration: Duration
) async throws {
try await Task.sleep(until: now + duration, clock: .flow)
}
}
extension FlowClock.Instant: InstantProtocol {
public static var now: FlowClock.Instant {
FlowClock.now
}
public func advanced(by duration: Swift.Duration) -> FlowClock.Instant {
return FlowClock.Instant(_value: _value + duration)
}
public func duration(to other: FlowClock.Instant) -> Swift.Duration {
other._value - _value
}
public func hash(into hasher: inout Hasher) {
hasher.combine(_value)
}
public static func ==(
_ lhs: FlowClock.Instant, _ rhs: FlowClock.Instant
) -> Bool {
return lhs._value == rhs._value
}
public static func <(
_ lhs: FlowClock.Instant, _ rhs: FlowClock.Instant
) -> Bool {
return lhs._value < rhs._value
}
@_alwaysEmitIntoClient
@inlinable
public static func +(
_ lhs: FlowClock.Instant, _ rhs: Swift.Duration
) -> FlowClock.Instant {
lhs.advanced(by: rhs)
}
@_alwaysEmitIntoClient
@inlinable
public static func +=(
_ lhs: inout FlowClock.Instant, _ rhs: Swift.Duration
) {
lhs = lhs.advanced(by: rhs)
}
@_alwaysEmitIntoClient
@inlinable
public static func -(
_ lhs: FlowClock.Instant, _ rhs: Swift.Duration
) -> FlowClock.Instant {
lhs.advanced(by: .zero - rhs)
}
@_alwaysEmitIntoClient
@inlinable
public static func -=(
_ lhs: inout FlowClock.Instant, _ rhs: Swift.Duration
) {
lhs = lhs.advanced(by: .zero - rhs)
}
@_alwaysEmitIntoClient
@inlinable
public static func -(
_ lhs: FlowClock.Instant, _ rhs: FlowClock.Instant
) -> Swift.Duration {
rhs.duration(to: lhs)
}
}
// ==== ----------------------------------------------------------------------------------------------------------------
// MARK: Duration conversion support
extension Swift.Duration {
public typealias Value = Int64
public var nanoseconds: Value {
let (seconds, attoseconds) = self.components
let sNanos = seconds * Value(1_000_000_000)
let asNanos = attoseconds / Value(1_000_000_000)
let (totalNanos, overflow) = sNanos.addingReportingOverflow(asNanos)
return overflow ? .max : totalNanos
}
/// The microseconds representation of the `TimeAmount`.
public var microseconds: Value {
self.nanoseconds / TimeUnit.microseconds.rawValue
}
/// The milliseconds representation of the `TimeAmount`.
public var milliseconds: Value {
self.nanoseconds / TimeUnit.milliseconds.rawValue
}
/// The seconds representation of the `TimeAmount`.
public var seconds: Value {
self.nanoseconds / TimeUnit.seconds.rawValue
}
public var isEffectivelyInfinite: Bool {
self.nanoseconds == .max
}
/// Represents number of nanoseconds within given time unit
public enum TimeUnit: Value {
case days = 86_400_000_000_000
case hours = 3_600_000_000_000
case minutes = 60_000_000_000
case seconds = 1_000_000_000
case milliseconds = 1_000_000
case microseconds = 1000
case nanoseconds = 1
public var abbreviated: String {
switch self {
case .nanoseconds: return "ns"
case .microseconds: return "μs"
case .milliseconds: return "ms"
case .seconds: return "s"
case .minutes: return "m"
case .hours: return "h"
case .days: return "d"
}
}
public func duration(_ duration: Int) -> Duration {
switch self {
case .nanoseconds: return .nanoseconds(Value(duration))
case .microseconds: return .microseconds(Value(duration))
case .milliseconds: return .milliseconds(Value(duration))
case .seconds: return .seconds(Value(duration))
case .minutes: return .seconds(Value(duration) * 60)
case .hours: return .seconds(Value(duration) * 60 * 60)
case .days: return .seconds(Value(duration) * 24 * 60 * 60)
}
}
}
}

27
flow/error_support.swift Normal file
View File

@ -0,0 +1,27 @@
/*
* clock_support.swift
*
* This source file is part of the FoundationDB open source project
*
* Copyright 2013-2022 Apple Inc. and the FoundationDB project authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import Flow
extension Flow.Error {
public var isEndOfStream: Bool {
self.code() == error_code_end_of_stream
}
}

View File

@ -0,0 +1,31 @@
import Flow
// ==== Future: Conformances ------------------------------------------------------------------------------------------
// ==== ----------------------------------------------------------------------------------------------------------------
// MARK: CInt
// This is a C++ type that we add the conformance to; so no easier way around it currently
//extension FlowCallbackForSwiftContinuationCInt: FlowCallbackForSwiftContinuationT {
//// public typealias AssociatedFuture = FutureCInt
//}
//extension FutureCInt: FlowFutureOps {
// public typealias Element = CInt
// public typealias FlowCallbackForSwiftContinuation = FlowCallbackForSwiftContinuationCInt
//}
// ==== ----------------------------------------------------------------------------------------------------------------
// MARK: Void
// This is a C++ type that we add the conformance to; so no easier way around it currently
//extension FlowCallbackForSwiftContinuationVoid: FlowCallbackForSwiftContinuationT {
//// public typealias AssociatedFuture = FutureVoid
//}
//extension FutureVoid: FlowFutureOps {
// public typealias Element = Void
// public typealias FlowCallbackForSwiftContinuation = FlowCallbackForSwiftContinuationVoid
//}
// ==== ---------------------------------------------------------------------------------- ==== //
// ==== Add further conformances here that should be shared for all Flow importing modules ==== //
// ==== ---------------------------------------------------------------------------------- ==== //

View File

@ -0,0 +1,22 @@
import Flow
// Generic interface for the Flow.Optional type.
public protocol FlowOptionalProtocol {
associatedtype Wrapped
func present() -> Bool
// FIXME: Avoid using __getUnsafe.
func __getUnsafe() -> UnsafePointer<Wrapped>
}
extension Swift.Optional {
/// Construct a Swift.Optional from a FlowOptional.
@inline(__always)
public init<OptT: FlowOptionalProtocol>(cxxOptional value: OptT) where OptT.Wrapped == Wrapped {
guard value.present() else {
self = nil
return
}
self = value.__getUnsafe().pointee
}
}

View File

@ -0,0 +1,57 @@
/*
* stream_support.swift
*
* This source file is part of the FoundationDB open source project
*
* Copyright 2013-2022 Apple Inc. and the FoundationDB project authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import Flow
// ==== FutureStream: Conformances -------------------------------------------------------------------------------------
// ==== ----------------------------------------------------------------------------------------------------------------
// MARK: CInt
extension FutureStreamCInt: FlowStreamOps {
public typealias Element = CInt
public typealias SingleCB = FlowSingleCallbackForSwiftContinuation_CInt
public typealias AsyncIterator = FlowStreamOpsAsyncIteratorAsyncIterator<Self>
}
// This is a C++ type that we add the conformance to; so no easier way around it currently
extension FlowSingleCallbackForSwiftContinuation_CInt:
FlowSingleCallbackForSwiftContinuationProtocol {
public typealias AssociatedFutureStream = FutureStreamCInt
}
//// ==== ----------------------------------------------------------------------------------------------------------------
//// MARK: Void
//
//extension FutureStreamCInt: FlowStreamOps {
// public typealias Element = Flow.Void
// public typealias SingleCB = FlowSingleCallbackForSwiftContinuation_Void
// public typealias AsyncIterator = FlowStreamOpsAsyncIteratorAsyncIterator<Self>
//}
//
//// This is a C++ type that we add the conformance to; so no easier way around it currently
//extension FlowSingleCallbackForSwiftContinuation_CInt:
// FlowSingleCallbackForSwiftContinuationProtocol {
// public typealias AssociatedFutureStream = FutureStreamCInt
//}
// ==== ---------------------------------------------------------------------------------- ==== //
// ==== Add further conformances here that should be shared for all Flow importing modules ==== //
// ==== ---------------------------------------------------------------------------------- ==== //

76
flow/future_support.swift Normal file
View File

@ -0,0 +1,76 @@
import Flow
// ==== ---------------------------------------------------------------------------------------------------------------
public protocol FlowCallbackForSwiftContinuationT {
associatedtype AssociatedFuture: FlowFutureOps
typealias Element = AssociatedFuture.Element
init()
mutating func set(_ continuationPointer: UnsafeRawPointer,
_ future: AssociatedFuture,
_ thisPointer: UnsafeRawPointer)
}
public protocol FlowFutureOps {
/// Element type of the future
associatedtype Element
associatedtype FlowCallbackForSwiftContinuation: FlowCallbackForSwiftContinuationT
func isReady() -> Bool
func isError() -> Bool
func canGet() -> Bool
func __getUnsafe() -> UnsafePointer<Element>
}
extension FlowFutureOps where Self == FlowCallbackForSwiftContinuation.AssociatedFuture {
/// Swift async method to make a Flow future awaitable.
public func value() async throws -> Element {
return try await self.waitValue
}
@inlinable
internal var waitValue: Element {
get async throws {
guard !self.isReady() else {
// FIXME(flow): handle isError and cancellation
if self.isError() {
// let error = self.__getErrorUnsafe() // TODO: rethrow the error?
throw GeneralFlowError()
} else {
// print("[swift][\(#fileID):\(#line)](\(#function)) future was ready, return immediately.")
// FIXME(swift): we'd need that technically to be:
// return get().pointee
precondition(self.canGet())
return self.__getUnsafe().pointee
}
}
var s = FlowCallbackForSwiftContinuation()
return try await withCheckedThrowingContinuation { cc in
withUnsafeMutablePointer(to: &s) { ptr in
let ecc = FlowCheckedContinuation<Element>(cc)
withUnsafePointer(to: ecc) { ccPtr in
ptr.pointee.set(UnsafeRawPointer(ccPtr), self, UnsafeRawPointer(ptr))
}
}
}
}
}
}
extension FlowFutureOps where Self == FlowCallbackForSwiftContinuation.AssociatedFuture,
Element == Flow.Void {
/// Swift async method to make a Flow future awaitable.
/// Specialized for Flow.Void making the returned result (Flow.Void) discardable.
///
/// The reason these are done as value() and not a computed property is to allow discardable value
/// on Flow.Void returning futures.
@discardableResult
public func value() async throws -> Element {
return try await self.waitValue
}
}

View File

@ -29,6 +29,7 @@
#include "flow/IRandom.h"
#include "flow/ObjectSerializerTraits.h"
#include "flow/FileIdentifier.h"
#include "flow/swift_support.h"
#include "flow/Optional.h"
#include "flow/Traceable.h"
#include <algorithm>

View File

@ -79,4 +79,33 @@ __GENERATE_BUGGIFY_VARIABLES(CLIENT, Client, client)
deterministicRandom()->random01() < (x))
#define CLIENT_BUGGIFY CLIENT_BUGGIFY_WITH_PROB(P_CLIENT_BUGGIFIED_SECTION_FIRES)
#endif // FLOW_BUGGIFY_H
namespace SwiftBridging {
inline std::map<std::pair<std::string, int>, bool> SwiftGeneralSBVar;
inline bool getGeneralSBVar(const char* file, const int line) {
const auto paired = std::make_pair(std::string(file), line);
if (SwiftGeneralSBVar.count(paired)) [[likely]] {
return SwiftGeneralSBVar[paired];
}
const double rand = deterministicRandom()->random01();
const bool activated = rand < P_GENERAL_BUGGIFIED_SECTION_ACTIVATED;
SwiftGeneralSBVar[paired] = activated;
g_traceBatch.addBuggify(activated, line, file);
if (g_network) [[likely]] {
g_traceBatch.dump();
}
return activated;
}
inline bool buggify(const char* _Nonnull filename, int line) {
// SEE: BUGGIFY_WITH_PROB and BUGGIFY macros above.
return isGeneralBuggifyEnabled() && getGeneralSBVar(filename, line) &&
deterministicRandom()->random01() < P_GENERAL_BUGGIFIED_SECTION_FIRES;
}
} // namespace SwiftBridging
#endif // FLOW_BUGGIFY_H

View File

@ -30,7 +30,13 @@
#include <random>
class DeterministicRandom final : public IRandom, public ReferenceCounted<DeterministicRandom> {
// FIXME: Remove once https://github.com/apple/swift/issues/61620 is fixed.
#define SWIFT_CXX_REF_DETERMINISTICRANDOM \
__attribute__((swift_attr("import_reference"))) __attribute__((swift_attr("retain:addref_DeterministicRandom"))) \
__attribute__((swift_attr("release:delref_DeterministicRandom")))
class SWIFT_CXX_REF_DETERMINISTICRANDOM DeterministicRandom final : public IRandom,
public ReferenceCounted<DeterministicRandom> {
private:
std::mt19937 random;
uint64_t next;
@ -55,4 +61,14 @@ public:
void delref() override;
};
// FIXME: Remove once https://github.com/apple/swift/issues/61620 is fixed.
inline void addref_DeterministicRandom(DeterministicRandom* ptr) {
addref(ptr);
}
// FIXME: Remove once https://github.com/apple/swift/issues/61620 is fixed.
inline void delref_DeterministicRandom(DeterministicRandom* ptr) {
delref(ptr);
}
#endif

View File

@ -26,6 +26,8 @@
#include <cstdint>
#include "flow/Traceable.h"
#include "swift_support.h"
// The thread safety this class provides is that it's safe to call addref and
// delref on the same object concurrently in different threads. Subclass does
// not get deleted until after all calls to delref complete.

View File

@ -214,4 +214,9 @@ Reference<IRandom> nondeterministicRandom();
// WARNING: This is not thread safe and must not be called from any other thread than the network thread!
Reference<IRandom> debugRandom();
// Workaround for https://github.com/apple/swift/issues/62354
inline int64_t swift_get_randomInt64(Reference<IRandom> random, int64_t min, int64_t maxPlusOne) {
return random->randomInt64(min, maxPlusOne);
}
#endif

View File

@ -26,6 +26,10 @@
#include "flow/Traceable.h"
#include "flow/FileIdentifier.h"
#include "flow/swift_support.h"
#ifdef WITH_SWIFT
#include <swift/bridging>
#endif
class Arena;
class Void;
@ -43,7 +47,11 @@ class Void;
// assertion failures are preferable. This is the main reason we
// don't intend to use std::optional directly.
template <class T>
class Optional : public ComposedIdentifier<T, 4> {
class
#ifdef WITH_SWIFT
SWIFT_CONFORMS_TO_PROTOCOL(flow_swift.FlowOptionalProtocol)
#endif
Optional : public ComposedIdentifier<T, 4> {
public:
using ValueType = T;
using Wrapped = T;

View File

@ -183,7 +183,7 @@ public:
// Load all specified certificates into memory, and return an object that
// allows access to them.
// If self has any certificates by path, they will be *asynchronously* loaded from disk.
Future<LoadedTLSConfig> loadAsync() const { return loadAsync(this); }
Future<LoadedTLSConfig> loadAsync() const { return loadAsync(this); } // FIXME: swift
// Return the explicitly set path.
// If one was not set, return the path from the environment.

View File

@ -265,7 +265,7 @@ inline constexpr AuditedEvent operator""_audit(const char* eventType, size_t len
// This class is not intended to be used directly. Instead, this type is returned from most calls on trace events
// (e.g. detail). This is done to disallow calling suppression functions anywhere but first in a chained sequence of
// trace event function calls.
struct BaseTraceEvent {
struct SWIFT_CXX_IMPORT_OWNED BaseTraceEvent {
BaseTraceEvent(BaseTraceEvent&& ev);
BaseTraceEvent& operator=(BaseTraceEvent&& ev);
@ -462,7 +462,7 @@ protected:
// The TraceEvent class provides the implementation for BaseTraceEvent. The only functions that should be implemented
// here are those that must be called first in a trace event call sequence, such as the suppression functions.
struct TraceEvent : public BaseTraceEvent {
struct SWIFT_CXX_IMPORT_OWNED TraceEvent : public BaseTraceEvent {
TraceEvent() {}
TraceEvent(const char* type, UID id = UID()); // Assumes SevInfo severity
TraceEvent(Severity, const char* type, UID id = UID());
@ -479,6 +479,16 @@ struct TraceEvent : public BaseTraceEvent {
BaseTraceEvent& sample(double sampleRate, bool logSampleRate = true);
BaseTraceEvent& suppressFor(double duration, bool logSuppressedEventCount = true);
// Exposed for Swift which cannot use std::enable_if
template <class T>
void addDetail(std::string key, const T& value) {
if (enabled && init()) {
auto s = Traceable<T>::toString(value);
addMetric(key.c_str(), value, s);
detailImpl(std::move(key), std::move(s), false);
}
}
};
class StringRef;

View File

@ -46,6 +46,7 @@ class FutureStream;
// These are for intellisense to do proper type inferring, etc. They are no included at build time.
#ifndef NO_INTELLISENSE
#define ACTOR
#define SWIFT_ACTOR
#define DESCR
#define state
#define UNCANCELLABLE

View File

@ -58,6 +58,19 @@
#include "flow/network.h"
#include "flow/serialize.h"
#ifdef WITH_SWIFT
#include <swift/bridging>
// Flow_CheckedContinuation.h depends on this header, so we first parse it
// without relying on any imported Swift types.
#ifndef SWIFT_HIDE_CHECKED_CONTINUTATION
#include "SwiftModules/Flow_CheckedContinuation.h"
#endif /* SWIFT_HIDE_CHECKED_CONTINUATION */
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wnullability-completeness"
#endif /* WITH_SWIFT */
#include "pthread.h"
#include <boost/version.hpp>
@ -67,6 +80,7 @@
static_assert(false, "TEST macros are deprecated, please use CODE_PROBE instead"); \
} while (false)
extern Optional<uint64_t> parse_with_suffix(std::string const& toparse, std::string const& default_unit = "");
extern Optional<uint64_t> parseDuration(std::string const& str, std::string const& defaultUnit = "");
extern std::string format(const char* form, ...);
@ -913,10 +927,75 @@ public:
template <class T>
class Promise;
#ifdef WITH_SWIFT
#ifndef SWIFT_HIDE_CHECKED_CONTINUTATION
using flow_swift::FlowCheckedContinuation;
template<class T>
class
#ifdef WITH_SWIFT
SWIFT_CONFORMS_TO_PROTOCOL(flow_swift.FlowCallbackForSwiftContinuationT)
#endif
FlowCallbackForSwiftContinuation : Callback<T> {
public:
using SwiftCC = flow_swift::FlowCheckedContinuation<T>;
using AssociatedFuture = Future<T>;
private:
SwiftCC continuationInstance;
public:
FlowCallbackForSwiftContinuation() : continuationInstance(SwiftCC::init()) {}
void set(const void* _Nonnull pointerToContinuationInstance, Future<T> f, const void* _Nonnull thisPointer) {
// Verify Swift did not make a copy of the `self` value for this method
// call.
assert(this == thisPointer);
// FIXME: Propagate `SwiftCC` to Swift using forward
// interop, without relying on passing it via a `void *`
// here. That will let us avoid this hack.
const void* _Nonnull opaqueStorage = pointerToContinuationInstance;
static_assert(sizeof(SwiftCC) == sizeof(const void*));
const SwiftCC ccCopy(*reinterpret_cast<const SwiftCC*>(&opaqueStorage));
// Set the continuation instance.
continuationInstance.set(ccCopy);
// Add this callback to the future.
f.addCallbackAndClear(this);
}
void fire(const T& value) override {
Callback<T>::remove();
Callback<T>::next = nullptr;
continuationInstance.resume(value);
}
void error(Error error) override {
Callback<T>::remove();
Callback<T>::next = nullptr;
continuationInstance.resumeThrowing(error);
}
void unwait() override {
// TODO(swift): implement
}
};
#endif /* SWIFT_HIDE_CHECKED_CONTINUATION */
#endif /* WITH_SWIFT*/
template <class T>
class Future {
class SWIFT_SENDABLE
#ifndef SWIFT_HIDE_CHECKED_CONTINUTATION
#ifdef WITH_SWIFT
SWIFT_CONFORMS_TO_PROTOCOL(flow_swift.FlowFutureOps)
#endif
#endif
Future {
public:
using Element = T;
#ifdef WITH_SWIFT
#ifndef SWIFT_HIDE_CHECKED_CONTINUTATION
using FlowCallbackForSwiftContinuation = FlowCallbackForSwiftContinuation<T>;
#endif
#endif /* WITH_SWIFT */
T const& get() const { return sav->get(); }
T getValue() const { return get(); }
@ -974,12 +1053,12 @@ public:
sav->cancel();
}
void addCallbackAndClear(Callback<T>* cb) {
void addCallbackAndClear(Callback<T>* _Nonnull cb) {
sav->addCallbackAndDelFutureRef(cb);
sav = nullptr;
}
void addYieldedCallbackAndClear(Callback<T>* cb) {
void addYieldedCallbackAndClear(Callback<T>* _Nonnull cb) {
sav->addYieldedCallbackAndDelFutureRef(cb);
sav = nullptr;
}
@ -1009,7 +1088,7 @@ private:
// Future<T> result = wait(x); // This is legal if wait() generates Futures, but it's probably wrong. It's a compilation
// error if wait() generates StrictFutures.
template <class T>
class StrictFuture : public Future<T> {
class SWIFT_SENDABLE StrictFuture : public Future<T> {
public:
inline StrictFuture(Future<T> const& f) : Future<T>(f) {}
inline StrictFuture(Never n) : Future<T>(n) {}
@ -1020,19 +1099,23 @@ private:
};
template <class T>
class Promise final {
class SWIFT_SENDABLE Promise final {
public:
template <class U>
void send(U&& value) const {
sav->send(std::forward<U>(value));
}
// Swift can't call method that takes in a universal references (U&&),
// so provide a callable `send` method that copies the value.
void sendCopy(const T& valueCopy) const SWIFT_NAME(send(_:)) { sav->send(valueCopy); }
template <class E>
void sendError(const E& exc) const {
sav->sendError(exc);
}
Future<T> getFuture() const {
SWIFT_CXX_IMPORT_UNSAFE Future<T> getFuture() const {
sav->addFutureRef();
return Future<T>(sav);
}
@ -1085,7 +1168,11 @@ private:
};
template <class T>
struct NotifiedQueue : private SingleCallback<T>, FastAllocated<NotifiedQueue<T>> {
struct NotifiedQueue : private SingleCallback<T>
#ifndef WITH_SWIFT
, FastAllocated<NotifiedQueue<T>> // FIXME(swift): Swift can't deal with this type yet
#endif /* WITH_SWIFT */
{
int promises; // one for each promise (and one for an active actor if this is an actor)
int futures; // one for each future and one more if there are any callbacks
@ -1210,7 +1297,7 @@ protected:
};
template <class T>
class FutureStream {
class SWIFT_SENDABLE FutureStream {
public:
bool isValid() const { return queue != nullptr; }
bool isReady() const { return queue->isReady(); }
@ -1247,7 +1334,8 @@ public:
bool operator==(const FutureStream& rhs) { return rhs.queue == queue; }
bool operator!=(const FutureStream& rhs) { return rhs.queue != queue; }
T pop() { return queue->pop(); }
// FIXME: remove annotation after https://github.com/apple/swift/issues/64316 is fixed.
T pop() __attribute__((swift_attr("import_unsafe"))) { return queue->pop(); }
Error getError() const {
ASSERT(queue->isError());
return queue->error;
@ -1290,12 +1378,13 @@ struct ReplyType<ReplyPromise<T>> {
#endif
template <class T>
class PromiseStream {
class SWIFT_SENDABLE PromiseStream {
public:
// stream.send( request )
// Unreliable at most once delivery: Delivers request unless there is a connection failure (zero or one times)
void send(const T& value) { queue->send(value); }
void sendCopy(T value) { queue->send(value); }
void send(T&& value) { queue->send(std::move(value)); }
void sendError(const Error& error) { queue->sendError(error); }
@ -1329,7 +1418,7 @@ public:
// Not const, because this function gives mutable
// access to queue
FutureStream<T> getFuture() {
SWIFT_CXX_IMPORT_UNSAFE FutureStream<T> getFuture() {
queue->addFutureRef();
return FutureStream<T>(queue);
}
@ -1481,6 +1570,9 @@ inline Future<Void> delay(double seconds, TaskPriority taskID = TaskPriority::De
inline Future<Void> orderedDelay(double seconds, TaskPriority taskID = TaskPriority::DefaultDelay) {
return g_network->orderedDelay(seconds, taskID);
}
inline void _swiftEnqueue(void* task) {
return g_network->_swiftEnqueue(task);
}
inline Future<Void> delayUntil(double time, TaskPriority taskID = TaskPriority::DefaultDelay) {
return g_network->delay(std::max(0.0, time - g_network->now()), taskID);
}
@ -1498,6 +1590,10 @@ inline bool check_yield(TaskPriority taskID = TaskPriority::DefaultYield) {
void bindDeterministicRandomToOpenssl();
#ifdef WITH_SWIFT
#pragma clang diagnostic pop
#endif
#include "flow/Coroutines.h"
#include "flow/genericactors.actor.h"
#endif

View File

@ -26,6 +26,7 @@
#include "flow/FastRef.h"
#include "flow/TaskPriority.h"
#include "flow/network.h"
#include "flow/swift_support.h"
#include <utility>
#include <functional>
#include <unordered_set>
@ -752,8 +753,17 @@ private:
V value;
};
// FIXME(swift): Remove once https://github.com/apple/swift/issues/61620 is fixed.
#define SWIFT_CXX_REF_ASYNCVAR \
__attribute__((swift_attr("import_reference"))) __attribute__((swift_attr("retain:immortal"))) \
__attribute__((swift_attr("release:immortal")))
// // TODO(swift): https://github.com/apple/swift/issues/62456 can't support retain/release funcs that are templates
// themselves
// __attribute__((swift_attr("retain:addref_AsyncVar"))) \
// __attribute__((swift_attr("release:delref_AsyncVar")))
template <class V>
class AsyncVar : NonCopyable, public ReferenceCounted<AsyncVar<V>> {
class SWIFT_CXX_REF_ASYNCVAR AsyncVar : NonCopyable, public ReferenceCounted<AsyncVar<V>> {
public:
AsyncVar() : value() {}
AsyncVar(V const& v) : value(v) {}
@ -764,7 +774,7 @@ public:
}
V const& get() const { return value; }
V getCopy() { return value; }
V getCopy() const __attribute__((swift_attr("import_unsafe"))) { return value; }
Future<Void> onChange() const { return nextChange.getFuture(); }
void set(V const& v) {
if (v != value)

View File

@ -23,6 +23,7 @@
#pragma once
#include "flow/ProtocolVersion.h"
#include "flow/swift.h"
#include "flow/NetworkAddress.h"
#include "flow/IPAddress.h"
@ -130,12 +131,15 @@ typedef NetworkAddressList (*NetworkAddressesFuncPtr)();
class TLSConfig;
class INetwork;
class SWIFT_CXX_IMMORTAL_SINGLETON_TYPE INetwork;
extern INetwork* g_network;
extern INetwork* newNet2(const TLSConfig& tlsConfig, bool useThreadPool = false, bool useMetrics = false);
inline INetwork* _swift_newNet2(const TLSConfig* tlsConfig, bool useThreadPool = false, bool useMetrics = false) {
return newNet2(*tlsConfig, useThreadPool, useMetrics);
}
class INetwork {
class SWIFT_CXX_IMMORTAL_SINGLETON_TYPE INetwork {
public:
// This interface abstracts the physical or simulated network, event loop and hardware that FoundationDB is running
// on. Note that there are tools for disk access, scheduling, etc as well as networking, and that almost all access
@ -186,6 +190,8 @@ public:
virtual double timer_monotonic() = 0;
// Similar to timer, but monotonic
virtual void _swiftEnqueue(void* task) = 0;
virtual Future<class Void> delay(double seconds, TaskPriority taskID) = 0;
// The given future will be set after seconds have elapsed
@ -284,4 +290,11 @@ protected:
~INetwork() {} // Please don't try to delete through this interface!
};
/// A wrapper for `g_network` value that lets you access global properties from Swift.
namespace SwiftGNetwork {
inline double timer() {
return g_network->timer();
}
} // namespace SwiftGNetwork
#endif

62
flow/include/flow/swift.h Normal file
View File

@ -0,0 +1,62 @@
/*
* network.h
*
* This source file is part of the FoundationDB open source project
*
* Copyright 2013-2022 Apple Inc. and the FoundationDB project authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef FLOW_SWIFT_H
#define FLOW_SWIFT_H
#include "swift_support.h"
#include "swift/ABI/Task.h"
#include "flow/ProtocolVersion.h"
#pragma once
#include <array>
#include <variant>
#include <atomic>
// ==== ----------------------------------------------------------------------------------------------------------------
inline pthread_t _tid() {
return pthread_self();
}
// ==== ----------------------------------------------------------------------------------------------------------------
double flow_gNetwork_now();
enum class TaskPriority;
Future<class Void> flow_gNetwork_delay(double seconds, TaskPriority taskID);
// ==== ----------------------------------------------------------------------------------------------------------------
/// A count in nanoseconds.
using JobDelay = unsigned long long;
class ExecutorRef {
public:
void* Identity;
uintptr_t Implementation;
constexpr ExecutorRef(void* identity, uintptr_t implementation)
: Identity(identity), Implementation(implementation) {}
constexpr static ExecutorRef generic() { return ExecutorRef(nullptr, 0); }
};
#endif

View File

@ -0,0 +1,192 @@
//===--- MetadataValues.h - Compiler/runtime ABI Metadata -------*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 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
//
//===----------------------------------------------------------------------===//
//
// This header is shared between the runtime and the compiler and
// includes target-independent information which can be usefully shared
// between them.
//
//===----------------------------------------------------------------------===//
#ifndef SWIFT_ABI_METADATAVALUES_H
#define SWIFT_ABI_METADATAVALUES_H
#include "../../swift/Basic/FlagSet.h"
#include <stdlib.h>
#include <stdint.h>
namespace swift {
enum {
/// The number of words (pointers) in a value buffer.
NumWords_ValueBuffer = 3,
/// The number of words in a metadata completion context.
NumWords_MetadataCompletionContext = 4,
/// The number of words in a yield-once coroutine buffer.
NumWords_YieldOnceBuffer = 4,
/// The number of words in a yield-many coroutine buffer.
NumWords_YieldManyBuffer = 8,
/// The number of words (in addition to the heap-object header)
/// in a default actor.
NumWords_DefaultActor = 12,
/// The number of words in a task.
NumWords_AsyncTask = 24,
/// The number of words in a task group.
NumWords_TaskGroup = 32,
/// The number of words in an AsyncLet (flags + child task context & allocation)
NumWords_AsyncLet = 80, // 640 bytes ought to be enough for anyone
/// The size of a unique hash.
NumBytes_UniqueHash = 16,
/// The maximum number of generic parameters that can be
/// implicitly declared, for generic signatures that support that.
MaxNumImplicitGenericParamDescriptors = 64,
};
struct InProcess;
template <typename Runtime>
struct TargetMetadata;
using Metadata = TargetMetadata<InProcess>;
/// Kinds of schedulable job.s
enum class JobKind : size_t {
// There are 256 possible job kinds.
/// An AsyncTask.
Task = 0,
/// Job kinds >= 192 are private to the implementation.
First_Reserved = 192,
DefaultActorInline = First_Reserved,
DefaultActorSeparate,
DefaultActorOverride,
NullaryContinuation
};
/// The priority of a job. Higher priorities are larger values.
enum class JobPriority : size_t {
// This is modelled off of Dispatch.QoS, and the values are directly
// stolen from there.
UserInteractive = 0x21, /* UI */
UserInitiated = 0x19, /* IN */
Default = 0x15, /* DEF */
Utility = 0x11, /* UT */
Background = 0x09, /* BG */
Unspecified = 0x00, /* UN */
};
/// A tri-valued comparator which orders higher priorities first.
inline int descendingPriorityOrder(JobPriority lhs, JobPriority rhs) {
return (lhs == rhs ? 0 : lhs > rhs ? -1 : 1);
}
/// Flags for schedulable jobs.
class JobFlags : public FlagSet<uint32_t> {
public:
enum {
Kind = 0,
Kind_width = 8,
Priority = 8,
Priority_width = 8,
// 8 bits reserved for more generic job flags.
// Kind-specific flags.
Task_IsChildTask = 24,
Task_IsFuture = 25,
Task_IsGroupChildTask = 26,
// 27 is currently unused
Task_IsAsyncLetTask = 28,
};
explicit JobFlags(uint32_t bits) : FlagSet(bits) {}
JobFlags(JobKind kind) { setKind(kind); }
JobFlags(JobKind kind, JobPriority priority) {
setKind(kind);
setPriority(priority);
}
constexpr JobFlags() {}
FLAGSET_DEFINE_FIELD_ACCESSORS(Kind, Kind_width, JobKind, getKind, setKind)
FLAGSET_DEFINE_FIELD_ACCESSORS(Priority, Priority_width, JobPriority, getPriority, setPriority)
bool isAsyncTask() const { return getKind() == JobKind::Task; }
FLAGSET_DEFINE_FLAG_ACCESSORS(Task_IsChildTask, task_isChildTask, task_setIsChildTask)
FLAGSET_DEFINE_FLAG_ACCESSORS(Task_IsFuture, task_isFuture, task_setIsFuture)
FLAGSET_DEFINE_FLAG_ACCESSORS(Task_IsGroupChildTask, task_isGroupChildTask, task_setIsGroupChildTask)
FLAGSET_DEFINE_FLAG_ACCESSORS(Task_IsAsyncLetTask, task_isAsyncLetTask, task_setIsAsyncLetTask)
};
/// Kinds of option records that can be passed to creating asynchronous tasks.
enum class TaskOptionRecordKind : uint8_t {
/// Request a task to be kicked off, or resumed, on a specific executor.
Executor = 0,
/// Request a child task to be part of a specific task group.
TaskGroup = 1,
/// DEPRECATED. AsyncLetWithBuffer is used instead.
/// Request a child task for an 'async let'.
AsyncLet = 2,
/// Request a child task for an 'async let'.
AsyncLetWithBuffer = 3,
/// Request a child task for swift_task_run_inline.
RunInline = UINT8_MAX,
};
/// Status values for a continuation. Note that the "not yet"s in
/// the description below aren't quite right because the system
/// does not actually promise to update the status before scheduling
/// the task. This is because the continuation context is immediately
/// invalidated once the task starts running again, so the window in
/// which we can usefully protect against (say) double-resumption may
/// be very small.
enum class ContinuationStatus : size_t {
/// The continuation has not yet been awaited or resumed.
Pending = 0,
/// The continuation has already been awaited, but not yet resumed.
Awaited = 1,
/// The continuation has already been resumed, but not yet awaited.
Resumed = 2
};
/// Flags that go in a TargetAccessibleFunction structure.
class AccessibleFunctionFlags : public FlagSet<uint32_t> {
public:
enum {
/// Whether this is a "distributed" actor function.
Distributed = 0,
};
explicit AccessibleFunctionFlags(uint32_t bits) : FlagSet(bits) {}
constexpr AccessibleFunctionFlags() {}
/// Whether the this is a "distributed" actor function.
FLAGSET_DEFINE_FLAG_ACCESSORS(Distributed, isDistributed, setDistributed)
};
} // end namespace swift
#endif // SWIFT_ABI_METADATAVALUES_H

View File

@ -0,0 +1,81 @@
//===--- Task.h - ABI structures for asynchronous tasks ---------*- 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 tasks.
//
//===----------------------------------------------------------------------===//
#ifndef SWIFT_ABI_TASK_H
#define SWIFT_ABI_TASK_H
#include "MetadataValues.h"
namespace swift {
enum {
NumWords_HeapObject = 2,
};
/// A schedulable job.
class alignas(2 * alignof(void*)) Job
// // We don't need the detailed definition of HeapObject, and instead just use __HeapObjectReserved to get the right
// // layout. Pulling in a full definition of HeapObject sadly takes with it a lot of other runtime,
// // so until we have to, let's avoid doing so
// : public HeapObject
{
public:
// Fake fields, pretending the storage of a HeapObject
void* __HeapObjectPrivate[NumWords_HeapObject];
// Indices into SchedulerPrivate, for use by the runtime.
enum {
/// The next waiting task link, an AsyncTask that is waiting on a future.
NextWaitingTaskIndex = 0,
// The Dispatch object header is one pointer and two ints, which is
// equivalent to three pointers on 32-bit and two pointers 64-bit. Set the
// indexes accordingly so that DispatchLinkageIndex points to where Dispatch
// expects.
DispatchHasLongObjectHeader = sizeof(void*) == sizeof(int),
/// An opaque field used by Dispatch when enqueueing Jobs directly.
DispatchLinkageIndex = DispatchHasLongObjectHeader ? 1 : 0,
/// The dispatch queue being used when enqueueing a Job directly with
/// Dispatch.
DispatchQueueIndex = DispatchHasLongObjectHeader ? 0 : 1,
};
// Reserved for the use of the scheduler.
void* SchedulerPrivate[2];
JobFlags Flags;
// Derived classes can use this to store a Job Id.
uint32_t Id = 0;
/// The voucher associated with the job. Note: this is currently unused on
/// non-Darwin platforms, with stub implementations of the functions for
/// consistency.
void* Voucher = nullptr; // voucher_t Voucher = nullptr;
/// Reserved for future use.
void* Reserved = nullptr;
bool isAsyncTask() const { return Flags.isAsyncTask(); }
JobPriority getPriority() const { return Flags.getPriority(); }
};
} // end namespace swift
#endif

View File

@ -0,0 +1,107 @@
//===--- FlagSet.h - Helper class for opaque flag types ---------*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 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
//
//===----------------------------------------------------------------------===//
//
// This file defines the FlagSet template, a class which makes it easier to
// define opaque flag types.
//
//===----------------------------------------------------------------------===//
#ifndef SWIFT_BASIC_FLAGSET_H
#define SWIFT_BASIC_FLAGSET_H
#include <type_traits>
#include <assert.h>
namespace swift {
/// A template designed to simplify the task of defining a wrapper type
/// for a flags bitfield.
///
/// Unfortunately, this doesn't currently support functional-style
/// building patterns, which means this can't practically be used for
/// types that need to be used in constant expressions.
template <typename IntType>
class FlagSet {
static_assert(std::is_integral<IntType>::value, "storage type for FlagSet must be an integral type");
IntType Bits;
protected:
template <unsigned BitWidth>
static constexpr IntType lowMaskFor() {
return IntType((1 << BitWidth) - 1);
}
template <unsigned FirstBit, unsigned BitWidth = 1>
static constexpr IntType maskFor() {
return lowMaskFor<BitWidth>() << FirstBit;
}
constexpr FlagSet(IntType bits = 0) : Bits(bits) {}
/// Read a single-bit flag.
template <unsigned Bit>
bool getFlag() const {
return Bits & maskFor<Bit>();
}
/// Set a single-bit flag.
template <unsigned Bit>
void setFlag(bool value) {
if (value) {
Bits |= maskFor<Bit>();
} else {
Bits &= ~maskFor<Bit>();
}
}
/// Read a multi-bit field.
template <unsigned FirstBit, unsigned BitWidth, typename FieldType = IntType>
FieldType getField() const {
return FieldType((Bits >> FirstBit) & lowMaskFor<BitWidth>());
}
/// Assign to a multi-bit field.
template <unsigned FirstBit, unsigned BitWidth, typename FieldType = IntType>
void setField(typename std::enable_if<true, FieldType>::type value) {
// Note that we suppress template argument deduction for FieldType.
assert(IntType(value) <= lowMaskFor<BitWidth>() && "value out of range");
Bits = (Bits & ~maskFor<FirstBit, BitWidth>()) | (IntType(value) << FirstBit);
}
// A convenient macro for defining a getter and setter for a flag.
// Intended to be used in the body of a subclass of FlagSet.
#define FLAGSET_DEFINE_FLAG_ACCESSORS(BIT, GETTER, SETTER) \
bool GETTER() const { return this->template getFlag<BIT>(); } \
void SETTER(bool value) { this->template setFlag<BIT>(value); }
// A convenient macro for defining a getter and setter for a field.
// Intended to be used in the body of a subclass of FlagSet.
#define FLAGSET_DEFINE_FIELD_ACCESSORS(BIT, WIDTH, TYPE, GETTER, SETTER) \
TYPE GETTER() const { return this->template getField<BIT, WIDTH, TYPE>(); } \
void SETTER(TYPE value) { this->template setField<BIT, WIDTH, TYPE>(value); }
// A convenient macro to expose equality operators.
// These can't be provided directly by FlagSet because that would allow
// different flag sets to be compared if they happen to have the same
// underlying type.
#define FLAGSET_DEFINE_EQUALITY(TYPENAME) \
friend bool operator==(TYPENAME lhs, TYPENAME rhs) { return lhs.getOpaqueValue() == rhs.getOpaqueValue(); } \
friend bool operator!=(TYPENAME lhs, TYPENAME rhs) { return lhs.getOpaqueValue() != rhs.getOpaqueValue(); }
public:
/// Get the bits as an opaque integer value.
IntType getOpaqueValue() const { return Bits; }
};
} // end namespace swift
#endif

View File

@ -0,0 +1,323 @@
/*
* swift_concurrency_hooks.h
*
* This source file is part of the FoundationDB open source project
*
* Copyright 2013-2022 Apple Inc. and the FoundationDB project authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef FLOW_SWIFT_CONCURRENCY_HOOKS_H
#define FLOW_SWIFT_CONCURRENCY_HOOKS_H
#include "swift.h"
#include "swift/ABI/Task.h"
#include <stdint.h>
#include "flow/AsioReactor.h"
#include "flow/TLSConfig.actor.h"
#if !defined(__has_feature)
#define __has_feature(x) 0
#endif
#if !defined(__has_attribute)
#define __has_attribute(x) 0
#endif
#if !defined(__has_builtin)
#define __has_builtin(builtin) 0
#endif
#if !defined(__has_cpp_attribute)
#define __has_cpp_attribute(attribute) 0
#endif
#define SWIFT_MACRO_CONCAT(A, B) A##B
#define SWIFT_MACRO_IF_0(IF_TRUE, IF_FALSE) IF_FALSE
#define SWIFT_MACRO_IF_1(IF_TRUE, IF_FALSE) IF_TRUE
#define SWIFT_MACRO_IF(COND, IF_TRUE, IF_FALSE) SWIFT_MACRO_CONCAT(SWIFT_MACRO_IF_, COND)(IF_TRUE, IF_FALSE)
#if __has_attribute(pure)
#define SWIFT_READONLY __attribute__((__pure__))
#else
#define SWIFT_READONLY
#endif
#if __has_attribute(const)
#define SWIFT_READNONE __attribute__((__const__))
#else
#define SWIFT_READNONE
#endif
#if __has_attribute(always_inline)
#define SWIFT_ALWAYS_INLINE __attribute__((always_inline))
#else
#define SWIFT_ALWAYS_INLINE
#endif
#if __has_attribute(noinline)
#define SWIFT_NOINLINE __attribute__((__noinline__))
#else
#define SWIFT_NOINLINE
#endif
#if __has_attribute(noreturn)
#ifndef SWIFT_NORETURN // since the generated Swift module header also will include a declaration
#define SWIFT_NORETURN __attribute__((__noreturn__))
#endif
#else
#define SWIFT_NORETURN
#endif
#if __has_attribute(used)
#define SWIFT_USED __attribute__((__used__))
#else
#define SWIFT_USED
#endif
#if __has_attribute(unavailable)
#define SWIFT_ATTRIBUTE_UNAVAILABLE __attribute__((__unavailable__))
#else
#define SWIFT_ATTRIBUTE_UNAVAILABLE
#endif
#if (__has_attribute(weak_import))
#define SWIFT_WEAK_IMPORT __attribute__((weak_import))
#else
#define SWIFT_WEAK_IMPORT
#endif
// Define the appropriate attributes for sharing symbols across
// image (executable / shared-library) boundaries.
//
// SWIFT_ATTRIBUTE_FOR_EXPORTS will be placed on declarations that
// are known to be exported from the current image. Typically, they
// are placed on header declarations and then inherited by the actual
// definitions.
//
// SWIFT_ATTRIBUTE_FOR_IMPORTS will be placed on declarations that
// are known to be exported from a different image. This never
// includes a definition.
//
// Getting the right attribute on a declaratioon can be pretty awkward,
// but it's necessary under the C translation model. All of this
// ceremony is familiar to Windows programmers; C/C++ programmers
// everywhere else usually don't bother, but since we have to get it
// right for Windows, we have everything set up to get it right on
// other targets as well, and doing so lets the compiler use more
// efficient symbol access patterns.
#if defined(__MACH__) || defined(__wasi__)
// On Mach-O and WebAssembly, we use non-hidden visibility. We just use
// default visibility on both imports and exports, both because these
// targets don't support protected visibility but because they don't
// need it: symbols are not interposable outside the current image
// by default.
#define SWIFT_ATTRIBUTE_FOR_EXPORTS __attribute__((__visibility__("default")))
#define SWIFT_ATTRIBUTE_FOR_IMPORTS __attribute__((__visibility__("default")))
#elif defined(__ELF__)
// On ELF, we use non-hidden visibility. For exports, we must use
// protected visibility to tell the compiler and linker that the symbols
// can't be interposed outside the current image. For imports, we must
// use default visibility because protected visibility guarantees that
// the symbol is defined in the current library, which isn't true for
// an import.
//
// The compiler does assume that the runtime and standard library can
// refer to each other's symbols as DSO-local, so it's important that
// we get this right or we can get linker errors.
#define SWIFT_ATTRIBUTE_FOR_EXPORTS __attribute__((__visibility__("protected")))
#define SWIFT_ATTRIBUTE_FOR_IMPORTS __attribute__((__visibility__("default")))
#elif defined(__CYGWIN__)
// For now, we ignore all this on Cygwin.
#define SWIFT_ATTRIBUTE_FOR_EXPORTS
#define SWIFT_ATTRIBUTE_FOR_IMPORTS
// FIXME: this #else should be some sort of #elif Windows
#else // !__MACH__ && !__ELF__
// On PE/COFF, we use dllimport and dllexport.
#define SWIFT_ATTRIBUTE_FOR_EXPORTS __declspec(dllexport)
#define SWIFT_ATTRIBUTE_FOR_IMPORTS __declspec(dllimport)
#endif
// CMake conventionally passes -DlibraryName_EXPORTS when building
// code that goes into libraryName. This isn't the best macro name,
// but it's conventional. We do have to pass it explicitly in a few
// places in the build system for a variety of reasons.
//
// Unfortunately, defined(D) is a special function you can use in
// preprocessor conditions, not a macro you can use anywhere, so we
// need to manually check for all the libraries we know about so that
// we can use them in our condition below.s
#if defined(swiftCore_EXPORTS)
#define SWIFT_IMAGE_EXPORTS_swiftCore 1
#else
#define SWIFT_IMAGE_EXPORTS_swiftCore 0
#endif
#if defined(swift_Concurrency_EXPORTS)
#define SWIFT_IMAGE_EXPORTS_swift_Concurrency 1
#else
#define SWIFT_IMAGE_EXPORTS_swift_Concurrency 0
#endif
#if defined(swift_Distributed_EXPORTS)
#define SWIFT_IMAGE_EXPORTS_swift_Distributed 1
#else
#define SWIFT_IMAGE_EXPORTS_swift_Distributed 0
#endif
#if defined(swift_Differentiation_EXPORTS)
#define SWIFT_IMAGE_EXPORTS_swift_Differentiation 1
#else
#define SWIFT_IMAGE_EXPORTS_swift_Differentiation 0
#endif
#define SWIFT_EXPORT_FROM_ATTRIBUTE(LIBRARY) \
SWIFT_MACRO_IF(SWIFT_IMAGE_EXPORTS_##LIBRARY, SWIFT_ATTRIBUTE_FOR_EXPORTS, SWIFT_ATTRIBUTE_FOR_IMPORTS)
// SWIFT_EXPORT_FROM(LIBRARY) declares something to be a C-linkage
// entity exported by the given library.
//
// SWIFT_RUNTIME_EXPORT is just SWIFT_EXPORT_FROM(swiftCore).
//
// TODO: use this in shims headers in overlays.
#if defined(__cplusplus)
#define SWIFT_EXPORT_FROM(LIBRARY) extern "C" SWIFT_EXPORT_FROM_ATTRIBUTE(LIBRARY)
#define SWIFT_EXPORT extern "C"
#else
#define SWIFT_EXPORT extern
#define SWIFT_EXPORT_FROM(LIBRARY) SWIFT_EXPORT_FROM_ATTRIBUTE(LIBRARY)
#endif
#define SWIFT_RUNTIME_EXPORT SWIFT_EXPORT_FROM(swiftCore)
// Define mappings for calling conventions.
// Annotation for specifying a calling convention of
// a runtime function. It should be used with declarations
// of runtime functions like this:
// void runtime_function_name() SWIFT_CC(swift)
#define SWIFT_CC(CC) SWIFT_CC_##CC
// SWIFT_CC(c) is the C calling convention.
#define SWIFT_CC_c
// SWIFT_CC(swift) is the Swift calling convention.
// FIXME: the next comment is false.
// Functions outside the stdlib or runtime that include this file may be built
// with a compiler that doesn't support swiftcall; don't define these macros
// in that case so any incorrect usage is caught.
#if __has_attribute(swiftcall)
#define SWIFT_CC_swift __attribute__((swiftcall))
#define SWIFT_CONTEXT __attribute__((swift_context))
#define SWIFT_ERROR_RESULT __attribute__((swift_error_result))
#define SWIFT_INDIRECT_RESULT __attribute__((swift_indirect_result))
#else
#define SWIFT_CC_swift
#define SWIFT_CONTEXT
#define SWIFT_ERROR_RESULT
#define SWIFT_INDIRECT_RESULT
#endif
// typedef struct _Job* JobRef;
/// A hook to take over global enqueuing.
typedef SWIFT_CC(swift) void (*swift_task_enqueueGlobal_original)(swift::Job* _Nonnull job);
SWIFT_EXPORT_FROM(swift_Concurrency)
SWIFT_CC(swift)
void (*_Nullable swift_task_enqueueGlobal_hook)(swift::Job* _Nonnull job,
swift_task_enqueueGlobal_original _Nonnull original);
/// A hook to take over global enqueuing with delay.
typedef SWIFT_CC(swift) void (*swift_task_enqueueGlobalWithDelay_original)(unsigned long long delay,
swift::Job* _Nonnull job);
SWIFT_EXPORT_FROM(swift_Concurrency)
SWIFT_CC(swift)
void (*_Nonnull swift_task_enqueueGlobalWithDelay_hook)(unsigned long long delay,
swift::Job* _Nonnull job,
swift_task_enqueueGlobalWithDelay_original _Nonnull original);
typedef SWIFT_CC(swift) void (*swift_task_enqueueGlobalWithDeadline_original)(long long sec,
long long nsec,
long long tsec,
long long tnsec,
int clock,
swift::Job* _Nonnull job);
SWIFT_EXPORT_FROM(swift_Concurrency)
SWIFT_CC(swift)
void (*_Nonnull swift_task_enqueueGlobalWithDeadline_hook)(
long long sec,
long long nsec,
long long tsec,
long long tnsec,
int clock,
swift::Job* _Nonnull job,
swift_task_enqueueGlobalWithDeadline_original _Nonnull original);
/// A hook to take over main executor enqueueing.
typedef SWIFT_CC(swift) void (*swift_task_enqueueMainExecutor_original)(swift::Job* _Nonnull job);
SWIFT_EXPORT_FROM(swift_Concurrency)
SWIFT_CC(swift)
void (*_Nonnull swift_task_enqueueMainExecutor_hook)(swift::Job* _Nonnull job,
swift_task_enqueueMainExecutor_original _Nonnull original);
SWIFT_EXPORT_FROM(swift_Concurrency)
SWIFT_CC(swift) void swift_job_run(swift::Job* _Nonnull job, ExecutorRef executor);
// FIXME: why is adding this function causing TDB issues, even if it is not a Swift declared thing?
// <unknown>:0: error: symbol '_Z17s_job_run_genericP3Job' (_Z17s_job_run_genericP3Job) is in generated IR file, but
// not in TBD file <unknown>:0: error: please submit a bug report (https://swift.org/contributing/#reporting-bugs)
// and include the project, and add '-Xfrontend -validate-tbd-against-ir=none' to squash the errors WORKAROUND:
// skipping TBD validation
// This function exists so we can get the generic executor, and don't have to do that from Swift.
void swift_job_run_generic(swift::Job* _Nonnull job);
SWIFT_CC(swift)
void net2_enqueueGlobal_hook_impl(swift::Job* _Nonnull job,
void (*_Nonnull)(swift::Job* _Nonnull) __attribute__((swiftcall)));
SWIFT_CC(swift)
void sim2_enqueueGlobal_hook_impl(swift::Job* _Nonnull job,
void (*_Nonnull)(swift::Job* _Nonnull) __attribute__((swiftcall)));
inline void installSwiftConcurrencyHooks(bool isSimulator, INetwork* _Nonnull net) {
#ifdef WITH_SWIFT
if (isSimulator) {
swift_task_enqueueGlobal_hook = &sim2_enqueueGlobal_hook_impl;
} else {
swift_task_enqueueGlobal_hook = &net2_enqueueGlobal_hook_impl;
}
#else
ASSERT(false && "Cannot install Swift Concurrency hooks when building without Swift.");
#endif
}
inline void newNet2ThenInstallSwiftConcurrencyHooks() {
#ifdef WITH_SWIFT
auto tls = new TLSConfig();
g_network = _swift_newNet2(tls, false, false);
installSwiftConcurrencyHooks(/*isSimulator=*/false, g_network);
#else
ASSERT(false && "Cannot install Swift Concurrency hooks when building without Swift.");
#endif
}
inline void globalNetworkRun() {
g_network->run(); // BLOCKS; dedicates this thread to the runloop
}
#endif

View File

@ -0,0 +1,102 @@
/*
* swift_future_support.h
*
* This source file is part of the FoundationDB open source project
*
* Copyright 2013-2022 Apple Inc. and the FoundationDB project authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef SWIFT_FUTURE_SUPPORT_H
#define SWIFT_FUTURE_SUPPORT_H
#include "swift.h"
#include "flow.h"
#include "swift_stream_support.h"
#include "pthread.h"
#include "unsafe_swift_compat.h"
#include "SwiftModules/Flow_CheckedContinuation.h"
#include <stdint.h>
// ==== ----------------------------------------------------------------------------------------------------------------
// MARK: type aliases, since we cannot work with templates yet in Swift
using PromiseCInt = Promise<int>;
using FutureCInt = Future<int>;
using CallbackInt = Callback<int>;
using PromiseVoid = Promise<Void>;
using FutureVoid = Future<Void>;
using CallbackVoid = Callback<Void>;
using PromiseStreamCInt = PromiseStream<int>;
using FutureStreamCInt = FutureStream<int>;
using PromiseStreamVoid = PromiseStream<Void>;
using FutureStreamVoid = FutureStream<Void>;
using PromiseStreamFutureVoid = PromiseStream<Future<Void>>;
using FutureStreamFutureVoid = FutureStream<Future<Void>>;
// ==== ----------------------------------------------------------------------------------------------------------------
// MARK: Callback types
// template<class T>
// class FlowCallbackForSwiftContinuation : Callback<T> {
// using SwiftCC = flow_swift::FlowCheckedContinuation<T>;
// SwiftCC continuationInstance;
// public:
// void set(const void * _Nonnull pointerToContinuationInstance,
// Future<T> f,
// const void * _Nonnull thisPointer) {
// // Verify Swift did not make a copy of the `self` value for this method
// // call.
// assert(this == thisPointer);
//
// // FIXME: Propagate `SwiftCC` to Swift using forward
// // interop, without relying on passing it via a `void *`
// // here. That will let us avoid this hack.
// const void *_Nonnull opaqueStorage = pointerToContinuationInstance;
// static_assert(sizeof(SwiftCC) == sizeof(const void *));
// const SwiftCC ccCopy(*reinterpret_cast<const SwiftCC *>(&opaqueStorage));
// // Set the continuation instance.
// continuationInstance.set(ccCopy);
// // Add this callback to the future.
// f.addCallbackAndClear(this);
// }
//
// FlowCallbackForSwiftContinuation() : continuationInstance(SwiftCC::init()) {
// }
//
// void fire(const T &value) override {
// Callback<T>::remove();
// Callback<T>::next = nullptr;
// continuationInstance.resume(value);
// }
// void error(Error error) override {
// Callback<T>::remove();
// Callback<T>::next = nullptr;
// continuationInstance.resumeThrowing(error);
// }
// void unwait() override {
// // TODO(swift): implement
// }
// };
// TODO(swift): Conform these to FlowCallbackForSwiftContinuationT automatically
using FlowCallbackForSwiftContinuationCInt = FlowCallbackForSwiftContinuation<int>;
// TODO(swift): Conform these to FlowCallbackForSwiftContinuationT automatically
using FlowCallbackForSwiftContinuationVoid = FlowCallbackForSwiftContinuation<Void>;
#endif

View File

@ -0,0 +1,156 @@
/*
* swift_stream_support.h
*
* This source file is part of the FoundationDB open source project
*
* Copyright 2013-2022 Apple Inc. and the FoundationDB project authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef SWIFT_STREAM_SUPPORT_H
#define SWIFT_STREAM_SUPPORT_H
#include "swift.h"
#include "flow.h"
#include "unsafe_swift_compat.h"
#include "SwiftModules/Flow_CheckedContinuation.h"
#include "pthread.h"
#include <stdint.h>
// ==== ----------------------------------------------------------------------------------------------------------------
// MARK: type aliases, since we cannot work with templates yet in Swift
using PromiseStreamCInt = PromiseStream<int>;
using FutureStreamCInt = FutureStream<int>;
// ==== ----------------------------------------------------------------------------------------------------------------
// TODO: Implements `FlowSingleCallbackForSwiftContinuationProtocol`
template <class T>
class FlowSingleCallbackForSwiftContinuation : SingleCallback<T> {
using SwiftCC = flow_swift::FlowCheckedContinuation<T>;
SwiftCC continuationInstance;
public:
void set(const void* _Nonnull pointerToContinuationInstance, FutureStream<T> fs, const void* _Nonnull thisPointer) {
// Verify Swift did not make a copy of the `self` value for this method
// call.
assert(this == thisPointer);
// FIXME: Propagate `SwiftCC` to Swift using forward
// interop, without relying on passing it via a `void *`
// here. That will let us avoid this hack.
const void* _Nonnull opaqueStorage = pointerToContinuationInstance;
static_assert(sizeof(SwiftCC) == sizeof(const void*));
const SwiftCC ccCopy(*reinterpret_cast<const SwiftCC*>(&opaqueStorage));
// Set the continuation instance.
continuationInstance.set(ccCopy);
// Add this callback to the future.
fs.addCallbackAndClear(this);
}
FlowSingleCallbackForSwiftContinuation() : continuationInstance(SwiftCC::init()) {}
void fire(T const& value) {
SingleCallback<T>::remove();
SingleCallback<T>::next = 0;
continuationInstance.resume(value);
}
void fire(T&& value) {
SingleCallback<T>::remove();
SingleCallback<T>::next = 0;
auto copy = value;
continuationInstance.resume(copy);
}
void error(Error error) {
SingleCallback<T>::remove();
SingleCallback<T>::next = 0;
continuationInstance.resumeThrowing(error);
}
void unwait() {
// TODO(swift): implement
}
};
// ==== ----------------------------------------------------------------------------------------------------------------
/// TODO(swift): Conform this to FlowSingleCallbackForSwiftContinuationProtocol from C++ already
using FlowSingleCallbackForSwiftContinuation_CInt = FlowSingleCallbackForSwiftContinuation<int>;
// ==== ----------------------------------------------------------------------------------------------------------------
// MARK: SingleCallback types
// Used for waiting on FutureStreams, which don't support multiple callbacks.
// FIXME(swift): either implement in Swift, or manage lifetime properly
struct UNSAFE_SWIFT_CXX_IMMORTAL_REF SwiftContinuationSingleCallbackCInt : SingleCallback<int> {
private:
void* _Nonnull continuationBox;
void (*_Nonnull resumeWithValue)(void* _Nonnull /*context*/, /*value*/ int);
void (*_Nonnull resumeWithError)(void* _Nonnull /*context*/, /*value*/ Error);
SwiftContinuationSingleCallbackCInt(void* _Nonnull continuationBox,
void (*_Nonnull returning)(void* _Nonnull, int),
void (*_Nonnull throwing)(void* _Nonnull, Error))
: continuationBox(continuationBox), resumeWithValue(returning), resumeWithError(throwing) {}
public:
static SwiftContinuationSingleCallbackCInt* _Nonnull make(void* _Nonnull continuationBox,
void (*_Nonnull returning)(void* _Nonnull, int),
void (*_Nonnull throwing)(void* _Nonnull, Error)) {
return new SwiftContinuationSingleCallbackCInt(continuationBox, returning, throwing);
}
void addCallbackAndClearTo(FutureStreamCInt f) { f.addCallbackAndClear(this); }
void fire(int const& value) {
SingleCallback<int>::remove();
SingleCallback<int>::next = 0;
resumeWithValue(continuationBox, value);
}
void fire(int&& value) {
SingleCallback<int>::remove();
SingleCallback<int>::next = 0;
auto copy = value;
resumeWithValue(continuationBox, copy);
}
void error(Error error) {
// TODO (Swift): TraceEvent?
printf("[c++][%s:%d](%s) [stream] cb=%p, ERROR: code=%d\n",
__FILE_NAME__,
__LINE__,
__FUNCTION__,
this,
error.code());
if (error.code() == error_code_end_of_stream) {
printf(
"[c++][%s:%d](%s) [stream] cb=%p, ERROR: END OF STREAM\n", __FILE_NAME__, __LINE__, __FUNCTION__, this);
}
SingleCallback<int>::remove();
SingleCallback<int>::next = 0;
resumeWithError(continuationBox, error);
}
void unwait() {
// TODO(swift): implement
}
};
#endif

View File

@ -0,0 +1,116 @@
/*
* swift_support.h
*
* This source file is part of the FoundationDB open source project
*
* Copyright 2013-2022 Apple Inc. and the FoundationDB project authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef FLOW_SWIFT_SUPPORT_H
#define FLOW_SWIFT_SUPPORT_H
#pragma once
#ifdef WITH_SWIFT
#include "flow/swift/ABI/Task.h"
#include "flow/TaskPriority.h"
// ==== ----------------------------------------------------------------------------------------------------------------
/// This annotation bridges immortal C++ singleton types
/// that are always accessed via a pointer or a reference in C++ as immortal class types in Swift.
#define SWIFT_CXX_IMMORTAL_SINGLETON_TYPE \
__attribute__((swift_attr("import_reference"))) __attribute__((swift_attr("retain:immortal"))) \
__attribute__((swift_attr("release:immortal")))
#define SWIFT_CXX_REF \
__attribute__((swift_attr("import_reference"))) __attribute__((swift_attr("retain:addref"))) \
__attribute__((swift_attr("release:delref")))
/// Ignore that a type seems to be an unsafe projection, and import it regardless.
#define SWIFT_CXX_IMPORT_UNSAFE __attribute__((swift_attr("import_unsafe")))
/// Import a C++ type as "owned", meaning that it "owns" all of the data it contains.
///
/// This is in contrast to a type which may have pointers to the "outside" somewhere,
/// where we might end up keeping pointers to memory which has been deallocated,
/// while we still have this projection in hand (and thus, an unsafe pointer access).
#define SWIFT_CXX_IMPORT_OWNED __attribute__((swift_attr("import_owned")))
/// Declare a type as `Sendable` which means that it is safe to be used concurrency,
/// and passed across actor and task boundaries.
///
/// Since in Flow we are single-threaded, basically everything is Sendable,
/// at least in the concurrent access safety sense of this annotation.
#define SWIFT_SENDABLE __attribute__((swift_attr("@Sendable")))
#define SWIFT_STRINGIFY(x) #x
#define CONCAT2(id1, id2) id1##id2
#define CONCAT3(id1, id2, id3) id1##id2##id3
// ==== ----------------------------------------------------------------------------------------------------------------
/// Convert a Swift JobPriority value to a numeric value of Flow/TaskPriority.
TaskPriority swift_priority_to_net2(swift::JobPriority p);
// ==== ----------------------------------------------------------------------------------------------------------------
#if __has_feature(nullability)
// Provide macros to temporarily suppress warning about the use of
// _Nullable and _Nonnull.
#define SWIFT_BEGIN_NULLABILITY_ANNOTATIONS \
_Pragma("clang diagnostic push") _Pragma("clang diagnostic ignored \"-Wnullability-extension\"") \
_Pragma("clang assume_nonnull begin")
#define SWIFT_END_NULLABILITY_ANNOTATIONS _Pragma("clang diagnostic pop") _Pragma("clang assume_nonnull end")
#else
// #define _Nullable and _Nonnull to nothing if we're not being built
// with a compiler that supports them.
#define _Nullable
#define _Nonnull
#define _Null_unspecified
#define SWIFT_BEGIN_NULLABILITY_ANNOTATIONS
#define SWIFT_END_NULLABILITY_ANNOTATIONS
#endif
#else
// No-op macros for Swift support
// ==== ----------------------------------------------------------------------------------------------------------------
/// This annotation bridges immortal C++ singleton types
/// that are always accessed via a pointer or a reference in C++ as immortal class types in Swift.
#define SWIFT_CXX_IMMORTAL_SINGLETON_TYPE
#define SWIFT_CXX_REF
#define SWIFT_CXX_IMPORT_UNSAFE
#define SWIFT_CXX_IMPORT_OWNED
#define SWIFT_SENDABLE
#define SWIFT_NAME(x)
#define CONCAT2(id1, id2) id1##id2
#define CONCAT3(id1, id2, id3) id1##id2##id3
#define _Nullable
#define _Nonnull
#define _Null_unspecified
#define SWIFT_BEGIN_NULLABILITY_ANNOTATIONS
#define SWIFT_END_NULLABILITY_ANNOTATIONS
#endif /* WITH_SWIFT */
#endif /* FLOW_SWIFT_SUPPORT_H */

View File

@ -22,6 +22,7 @@
#ifndef NO_INTELLISENSE
#undef ACTOR
#undef SWIFT_ACTOR
#undef DESCR
#undef state
#undef UNCANCELLABLE

View File

@ -0,0 +1,32 @@
/*
* unsafe_swift_compat.h
*
* This source file is part of the FoundationDB open source project
*
* Copyright 2013-2022 Apple Inc. and the FoundationDB project authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef FLOW_UNSAFE_SWIFT_COMPAT_H
#define FLOW_UNSAFE_SWIFT_COMPAT_H
/// This is an unsafe annotation that bridges C++ types as immortal class types in Swift.
///
/// Please take extreme care when applying this annotation to C++ types, as this annotation
/// can lead to use-after-frees or memory leaks very easily.
#define UNSAFE_SWIFT_CXX_IMMORTAL_REF \
__attribute__((swift_attr("import_reference"))) __attribute__((swift_attr("retain:immortal"))) \
__attribute__((swift_attr("release:immortal")))
#endif // FLOW_UNSAFE_SWIFT_COMPAT_H

24
flow/reply_support.swift Normal file
View File

@ -0,0 +1,24 @@
import Flow
// ==== ---------------------------------------------------------------------------------------------------------------
public protocol _FlowReplyPromiseOps {
/// Element type of the future
associatedtype _Reply
/// Complete this `ReplyPromise` with the result of this function
@_unsafeInheritExecutor
func with(_ makeReply: () async -> _Reply) async
/// ReplyPromise API
func send(_ value: inout _Reply)
}
extension _FlowReplyPromiseOps {
func with(_ makeReply: () async -> _Reply) async {
var rep = await makeReply()
self.send(&rep)
}
}
// TODO(swift): would love to extension ReplyPromise, but we can't until templates have better support.

137
flow/stream_support.swift Normal file
View File

@ -0,0 +1,137 @@
/*
* stream_support.swift
*
* This source file is part of the FoundationDB open source project
*
* Copyright 2013-2022 Apple Inc. and the FoundationDB project authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import Flow
public protocol FlowStreamOpsAsyncIterator: AsyncIteratorProtocol where Element == AssociatedFutureStream.Element {
associatedtype AssociatedFutureStream: FlowStreamOps
init(_: AssociatedFutureStream)
}
/// Corresponds to `FlowSingleCallbackForSwiftContinuation: Flow.SingleCallback<T>`
public protocol FlowSingleCallbackForSwiftContinuationProtocol<AssociatedFutureStream> {
associatedtype AssociatedFutureStream: FlowStreamOps
typealias Element = AssociatedFutureStream.Element
init()
///
/// ```
/// void set(
/// const void * _Nonnull pointerToContinuationInstance,
/// FutureStream<T> fs,
/// const void * _Nonnull thisPointer)
/// ```
mutating func set(_ continuationPointer: UnsafeRawPointer,
_ stream: Self.AssociatedFutureStream,
_ thisPointer: UnsafeRawPointer)
}
public protocol FlowStreamOps: AsyncSequence
where AsyncIterator: FlowStreamOpsAsyncIterator,
AsyncIterator.Element == Self.Element,
AsyncIterator.AssociatedFutureStream == Self,
SingleCB.AssociatedFutureStream == Self {
/// Element type of the stream
associatedtype Element
// : C++ SingleCallback<T> FlowSingleCallbackForSwiftContinuationProtocol
associatedtype SingleCB: FlowSingleCallbackForSwiftContinuationProtocol
where SingleCB.AssociatedFutureStream == Self
/// FIXME: can't typealias like that, we need to repeat it everywhere: rdar://103021742 ([Sema] Crash during self referential generic requirement)
// typealias AsyncIterator = FlowStreamOpsAsyncIteratorAsyncIterator<Self>
// === ---------------------------------------------------------------------
// Exposed Swift capabilities
/// Suspends and awaits for the next element.
///
/// If the stream completes while we're waiting on it, this will return `nil`.
/// Other errors thrown by the stream are re-thrown by this computed property.
var waitNext: Element? {
mutating get async throws
}
/// Implements protocol requirement of `AsyncSequence`, and enables async-for looping over this type.
func makeAsyncIterator() -> AsyncIterator
// === ---------------------------------------------------------------------
// C++ API
func isReady() -> Bool
func isError() -> Bool
mutating func pop() -> Element
mutating func getError() -> Flow.Error
}
/// Default implementations that are good for all adopters of this protocol, generally no need to override.
extension FlowStreamOps {
public var waitNext: Element? {
mutating get async throws {
if self.isReady() {
if self.isError() {
let error = self.getError()
if error.isEndOfStream {
return nil
} else {
throw GeneralFlowError(error)
}
} else {
return self.pop()
}
} else {
var s = SingleCB()
return try await withCheckedThrowingContinuation { (cc: CheckedContinuation<Element, Swift.Error>) in
withUnsafeMutablePointer(to: &s) { ptr in
let ecc = FlowCheckedContinuation<Element>(cc)
withUnsafePointer(to: ecc) { ccPtr in
ptr.pointee.set(UnsafeRawPointer(ccPtr), self, UnsafeRawPointer(ptr))
}
}
}
}
}
}
public func makeAsyncIterator() -> AsyncIterator {
AsyncIterator(self)
}
}
public struct FlowStreamOpsAsyncIteratorAsyncIterator<AssociatedFutureStream>: AsyncIteratorProtocol, FlowStreamOpsAsyncIterator
where AssociatedFutureStream: FlowStreamOps {
public typealias Element = AssociatedFutureStream.Element
var stream: AssociatedFutureStream
public init(_ stream: AssociatedFutureStream) {
self.stream = stream
}
public mutating func next() async throws -> Element? {
try await stream.waitNext
}
}

View File

@ -0,0 +1,73 @@
/*
* swift_concurrency_hooks.cpp
*
* This source file is part of the FoundationDB open source project
*
* Copyright 2013-2022 Apple Inc. and the FoundationDB project authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "flow/swift_concurrency_hooks.h"
#include "flow/swift.h"
#include "flow/swift/ABI/Task.h"
#include "flow/TLSConfig.actor.h"
// ==== ----------------------------------------------------------------------------------------------------------------
struct SwiftJobTask final : public N2::Task, public FastAllocated<SwiftJobTask> {
swift::Job* job;
explicit SwiftJobTask(swift::Job* job) noexcept : job(job) {}
void operator()() override {
swift_job_run(job, ExecutorRef::generic());
delete this;
}
};
// ==== ----------------------------------------------------------------------------------------------------------------
double flow_gNetwork_now() {
return g_network->now();
}
Future<class Void> flow_gNetwork_delay(double seconds, TaskPriority taskID) {
return g_network->delay(seconds, taskID);
}
// ==== ----------------------------------------------------------------------------------------------------------------
// ==== Net2 hooks
SWIFT_CC(swift)
void net2_enqueueGlobal_hook_impl(swift::Job* _Nonnull job, void (*_Nonnull)(swift::Job*) __attribute__((swiftcall))) {
// TODO: can't access Net2 since it's incomplete here, would be nicer to not expose API on INetwork I suppose
auto net = g_network;
ASSERT(net);
// auto swiftPriority = job->getPriority();
// int64_t priority = swift_priority_to_net2(swiftPriority); // default to lowest "Min"
//
// TaskPriority taskID = TaskPriority::DefaultOnMainThread; // FIXME: how to determine
//
// SwiftJobTask* jobTask = new SwiftJobTask(job);
// N2::OrderedTask* orderedTask = new N2::OrderedTask(priority, taskID, jobTask);
net->_swiftEnqueue(job);
}
void swift_job_run_generic(swift::Job* _Nonnull job) {
// NOTE: Guarded because swift_job_run is external import.
#ifdef WITH_SWIFT
swift_job_run(job, ExecutorRef::generic());
#endif
}

View File

@ -0,0 +1,270 @@
/*
* swift_concurrency_hooks.cpp
*
* This source file is part of the FoundationDB open source project
*
* Copyright 2013-2022 Apple Inc. and the FoundationDB project authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "flow/swift.h"
#include "flow/swift_concurrency_hooks.h"
#include "flow/swift/ABI/Task.h"
#include "flow/TLSConfig.actor.h"
// FIXME: surely there must be some more automatic way to maintain the mappings with Swift/C++ interop.
TaskPriority swift_priority_to_net2(swift::JobPriority p) {
// printf("[c++][%s:%d](%s) converting a priority (priority: %zu)\n", __FILE_NAME__, __LINE__, __FUNCTION__, p);
TaskPriority fp = TaskPriority::Zero;
switch (static_cast<std::underlying_type<swift::JobPriority>::type>(p)) {
case 255:
fp = TaskPriority::Max;
break;
case 200:
fp = TaskPriority::RunLoop;
break;
case 173:
fp = TaskPriority::ASIOReactor;
break;
case 73:
fp = TaskPriority::RunCycleFunction;
break;
case 72:
fp = TaskPriority::FlushTrace;
break;
case 71:
fp = TaskPriority::WriteSocket;
break;
case 70:
fp = TaskPriority::PollEIO;
break;
case 69:
fp = TaskPriority::DiskIOComplete;
break;
case 68:
fp = TaskPriority::LoadBalancedEndpoint;
break;
case 67:
fp = TaskPriority::ReadSocket;
break;
case 66:
fp = TaskPriority::AcceptSocket;
break;
case 65:
fp = TaskPriority::Handshake;
break;
case 64:
fp = TaskPriority::CoordinationReply;
break;
case 63:
fp = TaskPriority::Coordination;
break;
case 62:
fp = TaskPriority::FailureMonitor;
break;
case 61:
fp = TaskPriority::ResolutionMetrics;
break;
case 60:
fp = TaskPriority::Worker;
break;
case 59:
fp = TaskPriority::ClusterControllerWorker;
break;
case 58:
fp = TaskPriority::ClusterControllerRecruit;
break;
case 57:
fp = TaskPriority::ClusterControllerRegister;
break;
case 56:
fp = TaskPriority::ClusterController;
break;
case 55:
fp = TaskPriority::MasterTLogRejoin;
break;
case 54:
fp = TaskPriority::ProxyStorageRejoin;
break;
case 53:
fp = TaskPriority::TLogQueuingMetrics;
break;
case 52:
fp = TaskPriority::TLogPop;
break;
case 51:
fp = TaskPriority::TLogPeekReply;
break;
case 50:
fp = TaskPriority::TLogPeek;
break;
case 49:
fp = TaskPriority::TLogCommitReply;
break;
case 48:
fp = TaskPriority::TLogCommit;
break;
case 47:
fp = TaskPriority::ReportLiveCommittedVersion;
break;
case 46:
fp = TaskPriority::ProxyGetRawCommittedVersion;
break;
case 45:
fp = TaskPriority::ProxyMasterVersionReply;
break;
case 44:
fp = TaskPriority::ProxyCommitYield2;
break;
case 43:
fp = TaskPriority::ProxyTLogCommitReply;
break;
case 42:
fp = TaskPriority::ProxyCommitYield1;
break;
case 41:
fp = TaskPriority::ProxyResolverReply;
break;
case 40:
fp = TaskPriority::ProxyCommit;
break;
case 39:
fp = TaskPriority::ProxyCommitBatcher;
break;
case 38:
fp = TaskPriority::TLogConfirmRunningReply;
break;
case 37:
fp = TaskPriority::TLogConfirmRunning;
break;
case 36:
fp = TaskPriority::ProxyGRVTimer;
break;
case 35:
fp = TaskPriority::GetConsistentReadVersion;
break;
case 34:
fp = TaskPriority::GetLiveCommittedVersionReply;
break;
case 33:
fp = TaskPriority::GetLiveCommittedVersion;
break;
case 32:
fp = TaskPriority::GetTLogPrevCommitVersion;
break;
case 31:
fp = TaskPriority::UpdateRecoveryTransactionVersion;
break;
case 30:
fp = TaskPriority::DefaultPromiseEndpoint;
break;
case 29:
fp = TaskPriority::DefaultOnMainThread;
break;
case 28:
fp = TaskPriority::DefaultDelay;
break;
case 27:
fp = TaskPriority::DefaultYield;
break;
case 26:
fp = TaskPriority::DiskRead;
break;
case 25:
fp = TaskPriority::DefaultEndpoint;
break;
case 24:
fp = TaskPriority::UnknownEndpoint;
break;
case 23:
fp = TaskPriority::MoveKeys;
break;
case 22:
fp = TaskPriority::DataDistributionLaunch;
break;
case 21:
fp = TaskPriority::Ratekeeper;
break;
case 20:
fp = TaskPriority::DataDistribution;
break;
case 19:
fp = TaskPriority::DataDistributionLow;
break;
case 18:
fp = TaskPriority::DataDistributionVeryLow;
break;
case 17:
fp = TaskPriority::BlobManager;
break;
case 16:
fp = TaskPriority::DiskWrite;
break;
case 15:
fp = TaskPriority::UpdateStorage;
break;
case 14:
fp = TaskPriority::CompactCache;
break;
case 13:
fp = TaskPriority::TLogSpilledPeekReply;
break;
case 12:
fp = TaskPriority::BlobWorkerReadChangeFeed;
break;
case 11:
fp = TaskPriority::BlobWorkerUpdateFDB;
break;
case 10:
fp = TaskPriority::BlobWorkerUpdateStorage;
break;
case 9:
fp = TaskPriority::FetchKeys;
break;
case 8:
fp = TaskPriority::RestoreApplierWriteDB;
break;
case 7:
fp = TaskPriority::RestoreApplierReceiveMutations;
break;
case 6:
fp = TaskPriority::RestoreLoaderFinishVersionBatch;
break;
case 5:
fp = TaskPriority::RestoreLoaderSendMutations;
break;
case 4:
fp = TaskPriority::RestoreLoaderLoadFiles;
break;
case 3:
fp = TaskPriority::LowPriorityRead;
break;
case 2:
fp = TaskPriority::Low;
break;
case 1:
fp = TaskPriority::Min;
break;
case 0:
fp = TaskPriority::Zero;
break;
default: {
printf("[c++]Unknown priority: %zu\n", p);
abort();
}
}
// return static_cast<std::underlying_type<TaskPriority>::type>(fp);
return fp;
}

View File

@ -0,0 +1,439 @@
import Flow
import Flow
// ==== ---------------------------------------------------------------------------------------------------------------
extension Flow.TaskPriority {
/// Values representing TaskPriority in the range that is possible to use as Task priority in Swift Concurrency.
///
/// The values are set back to ``Flow/TaskPriority`` when they are enqueued.
///
/// WARNING: When changing the values here, make sure to change the switch in `swift_priority_to_flow`
///
/// Note: Priority in swift must be in the 0-255 range, so we should map the priorities into this range.
/// The actual values don't really matter at this point, because they'll be mapped back into the exact values as
/// has them declared in `flow.h` before enqueueing jobs.
private enum Repr: UInt8, CaseIterable {
/// WARNING: When changing the values here, make sure to change the switch in `swift_priority_to_net2`
case Max = 255
case RunLoop = 200
case ASIOReactor = 173
case SSSpilledChangeFeedReply = 74
case RunCycleFunction = 73
case FlushTrace = 72
case WriteSocket = 71
case PollEIO = 70
case DiskIOComplete = 69
case LoadBalancedEndpoint = 68
case ReadSocket = 67
case AcceptSocket = 66
case Handshake = 65
case CoordinationReply = 64
case Coordination = 63
case FailureMonitor = 62
case ResolutionMetrics = 61
case Worker = 60
case ClusterControllerWorker = 59
case ClusterControllerRecruit = 58
case ClusterControllerRegister = 57
case ClusterController = 56
case MasterTLogRejoin = 55
case ProxyStorageRejoin = 54
case TLogQueuingMetrics = 53
case TLogPop = 52
case TLogPeekReply = 51
case TLogPeek = 50
case TLogCommitReply = 49
case TLogCommit = 48
case ReportLiveCommittedVersion = 47
case ProxyGetRawCommittedVersion = 46
case ProxyMasterVersionReply = 45
case ProxyCommitYield2 = 44
case ProxyTLogCommitReply = 43
case ProxyCommitYield1 = 42
case ProxyResolverReply = 41
case ProxyCommit = 40
case ProxyCommitBatcher = 39
case TLogConfirmRunningReply = 38
case TLogConfirmRunning = 37
case ProxyGRVTimer = 36
case GetConsistentReadVersion = 35
case GetLiveCommittedVersionReply = 34
case GetLiveCommittedVersion = 33
case GetTLogPrevCommitVersion = 32
case UpdateRecoveryTransactionVersion = 31
case DefaultPromiseEndpoint = 30
case DefaultOnMainThread = 29
case DefaultDelay = 28
case DefaultYield = 27
case DiskRead = 26
case DefaultEndpoint = 25 // FIXME: avoid overlaps with Swift's predefined priorities
case UnknownEndpoint = 24
case MoveKeys = 23
case DataDistributionLaunch = 22
case Ratekeeper = 21
case DataDistribution = 20
case DataDistributionLow = 19
case DataDistributionVeryLow = 18
case BlobManager = 17
case DiskWrite = 16
case UpdateStorage = 15
case CompactCache = 14
case TLogSpilledPeekReply = 13
case BlobWorkerReadChangeFeed = 12
case BlobWorkerUpdateFDB = 11
case BlobWorkerUpdateStorage = 10
case FetchKeys = 9
case RestoreApplierWriteDB = 8
case RestoreApplierReceiveMutations = 7
case RestoreLoaderFinishVersionBatch = 6
case RestoreLoaderSendMutations = 5
case RestoreLoaderLoadFiles = 4
case LowPriorityRead = 3
case Low = 2
case Min = 1
case Zero = 0
}
// NOTE: The values returned MUST fit in UInt8 (i.e. be 0..<256)
var asSwift: _Concurrency.TaskPriority {
switch self {
case .Max: return .init(rawValue: Repr.Max.rawValue)
case .RunLoop: return .init(rawValue: Repr.RunLoop.rawValue)
case .ASIOReactor: return .init(rawValue: Repr.ASIOReactor.rawValue)
case .RunCycleFunction: return .init(rawValue: Repr.RunCycleFunction.rawValue)
case .FlushTrace: return .init(rawValue: Repr.FlushTrace.rawValue)
case .WriteSocket: return .init(rawValue: Repr.WriteSocket.rawValue)
case .PollEIO: return .init(rawValue: Repr.PollEIO.rawValue)
case .DiskIOComplete: return .init(rawValue: Repr.DiskIOComplete.rawValue)
case .LoadBalancedEndpoint: return .init(rawValue: Repr.LoadBalancedEndpoint.rawValue)
case .ReadSocket: return .init(rawValue: Repr.ReadSocket.rawValue)
case .AcceptSocket: return .init(rawValue: Repr.AcceptSocket.rawValue)
case .Handshake: return .init(rawValue: Repr.Handshake.rawValue)
case .CoordinationReply: return .init(rawValue: Repr.CoordinationReply.rawValue)
case .Coordination: return .init(rawValue: Repr.Coordination.rawValue)
case .FailureMonitor: return .init(rawValue: Repr.FailureMonitor.rawValue)
case .ResolutionMetrics: return .init(rawValue: Repr.ResolutionMetrics.rawValue)
case .Worker: return .init(rawValue: Repr.Worker.rawValue)
case .ClusterControllerWorker: return .init(rawValue: Repr.ClusterControllerWorker.rawValue)
case .ClusterControllerRecruit: return .init(rawValue: Repr.ClusterControllerRecruit.rawValue)
case .ClusterControllerRegister: return .init(rawValue: Repr.ClusterControllerRegister.rawValue)
case .ClusterController: return .init(rawValue: Repr.ClusterController.rawValue)
case .MasterTLogRejoin: return .init(rawValue: Repr.MasterTLogRejoin.rawValue)
case .ProxyStorageRejoin: return .init(rawValue: Repr.ProxyStorageRejoin.rawValue)
case .TLogQueuingMetrics: return .init(rawValue: Repr.TLogQueuingMetrics.rawValue)
case .TLogPop: return .init(rawValue: Repr.TLogPop.rawValue)
case .TLogPeekReply: return .init(rawValue: Repr.TLogPeekReply.rawValue)
case .TLogPeek: return .init(rawValue: Repr.TLogPeek.rawValue)
case .TLogCommitReply: return .init(rawValue: Repr.TLogCommitReply.rawValue)
case .TLogCommit: return .init(rawValue: Repr.TLogCommit.rawValue)
case .ReportLiveCommittedVersion: return .init(rawValue: Repr.ReportLiveCommittedVersion.rawValue)
case .ProxyGetRawCommittedVersion: return .init(rawValue: Repr.ProxyGetRawCommittedVersion.rawValue)
case .ProxyMasterVersionReply: return .init(rawValue: Repr.ProxyMasterVersionReply.rawValue)
case .ProxyCommitYield2: return .init(rawValue: Repr.ProxyCommitYield2.rawValue)
case .ProxyTLogCommitReply: return .init(rawValue: Repr.ProxyTLogCommitReply.rawValue)
case .ProxyCommitYield1: return .init(rawValue: Repr.ProxyCommitYield1.rawValue)
case .ProxyResolverReply: return .init(rawValue: Repr.ProxyResolverReply.rawValue)
case .ProxyCommit: return .init(rawValue: Repr.ProxyCommit.rawValue)
case .ProxyCommitBatcher: return .init(rawValue: Repr.ProxyCommitBatcher.rawValue)
case .TLogConfirmRunningReply: return .init(rawValue: Repr.TLogConfirmRunningReply.rawValue)
case .TLogConfirmRunning: return .init(rawValue: Repr.TLogConfirmRunning.rawValue)
case .ProxyGRVTimer: return .init(rawValue: Repr.ProxyGRVTimer.rawValue)
case .GetConsistentReadVersion: return .init(rawValue: Repr.GetConsistentReadVersion.rawValue)
case .GetLiveCommittedVersionReply: return .init(rawValue: Repr.GetLiveCommittedVersionReply.rawValue)
case .GetLiveCommittedVersion: return .init(rawValue: Repr.GetLiveCommittedVersion.rawValue)
case .GetTLogPrevCommitVersion: return .init(rawValue: Repr.GetTLogPrevCommitVersion.rawValue)
case .UpdateRecoveryTransactionVersion: return .init(rawValue: Repr.UpdateRecoveryTransactionVersion.rawValue)
case .DefaultPromiseEndpoint: return .init(rawValue: Repr.DefaultPromiseEndpoint.rawValue)
case .DefaultOnMainThread: return .init(rawValue: Repr.DefaultOnMainThread.rawValue)
case .DefaultDelay: return .init(rawValue: Repr.DefaultDelay.rawValue)
case .DefaultYield: return .init(rawValue: Repr.DefaultYield.rawValue)
case .DiskRead: return .init(rawValue: Repr.DiskRead.rawValue)
case .DefaultEndpoint: return .init(rawValue: Repr.DefaultEndpoint.rawValue)
case .UnknownEndpoint: return .init(rawValue: Repr.UnknownEndpoint.rawValue)
case .MoveKeys: return .init(rawValue: Repr.MoveKeys.rawValue)
case .DataDistributionLaunch: return .init(rawValue: Repr.DataDistributionLaunch.rawValue)
case .Ratekeeper: return .init(rawValue: Repr.Ratekeeper.rawValue)
case .DataDistribution: return .init(rawValue: Repr.DataDistribution.rawValue)
case .DataDistributionLow: return .init(rawValue: Repr.DataDistributionLow.rawValue)
case .DataDistributionVeryLow: return .init(rawValue: Repr.DataDistributionVeryLow.rawValue)
case .BlobManager: return .init(rawValue: Repr.BlobManager.rawValue)
case .DiskWrite: return .init(rawValue: Repr.DiskWrite.rawValue)
case .UpdateStorage: return .init(rawValue: Repr.UpdateStorage.rawValue)
case .CompactCache: return .init(rawValue: Repr.CompactCache.rawValue)
case .TLogSpilledPeekReply: return .init(rawValue: Repr.TLogSpilledPeekReply.rawValue)
case .SSSpilledChangeFeedReply: return .init(rawValue: Repr.SSSpilledChangeFeedReply.rawValue)
case .BlobWorkerReadChangeFeed: return .init(rawValue: Repr.BlobWorkerReadChangeFeed.rawValue)
case .BlobWorkerUpdateFDB: return .init(rawValue: Repr.BlobWorkerUpdateFDB.rawValue)
case .BlobWorkerUpdateStorage: return .init(rawValue: Repr.BlobWorkerUpdateStorage.rawValue)
case .FetchKeys: return .init(rawValue: Repr.FetchKeys.rawValue)
case .RestoreApplierWriteDB: return .init(rawValue: Repr.RestoreApplierWriteDB.rawValue)
case .RestoreApplierReceiveMutations: return .init(rawValue: Repr.RestoreApplierReceiveMutations.rawValue)
case .RestoreLoaderFinishVersionBatch: return .init(rawValue: Repr.RestoreLoaderFinishVersionBatch.rawValue)
case .RestoreLoaderSendMutations: return .init(rawValue: Repr.RestoreLoaderSendMutations.rawValue)
case .RestoreLoaderLoadFiles: return .init(rawValue: Repr.RestoreLoaderLoadFiles.rawValue)
case .LowPriorityRead: return .init(rawValue: Repr.LowPriorityRead.rawValue)
case .Low: return .init(rawValue: Repr.Low.rawValue)
case .Min: return .init(rawValue: Repr.Min.rawValue)
case .Zero: return .init(rawValue: Repr.Zero.rawValue)
@unknown default: fatalError("Unknown Flow.TaskPriority: \(self)")
}
}
}
extension _Concurrency.TaskPriority {
public static var Max: Self { Flow.TaskPriority.Max.asSwift }
public static var RunLoop: Self { Flow.TaskPriority.RunLoop.asSwift }
public static var ASIOReactor: Self { Flow.TaskPriority.ASIOReactor.asSwift }
public static var RunCycleFunction: Self { Flow.TaskPriority.RunCycleFunction.asSwift }
public static var FlushTrace: Self { Flow.TaskPriority.FlushTrace.asSwift }
public static var WriteSocket: Self { Flow.TaskPriority.WriteSocket.asSwift }
public static var PollEIO: Self { Flow.TaskPriority.PollEIO.asSwift }
public static var DiskIOComplete: Self { Flow.TaskPriority.DiskIOComplete.asSwift }
public static var LoadBalancedEndpoint: Self { Flow.TaskPriority.LoadBalancedEndpoint.asSwift }
public static var ReadSocket: Self { Flow.TaskPriority.ReadSocket.asSwift }
public static var AcceptSocket: Self { Flow.TaskPriority.AcceptSocket.asSwift }
public static var Handshake: Self { Flow.TaskPriority.Handshake.asSwift }
public static var CoordinationReply: Self { Flow.TaskPriority.CoordinationReply.asSwift }
public static var Coordination: Self { Flow.TaskPriority.Coordination.asSwift }
public static var FailureMonitor: Self { Flow.TaskPriority.FailureMonitor.asSwift }
public static var ResolutionMetrics: Self { Flow.TaskPriority.ResolutionMetrics.asSwift }
public static var Worker: Self { Flow.TaskPriority.Worker.asSwift }
public static var ClusterControllerWorker: Self { Flow.TaskPriority.ClusterControllerWorker.asSwift }
public static var ClusterControllerRecruit: Self { Flow.TaskPriority.ClusterControllerRecruit.asSwift }
public static var ClusterControllerRegister: Self { Flow.TaskPriority.ClusterControllerRegister.asSwift }
public static var ClusterController: Self { Flow.TaskPriority.ClusterController.asSwift }
public static var MasterTLogRejoin: Self { Flow.TaskPriority.MasterTLogRejoin.asSwift }
public static var ProxyStorageRejoin: Self { Flow.TaskPriority.ProxyStorageRejoin.asSwift }
public static var TLogQueuingMetrics: Self { Flow.TaskPriority.TLogQueuingMetrics.asSwift }
public static var TLogPop: Self { Flow.TaskPriority.TLogPop.asSwift }
public static var TLogPeekReply: Self { Flow.TaskPriority.TLogPeekReply.asSwift }
public static var TLogPeek: Self { Flow.TaskPriority.TLogPeek.asSwift }
public static var TLogCommitReply: Self { Flow.TaskPriority.TLogCommitReply.asSwift }
public static var TLogCommit: Self { Flow.TaskPriority.TLogCommit.asSwift }
public static var ReportLiveCommittedVersion: Self { Flow.TaskPriority.ReportLiveCommittedVersion.asSwift }
public static var ProxyGetRawCommittedVersion: Self { Flow.TaskPriority.ProxyGetRawCommittedVersion.asSwift }
public static var ProxyMasterVersionReply: Self { Flow.TaskPriority.ProxyMasterVersionReply.asSwift }
public static var ProxyCommitYield2: Self { Flow.TaskPriority.ProxyCommitYield2.asSwift }
public static var ProxyTLogCommitReply: Self { Flow.TaskPriority.ProxyTLogCommitReply.asSwift }
public static var ProxyCommitYield1: Self { Flow.TaskPriority.ProxyCommitYield1.asSwift }
public static var ProxyResolverReply: Self { Flow.TaskPriority.ProxyResolverReply.asSwift }
public static var ProxyCommit: Self { Flow.TaskPriority.ProxyCommit.asSwift }
public static var ProxyCommitBatcher: Self { Flow.TaskPriority.ProxyCommitBatcher.asSwift }
public static var TLogConfirmRunningReply: Self { Flow.TaskPriority.TLogConfirmRunningReply.asSwift }
public static var TLogConfirmRunning: Self { Flow.TaskPriority.TLogConfirmRunning.asSwift }
public static var ProxyGRVTimer: Self { Flow.TaskPriority.ProxyGRVTimer.asSwift }
public static var GetConsistentReadVersion: Self { Flow.TaskPriority.GetConsistentReadVersion.asSwift }
public static var GetLiveCommittedVersionReply: Self { Flow.TaskPriority.GetLiveCommittedVersionReply.asSwift }
public static var GetLiveCommittedVersion: Self { Flow.TaskPriority.GetLiveCommittedVersion.asSwift }
public static var GetTLogPrevCommitVersion: Self { Flow.TaskPriority.GetTLogPrevCommitVersion.asSwift }
public static var UpdateRecoveryTransactionVersion: Self { Flow.TaskPriority.UpdateRecoveryTransactionVersion.asSwift }
public static var DefaultPromiseEndpoint: Self { Flow.TaskPriority.DefaultPromiseEndpoint.asSwift }
public static var DefaultOnMainThread: Self { Flow.TaskPriority.DefaultOnMainThread.asSwift }
public static var DefaultDelay: Self { Flow.TaskPriority.DefaultDelay.asSwift }
public static var DefaultYield: Self { Flow.TaskPriority.DefaultYield.asSwift }
public static var DiskRead: Self { Flow.TaskPriority.DiskRead.asSwift }
public static var DefaultEndpoint: Self { Flow.TaskPriority.DefaultEndpoint.asSwift }
public static var UnknownEndpoint: Self { Flow.TaskPriority.UnknownEndpoint.asSwift }
public static var MoveKeys: Self { Flow.TaskPriority.MoveKeys.asSwift }
public static var DataDistributionLaunch: Self { Flow.TaskPriority.DataDistributionLaunch.asSwift }
public static var Ratekeeper: Self { Flow.TaskPriority.Ratekeeper.asSwift }
public static var DataDistribution: Self { Flow.TaskPriority.DataDistribution.asSwift }
public static var DataDistributionLow: Self { Flow.TaskPriority.DataDistributionLow.asSwift }
public static var DataDistributionVeryLow: Self { Flow.TaskPriority.DataDistributionVeryLow.asSwift }
public static var BlobManager: Self { Flow.TaskPriority.BlobManager.asSwift }
public static var DiskWrite: Self { Flow.TaskPriority.DiskWrite.asSwift }
public static var UpdateStorage: Self { Flow.TaskPriority.UpdateStorage.asSwift }
public static var CompactCache: Self { Flow.TaskPriority.CompactCache.asSwift }
public static var TLogSpilledPeekReply: Self { Flow.TaskPriority.TLogSpilledPeekReply.asSwift }
public static var SSSpilledChangeFeedReply: Self { Flow.TaskPriority.SSSpilledChangeFeedReply.asSwift }
public static var BlobWorkerReadChangeFeed: Self { Flow.TaskPriority.BlobWorkerReadChangeFeed.asSwift }
public static var BlobWorkerUpdateFDB: Self { Flow.TaskPriority.BlobWorkerUpdateFDB.asSwift }
public static var BlobWorkerUpdateStorage: Self { Flow.TaskPriority.BlobWorkerUpdateStorage.asSwift }
public static var FetchKeys: Self { Flow.TaskPriority.FetchKeys.asSwift }
public static var RestoreApplierWriteDB: Self { Flow.TaskPriority.RestoreApplierWriteDB.asSwift }
public static var RestoreApplierReceiveMutations: Self { Flow.TaskPriority.RestoreApplierReceiveMutations.asSwift }
public static var RestoreLoaderFinishVersionBatch: Self { Flow.TaskPriority.RestoreLoaderFinishVersionBatch.asSwift }
public static var RestoreLoaderSendMutations: Self { Flow.TaskPriority.RestoreLoaderSendMutations.asSwift }
public static var RestoreLoaderLoadFiles: Self { Flow.TaskPriority.RestoreLoaderLoadFiles.asSwift }
public static var LowPriorityRead: Self { Flow.TaskPriority.LowPriorityRead.asSwift }
public static var Low: Self { Flow.TaskPriority.Low.asSwift }
public static var Min : Self { Flow.TaskPriority.Min.asSwift }
public static var Zero: Self { Flow.TaskPriority.Zero.asSwift }
var rawValueNet2: Int32 {
switch self {
case .Max: return Flow.TaskPriority.Max.rawValue
case .RunLoop: return Flow.TaskPriority.RunLoop.rawValue
case .ASIOReactor: return Flow.TaskPriority.ASIOReactor.rawValue
case .RunCycleFunction: return Flow.TaskPriority.RunCycleFunction.rawValue
case .FlushTrace: return Flow.TaskPriority.FlushTrace.rawValue
case .WriteSocket: return Flow.TaskPriority.WriteSocket.rawValue
case .PollEIO: return Flow.TaskPriority.PollEIO.rawValue
case .DiskIOComplete: return Flow.TaskPriority.DiskIOComplete.rawValue
case .LoadBalancedEndpoint: return Flow.TaskPriority.LoadBalancedEndpoint.rawValue
case .ReadSocket: return Flow.TaskPriority.ReadSocket.rawValue
case .AcceptSocket: return Flow.TaskPriority.AcceptSocket.rawValue
case .Handshake: return Flow.TaskPriority.Handshake.rawValue
case .CoordinationReply: return Flow.TaskPriority.CoordinationReply.rawValue
case .Coordination: return Flow.TaskPriority.Coordination.rawValue
case .FailureMonitor: return Flow.TaskPriority.FailureMonitor.rawValue
case .ResolutionMetrics: return Flow.TaskPriority.ResolutionMetrics.rawValue
case .Worker: return Flow.TaskPriority.Worker.rawValue
case .ClusterControllerWorker: return Flow.TaskPriority.ClusterControllerWorker.rawValue
case .ClusterControllerRecruit: return Flow.TaskPriority.ClusterControllerRecruit.rawValue
case .ClusterControllerRegister: return Flow.TaskPriority.ClusterControllerRegister.rawValue
case .ClusterController: return Flow.TaskPriority.ClusterController.rawValue
case .MasterTLogRejoin: return Flow.TaskPriority.MasterTLogRejoin.rawValue
case .ProxyStorageRejoin: return Flow.TaskPriority.ProxyStorageRejoin.rawValue
case .TLogQueuingMetrics: return Flow.TaskPriority.TLogQueuingMetrics.rawValue
case .TLogPop: return Flow.TaskPriority.TLogPop.rawValue
case .TLogPeekReply: return Flow.TaskPriority.TLogPeekReply.rawValue
case .TLogPeek: return Flow.TaskPriority.TLogPeek.rawValue
case .TLogCommitReply: return Flow.TaskPriority.TLogCommitReply.rawValue
case .TLogCommit: return Flow.TaskPriority.TLogCommit.rawValue
case .ReportLiveCommittedVersion: return Flow.TaskPriority.ReportLiveCommittedVersion.rawValue
case .ProxyGetRawCommittedVersion: return Flow.TaskPriority.ProxyGetRawCommittedVersion.rawValue
case .ProxyMasterVersionReply: return Flow.TaskPriority.ProxyMasterVersionReply.rawValue
case .ProxyCommitYield2: return Flow.TaskPriority.ProxyCommitYield2.rawValue
case .ProxyTLogCommitReply: return Flow.TaskPriority.ProxyTLogCommitReply.rawValue
case .ProxyCommitYield1: return Flow.TaskPriority.ProxyCommitYield1.rawValue
case .ProxyResolverReply: return Flow.TaskPriority.ProxyResolverReply.rawValue
case .ProxyCommit: return Flow.TaskPriority.ProxyCommit.rawValue
case .ProxyCommitBatcher: return Flow.TaskPriority.ProxyCommitBatcher.rawValue
case .TLogConfirmRunningReply: return Flow.TaskPriority.TLogConfirmRunningReply.rawValue
case .TLogConfirmRunning: return Flow.TaskPriority.TLogConfirmRunning.rawValue
case .ProxyGRVTimer: return Flow.TaskPriority.ProxyGRVTimer.rawValue
case .GetConsistentReadVersion: return Flow.TaskPriority.GetConsistentReadVersion.rawValue
case .GetLiveCommittedVersionReply: return Flow.TaskPriority.GetLiveCommittedVersionReply.rawValue
case .GetLiveCommittedVersion: return Flow.TaskPriority.GetLiveCommittedVersion.rawValue
case .GetTLogPrevCommitVersion: return Flow.TaskPriority.GetTLogPrevCommitVersion.rawValue
case .UpdateRecoveryTransactionVersion: return Flow.TaskPriority.UpdateRecoveryTransactionVersion.rawValue
case .DefaultPromiseEndpoint: return Flow.TaskPriority.DefaultPromiseEndpoint.rawValue
case .DefaultOnMainThread: return Flow.TaskPriority.DefaultOnMainThread.rawValue
case .DefaultDelay: return Flow.TaskPriority.DefaultDelay.rawValue
case .DefaultYield: return Flow.TaskPriority.DefaultYield.rawValue
case .DiskRead: return Flow.TaskPriority.DiskRead.rawValue
case .DefaultEndpoint: return Flow.TaskPriority.DefaultEndpoint.rawValue
case .UnknownEndpoint: return Flow.TaskPriority.UnknownEndpoint.rawValue
case .MoveKeys: return Flow.TaskPriority.MoveKeys.rawValue
case .DataDistributionLaunch: return Flow.TaskPriority.DataDistributionLaunch.rawValue
case .Ratekeeper: return Flow.TaskPriority.Ratekeeper.rawValue
case .DataDistribution: return Flow.TaskPriority.DataDistribution.rawValue
case .DataDistributionLow: return Flow.TaskPriority.DataDistributionLow.rawValue
case .DataDistributionVeryLow: return Flow.TaskPriority.DataDistributionVeryLow.rawValue
case .BlobManager: return Flow.TaskPriority.BlobManager.rawValue
case .DiskWrite: return Flow.TaskPriority.DiskWrite.rawValue
case .UpdateStorage: return Flow.TaskPriority.UpdateStorage.rawValue
case .CompactCache: return Flow.TaskPriority.CompactCache.rawValue
case .TLogSpilledPeekReply: return Flow.TaskPriority.TLogSpilledPeekReply.rawValue
case .SSSpilledChangeFeedReply: return Flow.TaskPriority.SSSpilledChangeFeedReply.rawValue
case .BlobWorkerReadChangeFeed: return Flow.TaskPriority.BlobWorkerReadChangeFeed.rawValue
case .BlobWorkerUpdateFDB: return Flow.TaskPriority.BlobWorkerUpdateFDB.rawValue
case .BlobWorkerUpdateStorage: return Flow.TaskPriority.BlobWorkerUpdateStorage.rawValue
case .FetchKeys: return Flow.TaskPriority.FetchKeys.rawValue
case .RestoreApplierWriteDB: return Flow.TaskPriority.RestoreApplierWriteDB.rawValue
case .RestoreApplierReceiveMutations: return Flow.TaskPriority.RestoreApplierReceiveMutations.rawValue
case .RestoreLoaderFinishVersionBatch: return Flow.TaskPriority.RestoreLoaderFinishVersionBatch.rawValue
case .RestoreLoaderSendMutations: return Flow.TaskPriority.RestoreLoaderSendMutations.rawValue
case .RestoreLoaderLoadFiles: return Flow.TaskPriority.RestoreLoaderLoadFiles.rawValue
case .LowPriorityRead: return Flow.TaskPriority.LowPriorityRead.rawValue
case .Low: return Flow.TaskPriority.Low.rawValue
case .Min: return Flow.TaskPriority.Min.rawValue
case .Zero: return Flow.TaskPriority.Zero.rawValue
default: fatalError("Unknown TaskPriority value: \(self.rawValue)")
}
}
var name: String {
switch self {
case .Max: return "Max"
case .RunLoop: return "RunLoop"
case .ASIOReactor: return "ASIOReactor"
case .RunCycleFunction: return "RunCycleFunction"
case .FlushTrace: return "FlushTrace"
case .WriteSocket: return "WriteSocket"
case .PollEIO: return "PollEIO"
case .DiskIOComplete: return "DiskIOComplete"
case .LoadBalancedEndpoint: return "LoadBalancedEndpoint"
case .ReadSocket: return "ReadSocket"
case .AcceptSocket: return "AcceptSocket"
case .Handshake: return "Handshake"
case .CoordinationReply: return "CoordinationReply"
case .Coordination: return "Coordination"
case .FailureMonitor: return "FailureMonitor"
case .ResolutionMetrics: return "ResolutionMetrics"
case .Worker: return "Worker"
case .ClusterControllerWorker: return "ClusterControllerWorker"
case .ClusterControllerRecruit: return "ClusterControllerRecruit"
case .ClusterControllerRegister: return "ClusterControllerRegister"
case .ClusterController: return "ClusterController"
case .MasterTLogRejoin: return "MasterTLogRejoin"
case .ProxyStorageRejoin: return "ProxyStorageRejoin"
case .TLogQueuingMetrics: return "TLogQueuingMetrics"
case .TLogPop: return "TLogPop"
case .TLogPeekReply: return "TLogPeekReply"
case .TLogPeek: return "TLogPeek"
case .TLogCommitReply: return "TLogCommitReply"
case .TLogCommit: return "TLogCommit"
case .ReportLiveCommittedVersion: return "ReportLiveCommittedVersion"
case .ProxyGetRawCommittedVersion: return "ProxyGetRawCommittedVersion"
case .ProxyMasterVersionReply: return "ProxyMasterVersionReply"
case .ProxyCommitYield2: return "ProxyCommitYield2"
case .ProxyTLogCommitReply: return "ProxyTLogCommitReply"
case .ProxyCommitYield1: return "ProxyCommitYield1"
case .ProxyResolverReply: return "ProxyResolverReply"
case .ProxyCommit: return "ProxyCommit"
case .ProxyCommitBatcher: return "ProxyCommitBatcher"
case .TLogConfirmRunningReply: return "TLogConfirmRunningReply"
case .TLogConfirmRunning: return "TLogConfirmRunning"
case .ProxyGRVTimer: return "ProxyGRVTimer"
case .GetConsistentReadVersion: return "GetConsistentReadVersion"
case .GetLiveCommittedVersionReply: return "GetLiveCommittedVersionReply"
case .GetLiveCommittedVersion: return "GetLiveCommittedVersion"
case .GetTLogPrevCommitVersion: return "GetTLogPrevCommitVersion"
case .UpdateRecoveryTransactionVersion: return "UpdateRecoveryTransactionVersion"
case .DefaultPromiseEndpoint: return "DefaultPromiseEndpoint"
case .DefaultOnMainThread: return "DefaultOnMainThread"
case .DefaultDelay: return "DefaultDelay"
case .DefaultYield: return "DefaultYield"
case .DiskRead: return "DiskRead"
case .DefaultEndpoint: return "DefaultEndpoint"
case .UnknownEndpoint: return "UnknownEndpoint"
case .MoveKeys: return "MoveKeys"
case .DataDistributionLaunch: return "DataDistributionLaunch"
case .Ratekeeper: return "Ratekeeper"
case .DataDistribution: return "DataDistribution"
case .DataDistributionLow: return "DataDistributionLow"
case .DataDistributionVeryLow: return "DataDistributionVeryLow"
case .BlobManager: return "BlobManager"
case .DiskWrite: return "DiskWrite"
case .UpdateStorage: return "UpdateStorage"
case .CompactCache: return "CompactCache"
case .TLogSpilledPeekReply: return "TLogSpilledPeekReply"
case .SSSpilledChangeFeedReply: return "SSSpilledChangeFeedReply"
case .BlobWorkerReadChangeFeed: return "BlobWorkerReadChangeFeed"
case .BlobWorkerUpdateFDB: return "BlobWorkerUpdateFDB"
case .BlobWorkerUpdateStorage: return "BlobWorkerUpdateStorage"
case .FetchKeys: return "FetchKeys"
case .RestoreApplierWriteDB: return "RestoreApplierWriteDB"
case .RestoreApplierReceiveMutations: return "RestoreApplierReceiveMutations"
case .RestoreLoaderFinishVersionBatch: return "RestoreLoaderFinishVersionBatch"
case .RestoreLoaderSendMutations: return "RestoreLoaderSendMutations"
case .RestoreLoaderLoadFiles: return "RestoreLoaderLoadFiles"
case .LowPriorityRead: return "LowPriorityRead"
case .Low: return "Low"
case .Min: return "Min"
case .Zero: return "Zero"
default: return "<unknown:\(self.rawValue)>"
}
}
}
extension _Concurrency.TaskPriority {
public var flowDescription: String {
"_Concurrency.TaskPriority(\(self.name), rawValue: \(rawValue), rawValueNet2: \(self.rawValueNet2))"
}
}

103
flow/trace_support.swift Normal file
View File

@ -0,0 +1,103 @@
import Flow
// ==== ---------------------------------------------------------------------------------------------------------------
/// Wrapper around `Flow.TraceEvent` that allows similar style "fluent api" use as the C++ flow event.
///
/// We need this because the C++ type actually mutates the struct, which in Swift would need to be
/// expressed as mutating functions on a variable, and cannot be done in a "fluent" style.
/// Instead, this wrapper returns a new struct when a detail is added allowing for the same style of use.
public final class STraceEvent {
private var keepAlive: [std.string] = []
public var event: Flow.TraceEvent
public init(_ type: std.string, _ id: Flow.UID) {
keepAlive.append(type)
event = .init(type.__c_strUnsafe(), id)
}
/// This function allows ignoring the returned value, i.e. emitting just a plain event
/// without any extra `detail()` attached to it.
@discardableResult
public static func make(_ type: std.string, _ id: Flow.UID) -> Self {
.init(type, id)
}
// Due to a limitation of passing a generic `T` to a C++ template, we cannot just use
// one generic detail<T>() and have to write out the overloads so the type is concrete
// for the template expansion.
@discardableResult
public func detail(_ type: std.string, _ value: OptionalStdString) -> Self {
/*copy*/keepAlive.append(type)
/*copy*/event.addDetail(type, value)
return self
}
@discardableResult
public func detail(_ type: std.string, _ value: std.string) -> Self {
/*copy*/keepAlive.append(type)
/*copy*/event.addDetail(type, value)
return self
}
@discardableResult
public func detail(_ type: std.string, _ value: Float) -> Self {
/*copy*/keepAlive.append(type)
/*copy*/event.addDetail(type, value)
return self
}
@discardableResult
public func detail(_ type: std.string, _ value: Double) -> Self {
/*copy*/keepAlive.append(type)
/*copy*/event.addDetail(type, value)
return self
}
@discardableResult
public func detail(_ type: std.string, _ value: Int) -> Self {
/*copy*/keepAlive.append(type)
/*copy*/event.addDetail(type, value)
return self
}
@discardableResult
public func detail(_ type: std.string, _ value: OptionalInt64) -> Self {
/*copy*/keepAlive.append(type)
/*copy*/event.addDetail(type, value)
return self
}
// TODO(swift): we seem to have a problem when mapping Int8 -> char
// @discardableResult
// public func detail(_ type: std.string, _ value: Int8) -> Self {
// // var copy = self
// /*copy*/event.addDetail(type, value)
// return self
// }
@discardableResult
public func detail(_ type: std.string, _ value: UInt32) -> Self {
/*copy*/keepAlive.append(type)
/*copy*/event.addDetail(type, value)
return self
}
@discardableResult
public func detail(_ type: std.string, _ value: Int32) -> Self {
/*copy*/keepAlive.append(type)
/*copy*/event.addDetail(type, value)
return self
}
@discardableResult
public func detail(_ type: std.string, _ value: Int64) -> Self {
/*copy*/keepAlive.append(type)
/*copy*/event.addDetail(type, value)
return self
}
@discardableResult
public func detail(_ type: std.string, _ value: UInt64) -> Self {
/*copy*/keepAlive.append(type)
/*copy*/event.addDetail(type, value)
return self
}
}

View File

@ -0,0 +1,5 @@
module @MODULE_NAME@ {
@MODULE_HEADERS@
export *
}

View File

@ -0,0 +1,7 @@
{
"use-external-names": false,
"version": 0,
"roots": [
@VFS_ROOTS@
]
}

7
swift_get_latest_toolchain.sh Executable file
View File

@ -0,0 +1,7 @@
#!/bin/bash
# snapshot toolchains from 'main'
# wget $(curl -s https://ci.swift.org/job/oss-swift-package-centos-7/lastSuccessfulBuild/consoleText | grep 'tmp-ci-nightly' | sed 's/-- /\n/g' | tail -1 | sed 's#https://store-030.blobstore.apple.com/swift-oss#https://download.swift.org#g')
# 5.9 snapshot toolchains
wget $(curl -s https://ci.swift.org/job/oss-swift-5.9-package-centos-7/lastSuccessfulBuild/consoleText | grep 'tmp-ci-nightly' | sed 's/-- /\n/g' | tail -1 | sed 's#https://store-030.blobstore.apple.com/swift-oss#https://download.swift.org#g')