blob: b813f071856e081db986598d1177f06bb371c14e [file] [log] [blame]
//===--- AsyncCall.h - Conveniences for doing async calls ----------*- 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
//
//===----------------------------------------------------------------------===//
//
// Convenience functions for implementing Swift asynchronous functions
// in C++ code.
//
//===----------------------------------------------------------------------===//
#ifndef SWIFT_CONCURRENCY_ASYNCCALL_H
#define SWIFT_CONCURRENCY_ASYNCCALL_H
#include "swift/Runtime/Concurrency.h"
#include "swift/ABI/Task.h"
#include <tuple>
namespace swift {
namespace {
/// Template-metaprogrammed basic layout for the given sequence of types.
template <size_t StartingOffset, class... FieldTys>
struct BasicLayout;
template <size_t StartingOffset>
struct BasicLayout<StartingOffset> {
static constexpr size_t size = StartingOffset;
};
template <size_t StartingOffset, class HeadTy, class... TailTys>
struct BasicLayout<StartingOffset, HeadTy, TailTys...> {
// Round up to a multiple of the alignment.
static constexpr size_t fieldOffset =
(StartingOffset + alignof(HeadTy) - 1) & ~(alignof(HeadTy) - 1);
static constexpr size_t fieldEnd = fieldOffset + sizeof(HeadTy);
using TailLayout = BasicLayout<fieldEnd, TailTys...>;
static constexpr size_t size = TailLayout::size;
};
template <class Layout, size_t Index>
struct BasicLayoutOffset {
static constexpr size_t value =
BasicLayoutOffset<typename Layout::TailLayout, Index - 1>::value;
};
template <class Layout>
struct BasicLayoutOffset<Layout, 0> {
static constexpr size_t value = Layout::fieldOffset;
};
/// Template-metaprogrammed layout for an async frame with the given
/// signature. Works as long as you don't have a mix of indirect and
/// direct results; indirect results should be coded as initial
/// indirect arguments in the function signature.
///
/// Note that there's always a slot for an error result.
template <class Signature>
struct AsyncFrameLayout;
template <class... ArgTys, bool HasErrorResult>
struct AsyncFrameLayout<AsyncSignature<void(ArgTys...), HasErrorResult>> {
using BasicLayout = BasicLayout<0, SwiftError*, ArgTys...>;
static constexpr size_t firstArgIndex = 1;
};
template <class ResultTy, class... ArgTys, bool HasErrorResult>
struct AsyncFrameLayout<AsyncSignature<ResultTy(ArgTys...), HasErrorResult>> {
using BasicLayout = BasicLayout<0, SwiftError*, ResultTy, ArgTys...>;
static constexpr size_t firstArgIndex = 2;
};
/// A helper class which, when used as a base class under common
/// C++ ABIs, adds no extra size to a struct when the template
/// argument is 0.
template <size_t Size>
class AsyncFrameStorageHelper: public AsyncContext {
// This needs to be aligned at least this much or else Itanium
// will try to put it in the tail-padding of the AsyncContext.
alignas(void*)
char buffer[Size];
public:
using AsyncContext::AsyncContext;
char *data() { return buffer; }
};
template <>
class AsyncFrameStorageHelper<0>: public AsyncContext {
public:
using AsyncContext::AsyncContext;
char *data() { return reinterpret_cast<char*>(this); }
};
template <class CalleeSignature,
class FrameLayout = AsyncFrameLayout<CalleeSignature>>
struct AsyncFrameStorage;
template <class ResultTy, class... ArgTys,
bool HasErrorResult, class FrameLayout>
struct AsyncFrameStorage<AsyncSignature<ResultTy(ArgTys...),
HasErrorResult>,
FrameLayout>
: AsyncFrameStorageHelper<FrameLayout::BasicLayout::size> {
AsyncFrameStorage(AsyncContextFlags flags,
TaskContinuationFunction *resumeFunction,
ExecutorRef resumeToExecutor,
AsyncContext *resumeToContext,
ArgTys... args)
: AsyncFrameStorageHelper<FrameLayout::BasicLayout::size>(
flags, resumeFunction, resumeToExecutor, resumeToContext) {
initializeHelper<FrameLayout::firstArgIndex>(this->data(), args...);
}
private:
template <size_t NextArgIndex>
void initializeHelper(char *buffer) {}
template <size_t NextArgIndex, class NextArgTy, class... TailArgTys>
void initializeHelper(char *buffer, NextArgTy nextArg,
TailArgTys... tailArgs) {
auto offset = BasicLayoutOffset<typename FrameLayout::BasicLayout,
NextArgIndex>::value;
new (buffer + offset) NextArgTy(nextArg);
initializeHelper<NextArgIndex+1>(buffer, tailArgs...);
}
};
/// The context header for calling a function that takes the
/// given arguments.
template <class CallerContextType, class CalleeSignature>
struct AsyncCalleeContext : AsyncFrameStorage<CalleeSignature> {
using CallerContext = CallerContextType;
template <class... Args>
AsyncCalleeContext(TaskContinuationFunction *resumeFunction,
ExecutorRef resumeToExecutor,
CallerContext *resumeToContext,
Args... args)
: AsyncFrameStorage<CalleeSignature>(AsyncContextKind::Ordinary,
resumeFunction, resumeToExecutor,
resumeToContext, args...) {}
CallerContext *getParent() const {
return static_cast<CallerContext*>(this->Parent);
}
};
/// Push a context to call a function.
template <class CalleeSignature, class CallerContext, class... Args>
static AsyncCalleeContext<CallerContext, CalleeSignature> *
pushAsyncContext(AsyncTask *task, ExecutorRef executor,
CallerContext *callerContext, size_t calleeContextSize,
TaskContinuationFunction *resumeFunction,
Args... args) {
using CalleeContext =
AsyncCalleeContext<CallerContext, CalleeSignature>;
assert(calleeContextSize >= sizeof(CalleeContext));
void *rawCalleeContext = swift_task_alloc(task, calleeContextSize);
return new (rawCalleeContext) CalleeContext(resumeFunction, executor,
callerContext, args...);
}
/// Make an asynchronous call.
template <class CalleeSignature, class CallerContext, class... Args>
SWIFT_CC(swiftasync)
static void callAsync(AsyncTask *task,
ExecutorRef executor,
CallerContext *callerContext,
TaskContinuationFunction *resumeFunction,
const typename CalleeSignature::FunctionPointer *function,
Args... args) {
auto calleeContextSize = function->ExpectedContextSize;
auto calleeContext = pushAsyncContext(task, executor, callerContext,
calleeContextSize, resumeFunction,
args...);
return function->Function(task, executor, calleeContext);
}
/// Given that that we've just entered the caller's continuation function
/// upon return from a function previously called with callAsync, pop the
/// callee's context and return the caller's context.
template <class CalleeContext>
static typename CalleeContext::CallerContext *
popAsyncContext(AsyncTask *task, CalleeContext *calleeContext) {
auto callerContext = calleeContext->getParent();
swift_task_dealloc(task, calleeContext);
return callerContext;
}
} // end anonymous namespace
} // end namespace swift
#endif