Skip to content

[Concurrency] Build executors as their own separate object modules. #76483

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Oct 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 31 additions & 61 deletions include/swift/Runtime/Concurrency.h
Original file line number Diff line number Diff line change
Expand Up @@ -720,6 +720,13 @@ void swift_task_enqueueGlobal(Job *job);
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
void swift_task_checkIsolated(SerialExecutorRef executor);

/// Invoke a Swift executor's `checkIsolated` implementation; returns
/// `true` if it invoked the Swift implementation, `false` otherwise.
/// Executors will want to call this from their `swift_task_checkIsolatedImpl`
/// implementation.
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
bool swift_task_invokeSwiftCheckIsolated(SerialExecutorRef executor);

/// A count in nanoseconds.
using JobDelay = unsigned long long;

Expand Down Expand Up @@ -760,64 +767,22 @@ void swift_task_enqueueOnDispatchQueue(Job *job, HeapObject *queue);

#endif

/// A hook to take over global enqueuing.
typedef SWIFT_CC(swift) void (*swift_task_enqueueGlobal_original)(Job *job);
SWIFT_EXPORT_FROM(swift_Concurrency)
SWIFT_CC(swift) void (*swift_task_enqueueGlobal_hook)(
Job *job, swift_task_enqueueGlobal_original original);
// Declare all the hooks
#define SWIFT_CONCURRENCY_HOOK(returnType, name, ...) \
typedef SWIFT_CC(swift) returnType (*name##_original)(__VA_ARGS__); \
typedef SWIFT_CC(swift) returnType \
(*name##_hook_t)(__VA_ARGS__, name##_original original); \
SWIFT_EXPORT_FROM(swift_Concurrency) name##_hook_t name##_hook

/// A hook to take over global enqueuing with delay.
typedef SWIFT_CC(swift) void (*swift_task_enqueueGlobalWithDelay_original)(
unsigned long long delay, Job *job);
SWIFT_EXPORT_FROM(swift_Concurrency)
SWIFT_CC(swift) void (*swift_task_enqueueGlobalWithDelay_hook)(
unsigned long long delay, Job *job,
swift_task_enqueueGlobalWithDelay_original original);

typedef SWIFT_CC(swift) void (*swift_task_enqueueGlobalWithDeadline_original)(
long long sec,
long long nsec,
long long tsec,
long long tnsec,
int clock, Job *job);
SWIFT_EXPORT_FROM(swift_Concurrency)
SWIFT_CC(swift) void (*swift_task_enqueueGlobalWithDeadline_hook)(
long long sec,
long long nsec,
long long tsec,
long long tnsec,
int clock, Job *job,
swift_task_enqueueGlobalWithDeadline_original original);

typedef SWIFT_CC(swift) void (*swift_task_checkIsolated_original)(SerialExecutorRef executor);
SWIFT_EXPORT_FROM(swift_Concurrency)
SWIFT_CC(swift) void (*swift_task_checkIsolated_hook)(
SerialExecutorRef executor, swift_task_checkIsolated_original original);
#define SWIFT_CONCURRENCY_HOOK0(returnType, name) \
typedef SWIFT_CC(swift) returnType (*name##_original)(); \
typedef SWIFT_CC(swift) returnType \
(*name##_hook_t)(name##_original original); \
SWIFT_EXPORT_FROM(swift_Concurrency) name##_hook_t name##_hook

#include "ConcurrencyHooks.def"

typedef SWIFT_CC(swift) bool (*swift_task_isOnExecutor_original)(
HeapObject *executor,
const Metadata *selfType,
const SerialExecutorWitnessTable *wtable);
SWIFT_EXPORT_FROM(swift_Concurrency)
SWIFT_CC(swift) bool (*swift_task_isOnExecutor_hook)(
HeapObject *executor,
const Metadata *selfType,
const SerialExecutorWitnessTable *wtable,
swift_task_isOnExecutor_original original);

/// A hook to take over main executor enqueueing.
typedef SWIFT_CC(swift) void (*swift_task_enqueueMainExecutor_original)(
Job *job);
SWIFT_EXPORT_FROM(swift_Concurrency)
SWIFT_CC(swift) void (*swift_task_enqueueMainExecutor_hook)(
Job *job, swift_task_enqueueMainExecutor_original original);

/// A hook to override the entrypoint to the main runloop used to drive the
/// concurrency runtime and drain the main queue. This function must not return.
/// Note: If the hook is wrapping the original function and the `compatOverride`
/// is passed in, the `original` function pointer must be passed into the
/// compatibility override function as the original function.
// This is a compatibility hook, *not* a concurrency hook
typedef SWIFT_CC(swift) void (*swift_task_asyncMainDrainQueue_original)();
typedef SWIFT_CC(swift) void (*swift_task_asyncMainDrainQueue_override)(
swift_task_asyncMainDrainQueue_original original);
Expand Down Expand Up @@ -913,11 +878,16 @@ extern "C" SWIFT_CC(swift)
void swift_continuation_logFailedCheck(const char *message);

/// Drain the queue
/// If the binary links CoreFoundation, uses CFRunLoopRun
/// Otherwise it uses dispatchMain.
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
void swift_task_asyncMainDrainQueue [[noreturn]]();

/// Drain the global executor. This does the same as the above, but
/// swift_task_asyncMainDrainQueue() is a compatibility override point,
/// whereas this function has a concurrency hook. The default
/// swift_task_asyncMainDrainQueue() implementation just calls this function.
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
void swift_task_drainGlobalExecutor [[noreturn]]();

/// Establish that the current thread is running as the given
/// executor, then run a job.
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
Expand Down Expand Up @@ -951,6 +921,10 @@ SerialExecutorRef swift_task_getCurrentExecutor(void);
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
SerialExecutorRef swift_task_getMainExecutor(void);

/// Test if an executor is the main executor.
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
bool swift_task_isMainExecutor(SerialExecutorRef executor);

/// Return the preferred task executor of the current task,
/// or ``TaskExecutorRef::undefined()`` if no preference.
///
Expand Down Expand Up @@ -984,17 +958,13 @@ JobPriority swift_task_getCurrentThreadPriority(void);
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
void swift_task_startOnMainActor(AsyncTask* job);

#if SWIFT_CONCURRENCY_COOPERATIVE_GLOBAL_EXECUTOR

/// Donate this thread to the global executor until either the
/// given condition returns true or we've run out of cooperative
/// tasks to run.
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
void swift_task_donateThreadToGlobalExecutorUntil(bool (*condition)(void*),
void *context);

#endif

enum swift_clock_id : int {
swift_clock_id_continuous = 1,
swift_clock_id_suspending = 2
Expand Down
67 changes: 67 additions & 0 deletions include/swift/Runtime/ConcurrencyHooks.def
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
//===--- Concurrency.h - Runtime interface for concurrency ------*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2024 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
//
//===----------------------------------------------------------------------===//
//
// Hooks for concurrency
//
//===----------------------------------------------------------------------===//

// To use this file, define the following macros:
//
// SWIFT_CONCURRENCY_HOOK(returnType, name, ...)
// SWIFT_CONCURRENCY_HOOK0(returnType, name)
// SWIFT_CONCURRENCY_HOOK_OVERRIDE0(returnType, name)
//
// Then include the file somewhere.

#ifndef SWIFT_CONCURRENCY_HOOK
#define SWIFT_CONCURRENCY_HOOK(returnType, name, ...)
#endif

#ifndef SWIFT_CONCURRENCY_HOOK0
#define SWIFT_CONCURRENCY_HOOK0(returnType, name)
#endif

// .............................................................................

SWIFT_CONCURRENCY_HOOK(void, swift_task_enqueueGlobal, Job *job);

SWIFT_CONCURRENCY_HOOK(void, swift_task_enqueueGlobalWithDelay,
unsigned long long delay, Job *job);

SWIFT_CONCURRENCY_HOOK(void, swift_task_enqueueGlobalWithDeadline,
long long sec,
long long nsec,
long long tsec,
long long tnsec,
int clock, Job *job);

SWIFT_CONCURRENCY_HOOK(void, swift_task_checkIsolated, SerialExecutorRef executor);

SWIFT_CONCURRENCY_HOOK(bool, swift_task_isOnExecutor,
HeapObject *executor,
const Metadata *selfType,
const SerialExecutorWitnessTable *wtable);

SWIFT_CONCURRENCY_HOOK(void, swift_task_enqueueMainExecutor, Job *job);

SWIFT_CONCURRENCY_HOOK0(SerialExecutorRef, swift_task_getMainExecutor);

SWIFT_CONCURRENCY_HOOK(bool, swift_task_isMainExecutor, SerialExecutorRef);

SWIFT_CONCURRENCY_HOOK(void, swift_task_donateThreadToGlobalExecutorUntil,
bool (*condition)(void *), void *context);

// .............................................................................

#undef SWIFT_CONCURRENCY_HOOK
#undef SWIFT_CONCURRENCY_HOOK0
#undef SWIFT_CONCURRENCY_HOOK_OVERRIDE0
3 changes: 2 additions & 1 deletion stdlib/public/Concurrency/Actor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
#define SWIFT_OBJC_INTEROP 1
#endif
#include "llvm/ADT/PointerIntPair.h"

#include "TaskPrivate.h"
#include "VoucherSupport.h"

Expand Down Expand Up @@ -2239,7 +2240,7 @@ static void swift_task_enqueueImpl(Job *job, SerialExecutorRef serialExecutorRef
}
SWIFT_TASK_DEBUG_LOG("enqueue job %p on serial serialExecutor %p, taskExecutor = %p", job,
serialExecutorRef.getIdentity(),
__taskExecutorRef.getIdentity());
_taskExecutorRef.getIdentity());
#endif

assert(job && "no job provided");
Expand Down
53 changes: 48 additions & 5 deletions stdlib/public/Concurrency/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,6 @@ else()
message(FATAL_ERROR "Invalid value for SWIFT_CONCURRENCY_GLOBAL_EXECUTOR (\"${SWIFT_CONCURRENCY_GLOBAL_EXECUTOR}\").")
endif()

if(NOT SWIFT_CONCURRENCY_USES_DISPATCH)

endif()

if(NOT swift_concurrency_async_fp_mode)
set(swift_concurrency_async_fp_mode "always")
endif()
Expand Down Expand Up @@ -78,8 +74,10 @@ set(SWIFT_RUNTIME_CONCURRENCY_C_SOURCES
AsyncLet.cpp
Clock.cpp
GlobalExecutor.cpp
ConcurrencyHooks.cpp
EmbeddedSupport.cpp
Error.cpp
ExecutorChecks.cpp
Setup.cpp
Task.cpp
TaskAlloc.cpp
Expand All @@ -92,6 +90,27 @@ set(SWIFT_RUNTIME_CONCURRENCY_C_SOURCES
linker-support/magic-symbols-for-install-name.c
)

if("${SWIFT_CONCURRENCY_GLOBAL_EXECUTOR}" STREQUAL "dispatch")
set(SWIFT_RUNTIME_CONCURRENCY_EXECUTOR_SOURCES
DispatchGlobalExecutor.cpp
)
elseif("${SWIFT_CONCURRENCY_GLOBAL_EXECUTOR}" STREQUAL "singlethreaded")
set(SWIFT_RUNTIME_CONCURRENCY_EXECUTOR_SOURCES
CooperativeGlobalExecutor.cpp
)
elseif("${SWIFT_CONCURRENCY_GLOBAL_EXECUTOR}" STREQUAL "hooked" OR
"${SWIFT_CONCURRENCY_GLOBAL_EXECUTOR}" STREQUAL "none")
set(SWIFT_RUNTIME_CONCURRENCY_EXECUTOR_SOURCES
NonDispatchGlobalExecutor.cpp
)
endif()

set(LLVM_OPTIONAL_SOURCES
CooperativeGlobalExecutor.cpp
DispatchGlobalExecutor.cpp
NonDispatchGlobalExecutor.cpp
)

set(SWIFT_RUNTIME_CONCURRENCY_SWIFT_SOURCES
Actor.swift
AsyncLet.swift
Expand Down Expand Up @@ -158,6 +177,7 @@ set(SWIFT_RUNTIME_CONCURRENCY_SWIFT_SOURCES

add_swift_target_library(swift_Concurrency ${SWIFT_STDLIB_LIBRARY_BUILD_TYPES} IS_STDLIB
${SWIFT_RUNTIME_CONCURRENCY_C_SOURCES}
${SWIFT_RUNTIME_CONCURRENCY_EXECUTOR_SOURCES}
${SWIFT_RUNTIME_CONCURRENCY_SWIFT_SOURCES}

SWIFT_MODULE_DEPENDS_ANDROID Android
Expand Down Expand Up @@ -230,7 +250,7 @@ if(SWIFT_SHOULD_BUILD_EMBEDDED_STDLIB AND SWIFT_SHOULD_BUILD_EMBEDDED_CONCURRENC
set(extra_c_compile_flags -D__MACH__ -D__APPLE__ -ffreestanding)
set(extra_swift_compile_flags -Xcc -D__MACH__ -Xcc -D__APPLE__ -Xcc -ffreestanding)
endif()

set(SWIFT_SDK_embedded_THREADING_PACKAGE none)
set(SWIFT_SDK_embedded_ARCH_${mod}_MODULE "${mod}")
set(SWIFT_SDK_embedded_LIB_SUBDIR "embedded")
Expand All @@ -243,6 +263,8 @@ if(SWIFT_SHOULD_BUILD_EMBEDDED_STDLIB AND SWIFT_SHOULD_BUILD_EMBEDDED_CONCURRENC
IS_STDLIB IS_FRAGILE

${SWIFT_RUNTIME_CONCURRENCY_C_SOURCES}
CooperativeGlobalExecutor.cpp

# TODO: Only a subset of Swift Concurrency .swift sources, for now.
Actor.swift
AsyncLet.swift
Expand Down Expand Up @@ -298,4 +320,25 @@ if(SWIFT_SHOULD_BUILD_EMBEDDED_STDLIB AND SWIFT_SHOULD_BUILD_EMBEDDED_CONCURRENC

add_dependencies(embedded-concurrency embedded-concurrency-${mod})
endforeach()

# Copy the ExecutorImpl.h header into the local include directory
# and install it in the compiler toolchain
add_custom_command(
OUTPUT "${SWIFT_INCLUDE_DIR}/swift/ExecutorImpl.h"
DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/ExecutorImpl.h"
COMMAND "${CMAKE_COMMAND}" "-E" "copy"
"${CMAKE_CURRENT_SOURCE_DIR}/ExecutorImpl.h"
"${SWIFT_INCLUDE_DIR}/swift")

add_custom_target("copy_executor_impl_header"
DEPENDS "${SWIFT_INCLUDE_DIR}/swift/ExecutorImpl.h"
COMMENT "Copying executor implementation header to ${SWIFT_INCLUDE_DIR}/swift")

swift_install_in_component(FILES
"${CMAKE_CURRENT_SOURCE_DIR}/ExecutorImpl.h"
DESTINATION "include/swift"
COMPONENT compiler)

add_dependencies(embedded-concurrency "copy_executor_impl_header")
endif()

Loading