stdlib/public/Concurrency/Task.cpp
/// All `swift_task_create*` variants funnel into this common implementation.
///
/// If \p isAsyncLetTask is true, the \p closureContext is not heap allocated,
/// but stack-allocated (and must not be ref-counted).
/// Also, async-let tasks are not heap allcoated, but allcoated with the parent
/// task's stack allocator.
All `swift_task_create*` variants funnel into this common implementation.
If \p isAsyncLetTask is true, the \p closureContext is not heap allocated,
but stack-allocated (and must not be ref-counted).
Also, async-let tasks are not heap allcoated, but allcoated with the parent
task's stack allocator.
All ` swift_task_create * ` variants funnel into this common implementation . If \ p isAsyncLetTask is true the \ p closureContext is not heap allocated but stack - allocated ( and must not be ref - counted ) . Also async - let tasks are not heap allcoated but allcoated with the parent task ' s stack allocator .
static AsyncTaskAndContext swift_task_create_group_future_commonImpl(
    size_t rawFlags, TaskGroup *group,
    const Metadata *futureResultType,
    FutureAsyncSignature::FunctionType *function,
    void *closureContext, bool isAsyncLetTask,
    size_t initialContextSize) {
  JobFlags flags(rawFlags);
  assert((futureResultType != nullptr) == flags.task_isFuture());
  assert(!flags.task_isFuture() ||
         initialContextSize >= sizeof(FutureAsyncContext));
  assert((group != nullptr) == flags.task_isGroupChildTask());
  AsyncTask *parent = nullptr;
  if (flags.task_isChildTask()) {
    parent = swift_task_getCurrent();
    assert(parent != nullptr && "creating a child task with no active task");
    if (flags.getPriority() == JobPriority::Unspecified)
      flags.setPriority(parent->getPriority());
  }
  size_t headerSize = sizeof(AsyncTask);
  if (parent) {
    headerSize += sizeof(AsyncTask::ChildFragment);
  }
  if (flags.task_isGroupChildTask()) {
    headerSize += sizeof(AsyncTask::GroupChildFragment);
  }
  if (futureResultType) {
    headerSize += FutureFragment::fragmentSize(futureResultType);
    headerSize += sizeof(FutureAsyncContextPrefix);
  } else {
    headerSize += sizeof(AsyncContextPrefix);
  }
  headerSize = llvm::alignTo(headerSize, llvm::Align(alignof(AsyncContext)));
  size_t amountToAllocate = headerSize + initialContextSize;
  assert(amountToAllocate % MaximumAlignment == 0);
  constexpr unsigned initialSlabSize = 512;
  void *allocation = nullptr;
  if (isAsyncLetTask) {
    assert(parent);
    allocation = _swift_task_alloc_specific(parent,
                                        amountToAllocate + initialSlabSize);
  } else {
    allocation = malloc(amountToAllocate);
  }
#if SWIFT_TASK_PRINTF_DEBUG
  fprintf(stderr, "[%lu] allocate task %p, parent = %p\n",
          _swift_get_thread_id(), allocation, parent);
#endif
  AsyncContext *initialContext =
    reinterpret_cast<AsyncContext*>(
      reinterpret_cast<char*>(allocation) + headerSize);
  if (!futureResultType) {
    auto asyncContextPrefix = reinterpret_cast<AsyncContextPrefix *>(
        reinterpret_cast<char *>(allocation) + headerSize -
        sizeof(AsyncContextPrefix));
    asyncContextPrefix->asyncEntryPoint =
        reinterpret_cast<AsyncVoidClosureEntryPoint *>(function);
    asyncContextPrefix->closureContext = closureContext;
    function = non_future_adapter;
    assert(sizeof(AsyncContextPrefix) == 3 * sizeof(void *));
  } else {
    auto asyncContextPrefix = reinterpret_cast<FutureAsyncContextPrefix *>(
        reinterpret_cast<char *>(allocation) + headerSize -
        sizeof(FutureAsyncContextPrefix));
    asyncContextPrefix->asyncEntryPoint =
        reinterpret_cast<AsyncGenericClosureEntryPoint *>(function);
    function = future_adapter;
    asyncContextPrefix->closureContext = closureContext;
    assert(sizeof(FutureAsyncContextPrefix) == 4 * sizeof(void *));
  }
  AsyncTask *task = nullptr;
  if (isAsyncLetTask) {
    task = new(allocation) AsyncTask(&taskHeapMetadata,
                             InlineRefCounts::Immortal, flags,
                             function, initialContext);
  } else {
    task = new(allocation) AsyncTask(&taskHeapMetadata, flags,
                                    function, initialContext);
  }
  if (parent) {
    auto childFragment = task->childFragment();
    new (childFragment) AsyncTask::ChildFragment(parent);
  }
  if (flags.task_isGroupChildTask()) {
    auto groupChildFragment = task->groupChildFragment();
    new (groupChildFragment) AsyncTask::GroupChildFragment(group);
  }
  if (futureResultType) {
    assert(task->isFuture());
    auto futureFragment = task->futureFragment();
    new (futureFragment) FutureFragment(futureResultType);
    auto futureAsyncContextPrefix =
        reinterpret_cast<FutureAsyncContextPrefix *>(
            reinterpret_cast<char *>(allocation) + headerSize -
            sizeof(FutureAsyncContextPrefix));
    futureAsyncContextPrefix->indirectResult = futureFragment->getStoragePtr();
  }
#if SWIFT_TASK_PRINTF_DEBUG
  fprintf(stderr, "[%lu] creating task %p with parent %p\n",
          _swift_get_thread_id(), task, parent);
#endif
  if (isAsyncLetTask) {
    initialContext->ResumeParent = reinterpret_cast<TaskContinuationFunction *>(
                                                   &completeTask);
    assert(parent);
    void *initialSlab = (char*)allocation + amountToAllocate;
    task->Private.initializeWithSlab(task, initialSlab, initialSlabSize);
  } else {
    initialContext->ResumeParent = reinterpret_cast<TaskContinuationFunction *>(
        closureContext ? &completeTaskWithClosure : &completeTaskAndRelease);
    task->Private.initialize(task);
  }
  if (parent) {
     In a task group we would not have allowed the `add` to create a child anymore,
     however better safe than sorry and `async let` are not expressed as task groups,
     so they may have been spawned in any case still.
    if (swift_task_isCancelled(parent))
      swift_task_cancel(task);
     Initialize task locals with a link to the parent task.
    task->_private().Local.initializeLinkParent(task, parent);
  }
   Configure the initial context.
   FIXME: if we store a null pointer here using the standard ABI for
   signed null pointers, then we'll have to authenticate context pointers
   as if they might be null, even though the only time they ever might
   be is the final hop.  Store a signed null instead.
  initialContext->Parent = nullptr;
  initialContext->Flags = AsyncContextKind::Ordinary;
  initialContext->Flags.setShouldNotDeallocateInCallee(true);
  return {task, initialContext};
}
static AsyncTaskAndContext swift_task_create_group_future_commonImpl ( size_t rawFlags , TaskGroup * group , const Metadata * futureResultType , FutureAsyncSignature :: FunctionType * function , void * closureContext , bool isAsyncLetTask , size_t initialContextSize ) { JobFlags flags ( rawFlags ) ; assert ( ( futureResultType != nullptr ) == flags . task_isFuture ( ) ) ; assert ( ! flags . task_isFuture ( ) || initialContextSize >= sizeof ( FutureAsyncContext ) ) ; assert ( ( group != nullptr ) == flags . task_isGroupChildTask ( ) ) ; AsyncTask * parent = nullptr ; if ( flags . task_isChildTask ( ) ) { parent = swift_task_getCurrent ( ) ; assert ( parent != nullptr && " " ) ; if ( flags . getPriority ( ) == JobPriority :: Unspecified ) flags . setPriority ( parent -> getPriority ( ) ) ; } size_t headerSize = sizeof ( AsyncTask ) ; if ( parent ) { headerSize += sizeof ( AsyncTask :: ChildFragment ) ; } if ( flags . task_isGroupChildTask ( ) ) { headerSize += sizeof ( AsyncTask :: GroupChildFragment ) ; } if ( futureResultType ) { headerSize += FutureFragment :: fragmentSize ( futureResultType ) ; headerSize += sizeof ( FutureAsyncContextPrefix ) ; } else { headerSize += sizeof ( AsyncContextPrefix ) ; } headerSize = llvm :: alignTo ( headerSize , llvm :: Align ( alignof ( AsyncContext ) ) ) ; size_t amountToAllocate = headerSize + initialContextSize ; assert ( amountToAllocate % MaximumAlignment == 0 ) ; constexpr unsigned initialSlabSize = 512 ; void * allocation = nullptr ; if ( isAsyncLetTask ) { assert ( parent ) ; allocation = _swift_task_alloc_specific ( parent , amountToAllocate + initialSlabSize ) ; } else { allocation = malloc ( amountToAllocate ) ; } #if SWIFT_TASK_PRINTF_DEBUG fprintf ( stderr , " \n " , _swift_get_thread_id ( ) , allocation , parent ) ; #endif AsyncContext * initialContext = reinterpret_cast < AsyncContext * > ( reinterpret_cast < char * > ( allocation ) + headerSize ) ; if ( ! futureResultType ) { auto asyncContextPrefix = reinterpret_cast < AsyncContextPrefix * > ( reinterpret_cast < char * > ( allocation ) + headerSize - sizeof ( AsyncContextPrefix ) ) ; asyncContextPrefix -> asyncEntryPoint = reinterpret_cast < AsyncVoidClosureEntryPoint * > ( function ) ; asyncContextPrefix -> closureContext = closureContext ; function = non_future_adapter ; assert ( sizeof ( AsyncContextPrefix ) == 3 * sizeof ( void * ) ) ; } else { auto asyncContextPrefix = reinterpret_cast < FutureAsyncContextPrefix * > ( reinterpret_cast < char * > ( allocation ) + headerSize - sizeof ( FutureAsyncContextPrefix ) ) ; asyncContextPrefix -> asyncEntryPoint = reinterpret_cast < AsyncGenericClosureEntryPoint * > ( function ) ; function = future_adapter ; asyncContextPrefix -> closureContext = closureContext ; assert ( sizeof ( FutureAsyncContextPrefix ) == 4 * sizeof ( void * ) ) ; } AsyncTask * task = nullptr ; if ( isAsyncLetTask ) { task = new ( allocation ) AsyncTask ( & taskHeapMetadata , InlineRefCounts :: Immortal , flags , function , initialContext ) ; } else { task = new ( allocation ) AsyncTask ( & taskHeapMetadata , flags , function , initialContext ) ; } if ( parent ) { auto childFragment = task -> childFragment ( ) ; new ( childFragment ) AsyncTask :: ChildFragment ( parent ) ; } if ( flags . task_isGroupChildTask ( ) ) { auto groupChildFragment = task -> groupChildFragment ( ) ; new ( groupChildFragment ) AsyncTask :: GroupChildFragment ( group ) ; } if ( futureResultType ) { assert ( task -> isFuture ( ) ) ; auto futureFragment = task -> futureFragment ( ) ; new ( futureFragment ) FutureFragment ( futureResultType ) ; auto futureAsyncContextPrefix = reinterpret_cast < FutureAsyncContextPrefix * > ( reinterpret_cast < char * > ( allocation ) + headerSize - sizeof ( FutureAsyncContextPrefix ) ) ; futureAsyncContextPrefix -> indirectResult = futureFragment -> getStoragePtr ( ) ; } #if SWIFT_TASK_PRINTF_DEBUG fprintf ( stderr , " \n " , _swift_get_thread_id ( ) , task , parent ) ; #endif if ( isAsyncLetTask ) { initialContext -> ResumeParent = reinterpret_cast < TaskContinuationFunction * > ( & completeTask ) ; assert ( parent ) ; void * initialSlab = ( char * ) allocation + amountToAllocate ; task -> Private . initializeWithSlab ( task , initialSlab , initialSlabSize ) ; } else { initialContext -> ResumeParent = reinterpret_cast < TaskContinuationFunction * > ( closureContext ? & completeTaskWithClosure : & completeTaskAndRelease ) ; task -> Private . initialize ( task ) ; } if ( parent ) { if ( swift_task_isCancelled ( parent ) ) swift_task_cancel ( task ) ; task -> _private ( ) . Local . initializeLinkParent ( task , parent ) ; } initialContext -> Parent = nullptr ; initialContext -> Flags = AsyncContextKind :: Ordinary ; initialContext -> Flags . setShouldNotDeallocateInCallee ( true ) ; return { task , initialContext } ; }
All `swift_task_create*` variants funnel into this common implementation.
All ` swift_task_create * ` variants funnel into this common implementation .
// Inherit the priority of the parent task if unspecified. // Figure out the size of the header. // Add the future async context prefix. // Add the async context prefix. // Allocate the initial context together with the job. // This means that we never get rid of this allocation. //  We can't just use `function` because it uses the new async function entry //  ABI -- passing parameters, closure context, indirect result addresses //  directly -- but AsyncTask->ResumeTask expects the signature to be //  `void (*, *, swiftasync *)`. //  Instead we use an adapter. This adaptor should use the storage prefixed to //  the async context to get at the parameters. //  See e.g. FutureAsyncContextPrefix. // Initialize the task so that resuming it will run the given // function on the initial context. // Initialize the refcount bits to "immortal", so that // ARC operations don't have any effect on the task. // Initialize the child fragment if applicable. // Initialize the group child fragment if applicable. // Initialize the future fragment if applicable. // Set up the context for the future so there is no error, and a successful // result will be written into the future fragment's storage. // Initialize the task-local allocator. // Perform additional linking between parent and child task. // If the parent was already cancelled, we carry this flag forward to the child. // // In a task group we would not have allowed the `add` to create a child anymore, // however better safe than sorry and `async let` are not expressed as task groups, // so they may have been spawned in any case still. // Initialize task locals with a link to the parent task. // Configure the initial context. // // FIXME: if we store a null pointer here using the standard ABI for // signed null pointers, then we'll have to authenticate context pointers // as if they might be null, even though the only time they ever might // be is the final hop.  Store a signed null instead.