Skip to content

Fix actor/job/task leaks in the back deployment runtime. #59288

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
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
18 changes: 14 additions & 4 deletions stdlib/public/BackDeployConcurrency/Actor.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@
//
//===----------------------------------------------------------------------===//

#ifndef SWIFT_ABI_ACTOR_H
#define SWIFT_ABI_ACTOR_H
#ifndef SWIFT_ABI_ACTOR_BACKDEPLOYED_H
#define SWIFT_ABI_ACTOR_BACKDEPLOYED_H

#include "swift/ABI/HeapObject.h"
#include "swift/ABI/MetadataValues.h"
Expand All @@ -31,11 +31,21 @@ class alignas(Alignment_DefaultActor) DefaultActor : public HeapObject {
// destructor does not destroy the actor instance; you must call
// swift_defaultActor_{initialize,destroy} yourself.
constexpr DefaultActor(const HeapMetadata *metadata)
: HeapObject(metadata), PrivateData{} {}
: HeapObject(metadata), PrivateData{} {
initHeapObject();
}

constexpr DefaultActor(const HeapMetadata *metadata,
InlineRefCounts::Immortal_t immortal)
: HeapObject(metadata, immortal), PrivateData{} {}
: HeapObject(metadata, immortal), PrivateData{} {
// By a lucky coincidence, the modern bit pattern for immortal objects also
// sets the immortal bit for older runtimes, so we don't need an immortal
// equivalent to initHeapObject().
}

void initHeapObject() {
_swift_instantiateInertHeapObject(this, metadata);
}

void *PrivateData[NumWords_DefaultActor];
};
Expand Down
9 changes: 9 additions & 0 deletions stdlib/public/BackDeployConcurrency/Task.h
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ class alignas(2 * alignof(void*)) Job :
Job(JobFlags flags, JobInvokeFunction *invoke,
const HeapMetadata *metadata = &jobHeapMetadata)
: HeapObject(metadata), Flags(flags), RunJob(invoke) {
initHeapObject();
Voucher = voucher_copy();
assert(!isAsyncTask() && "wrong constructor for a task");
}
Expand All @@ -103,6 +104,7 @@ class alignas(2 * alignof(void*)) Job :
const HeapMetadata *metadata = &jobHeapMetadata,
bool captureCurrentVoucher = true)
: HeapObject(metadata), Flags(flags), ResumeTask(invoke) {
initHeapObject();
if (captureCurrentVoucher)
Voucher = voucher_copy();
assert(isAsyncTask() && "wrong constructor for a non-task job");
Expand All @@ -114,11 +116,18 @@ class alignas(2 * alignof(void*)) Job :
const HeapMetadata *metadata, InlineRefCounts::Immortal_t immortal,
bool captureCurrentVoucher = true)
: HeapObject(metadata, immortal), Flags(flags), ResumeTask(invoke) {
// By a lucky coincidence, the modern bit pattern for immortal objects also
// sets the immortal bit for older runtimes, so we don't need an immortal
// equivalent to initHeapObject().
if (captureCurrentVoucher)
Voucher = voucher_copy();
assert(isAsyncTask() && "wrong constructor for a non-task job");
}

void initHeapObject() {
_swift_instantiateInertHeapObject(this, metadata);
}

~Job() { swift_voucher_release(Voucher); }

bool isAsyncTask() const {
Expand Down
14 changes: 3 additions & 11 deletions test/Concurrency/Backdeploy/linking.swift
Original file line number Diff line number Diff line change
@@ -1,21 +1,13 @@
// RUN: %empty-directory(%t)
// RUN: %target-build-swift -target x86_64-apple-macosx12 %s -o %t/linking_direct
// RUN: %target-build-swift -target x86_64-apple-macosx11 %s -o %t/linking_rpath
// RUN: %target-build-swift -target x86_64-apple-macosx10.10 %s -o %t/linking_rpath_old
// RUN: %target-build-swift -target %target-cpu-apple-macosx12 %s -o %t/linking_direct
// RUN: %target-build-swift -target %target-cpu-apple-macosx11 %s -o %t/linking_rpath
// RUN: %target-build-swift -target %target-cpu-apple-macosx10.10 %s -o %t/linking_rpath_old

// RUN: otool -L %t/linking_direct | %FileCheck -check-prefix CHECK-DIRECT %s
// RUN: otool -L %t/linking_rpath | %FileCheck -check-prefix CHECK-RPATH %s
// RUN: otool -L %t/linking_rpath_old | %FileCheck -check-prefix CHECK-RPATH %s

// RUN: %target-build-swift -disable-autolinking-runtime-compatibility-concurrency -target x86_64-apple-ios15.0-macabi %s -o %t/linking_direct
// RUN: %target-build-swift -disable-autolinking-runtime-compatibility-concurrency -target x86_64-apple-ios14.0-macabi %s -o %t/linking_rpath

// RUN: otool -L %t/linking_direct | %FileCheck -check-prefix CHECK-DIRECT %s
// RUN: otool -L %t/linking_rpath | %FileCheck -check-prefix CHECK-RPATH %s

// REQUIRES: OS=macosx
// REQUIRES: CPU=x86_64
// REQUIRES: maccatalyst_support

// CHECK-DIRECT: /usr/lib/swift/libswift_Concurrency.dylib
// CHECK-RPATH: @rpath/libswift_Concurrency.dylib
Expand Down
28 changes: 28 additions & 0 deletions test/Concurrency/Backdeploy/linking_maccatalyst.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// RUN: %empty-directory(%t)
// RUN: %target-build-swift -target %target-cpu-apple-macosx12 %s -o %t/linking_direct
// RUN: %target-build-swift -target %target-cpu-apple-macosx11 %s -o %t/linking_rpath
// RUN: %target-build-swift -target %target-cpu-apple-macosx10.10 %s -o %t/linking_rpath_old

// RUN: otool -L %t/linking_direct | %FileCheck -check-prefix CHECK-DIRECT %s
// RUN: otool -L %t/linking_rpath | %FileCheck -check-prefix CHECK-RPATH %s
// RUN: otool -L %t/linking_rpath_old | %FileCheck -check-prefix CHECK-RPATH %s

// RUN: %target-build-swift -disable-autolinking-runtime-compatibility-concurrency -target %target-cpu-apple-ios15.0-macabi %s -o %t/linking_direct
// RUN: %target-build-swift -disable-autolinking-runtime-compatibility-concurrency -target %target-cpu-apple-ios14.0-macabi %s -o %t/linking_rpath

// RUN: otool -L %t/linking_direct | %FileCheck -check-prefix CHECK-DIRECT %s
// RUN: otool -L %t/linking_rpath | %FileCheck -check-prefix CHECK-RPATH %s

// REQUIRES: OS=macosx
// REQUIRES: maccatalyst_support

// CHECK-DIRECT: /usr/lib/swift/libswift_Concurrency.dylib
// CHECK-RPATH: @rpath/libswift_Concurrency.dylib

@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *)
public struct X {
public func f() async -> Int { return 0 }
public func g() async -> Int {
await f()
}
}
8 changes: 4 additions & 4 deletions test/Concurrency/Backdeploy/mangling.swift
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
// RUN: %empty-directory(%t)
// RUN: %target-swift-frontend %s -target x86_64-apple-macosx12.0 -module-name main -emit-ir -o %t/new.ir
// RUN: %target-swift-frontend %s -target %target-cpu-apple-macosx12.0 -module-name main -emit-ir -o %t/new.ir
// RUN: %FileCheck %s --check-prefix=NEW < %t/new.ir
// RUN: %target-swift-frontend %s -target x86_64-apple-macosx10.15 -module-name main -emit-ir -o %t/old.ir -disable-availability-checking
// RUN: %target-swift-frontend %s -target %target-cpu-apple-macosx10.15 -module-name main -emit-ir -o %t/old.ir -disable-availability-checking
// RUN: %FileCheck %s --check-prefix=OLD < %t/old.ir

// Check that we add extra type metadata accessors for new kinds of functions
// when back-deploying. These are used instead of using demangling cache
// variables since old runtimes cannot synthesize type metadata based on the
// new mangling.

// RUN: %target-build-swift -target x86_64-apple-macosx10.15 %s -o %t/test_mangling -Xfrontend -disable-availability-checking
// RUN: %target-build-swift -target %target-cpu-apple-macosx10.15 %s -o %t/test_mangling -Xfrontend -disable-availability-checking
// RUN: %target-run %t/test_mangling

// REQUIRES: CPU=x86_64
// REQUIRESx: CPU=x86_64
// REQUIRES: OS=macosx
// REQUIRES: executable_test
// REQUIRES: concurrency_runtime
Expand Down
3 changes: 1 addition & 2 deletions test/Concurrency/Backdeploy/objc_actor.swift
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
// RUN: %empty-directory(%t)
// RUN: %target-build-swift -target x86_64-apple-macosx10.15 %s -o %t/test_backdeploy -Xfrontend -parse-as-library
// RUN: %target-build-swift -target %target-cpu-apple-macosx11 %s -o %t/test_backdeploy -Xfrontend -parse-as-library
// RUN: %target-run %t/test_backdeploy

// REQUIRES: CPU=x86_64
// REQUIRES: OS=macosx
// REQUIRES: executable_test
// REQUIRES: concurrency_runtime
Expand Down
2 changes: 1 addition & 1 deletion test/Concurrency/Runtime/exclusivity.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
// REQUIRES: concurrency_runtime
// UNSUPPORTED: back_deployment_runtime
// UNSUPPORTED: OS=wasi
// UNSUPPORTED: use_os_stdlib
// UNSUPPORTED: back_deploy_concurrency

// This test makes sure that:
//
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,11 @@
// rdar://76038845
// UNSUPPORTED: back_deployment_runtime
// REQUIRES: concurrency_runtime
// UNSUPPORTED: back_deploy_concurrency

// Crash expectations can't be implemented on WASI/WebAssembly.
// UNSUPPORTED: OS=wasi

// UNSUPPORTED: use_os_stdlib

// This test makes sure that we properly save/restore access when we
// synchronously launch a task from a serial executor. The access from the task
// should be merged into the already created access set while it runs and then
Expand Down
34 changes: 34 additions & 0 deletions test/Concurrency/Runtime/task_destruction.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// RUN: %target-run-simple-swift(-parse-as-library -Xfrontend -disable-availability-checking) | %FileCheck %s
// REQUIRES: executable_test
// REQUIRES: concurrency
// REQUIRES: concurrency_runtime

// UNSUPPORTED: back_deployment_runtime

class C {}

@_silgen_name("exit")
func exit(_ code : UInt32) -> Never

@main
enum Main {
static func main() async {
weak var weakRef: C?
do {
let c = C()
let t = Task.detached { return c }
weakRef = c
}
Task.detached {
try await Task.sleep(nanoseconds: 10_000_000_000)
print("Fail!")
exit(1)
}

while weakRef != nil {
await Task.yield()
}
// CHECK: Success
print("Success!")
}
}
1 change: 1 addition & 0 deletions test/Concurrency/async_task_base_priority.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
// rdar://76038845
// REQUIRES: concurrency_runtime
// UNSUPPORTED: back_deployment_runtime
// UNSUPPORTED: back_deploy_concurrency

import StdlibUnittest
import Dispatch
Expand Down
1 change: 1 addition & 0 deletions test/Concurrency/async_task_priority.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
// rdar://76038845
// REQUIRES: concurrency_runtime
// UNSUPPORTED: back_deployment_runtime
// UNSUPPORTED: back_deploy_concurrency

import Darwin
@preconcurrency import Dispatch
Expand Down
12 changes: 10 additions & 2 deletions test/lit.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -1803,7 +1803,8 @@ if back_deployment_runtime is not None:

concurrency_back_deploy_path = ''
if run_vendor == 'apple':
if 'back_deploy_concurrency' in config.available_features:
if 'back_deploy_concurrency' in lit_config.params:
config.available_features.add('back_deploy_concurrency')
concurrency_back_deploy_path = os.path.join(os.path.dirname(swift_obj_root), os.path.basename(swift_obj_root).replace("swift-", "backdeployconcurrency-"), 'lib', 'swift-5.5', run_os)

def os_stdlib_paths():
Expand Down Expand Up @@ -2092,6 +2093,13 @@ if 'concurrency' in config.available_features:
config.available_features.add('concurrency_runtime')
elif 'back_deploy_concurrency' in config.available_features and run_vers >= concurrency_back_deploy_version:
config.available_features.add('concurrency_runtime')
elif 'back_deploy_concurrency' in config.available_features:
print('Disabled concurrency runtime tests because run version',
run_vers, "< back deploy version",
concurrency_back_deploy_version)
else:
print('Disable concurrency runtime tests because run version',
run_vers, 'is too old and back-deployment is not enabled')
else:
config.available_features.add('concurrency_runtime')

Expand All @@ -2116,7 +2124,7 @@ elif not kIsWindows:
if 'use_os_stdlib' in lit_config.params:
config.available_features.add('use_os_stdlib')

target_stdlib_path = os_stdlib_paths() + [concurrency_back_deploy_path] + target_stdlib_path
target_stdlib_path = [concurrency_back_deploy_path] + os_stdlib_paths() + target_stdlib_path

lit_config.note('Testing with the standard libraries in the OS')
elif 'back_deployment_runtime' in lit_config.params:
Expand Down