//===--- 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 namespace swift { namespace { /// Template-metaprogrammed basic layout for the given sequence of types. template struct BasicLayout; template struct BasicLayout { static constexpr size_t size = StartingOffset; }; template struct BasicLayout { // 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; static constexpr size_t size = TailLayout::size; }; template struct BasicLayoutOffset { static constexpr size_t value = BasicLayoutOffset::value; }; template struct BasicLayoutOffset { 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 struct AsyncFrameLayout; template struct AsyncFrameLayout> { using BasicLayout = BasicLayout<0, SwiftError*, ArgTys...>; static constexpr size_t firstArgIndex = 1; }; template struct AsyncFrameLayout> { 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 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(this); } }; template > struct AsyncFrameStorage; template struct AsyncFrameStorage, FrameLayout> : AsyncFrameStorageHelper { AsyncFrameStorage(AsyncContextFlags flags, TaskContinuationFunction *resumeFunction, ExecutorRef resumeToExecutor, AsyncContext *resumeToContext, ArgTys... args) : AsyncFrameStorageHelper( flags, resumeFunction, resumeToExecutor, resumeToContext) { initializeHelper(this->data(), args...); } private: template void initializeHelper(char *buffer) {} template void initializeHelper(char *buffer, NextArgTy nextArg, TailArgTys... tailArgs) { auto offset = BasicLayoutOffset::value; new (buffer + offset) NextArgTy(nextArg); initializeHelper(buffer, tailArgs...); } }; /// The context header for calling a function that takes the /// given arguments. template struct AsyncCalleeContext : AsyncFrameStorage { using CallerContext = CallerContextType; template AsyncCalleeContext(TaskContinuationFunction *resumeFunction, ExecutorRef resumeToExecutor, CallerContext *resumeToContext, Args... args) : AsyncFrameStorage(AsyncContextKind::Ordinary, resumeFunction, resumeToExecutor, resumeToContext, args...) {} CallerContext *getParent() const { return static_cast(this->Parent); } }; /// Push a context to call a function. template static AsyncCalleeContext * pushAsyncContext(AsyncTask *task, ExecutorRef executor, CallerContext *callerContext, size_t calleeContextSize, TaskContinuationFunction *resumeFunction, Args... args) { using CalleeContext = AsyncCalleeContext; assert(calleeContextSize >= sizeof(CalleeContext)); void *rawCalleeContext = swift_task_alloc(task, calleeContextSize); return new (rawCalleeContext) CalleeContext(resumeFunction, executor, callerContext, args...); } /// Make an asynchronous call. template 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 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