Skip to content

Commit fda7e93

Browse files
committed
Create fallback event handler library
Minimal version of the changes in Jonathan's original PR here: swiftlang#1280 The new library is renamed to be "_TestingInterop" to more closely match its intended (and limited) purpose, but that's open to further discussion. This library will eventually be visible in the toolchain, and either testing library (Swift Testing or XCTest) will be able to use it to pass along unhandled issues to a test runner from the other framework, enabling interoperability.
1 parent c1be7ba commit fda7e93

File tree

3 files changed

+129
-0
lines changed

3 files changed

+129
-0
lines changed

Sources/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ endif()
104104
include(AvailabilityDefinitions)
105105
include(CompilerSettings)
106106
add_subdirectory(_TestDiscovery)
107+
add_subdirectory(_TestingInterop)
107108
add_subdirectory(_TestingInternals)
108109
add_subdirectory(Overlays)
109110
add_subdirectory(Testing)
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# This source file is part of the Swift.org open source project
2+
#
3+
# Copyright (c) 2025 Apple Inc. and the Swift project authors
4+
# Licensed under Apache License v2.0 with Runtime Library Exception
5+
#
6+
# See http://swift.org/LICENSE.txt for license information
7+
# See http://swift.org/CONTRIBUTORS.txt for Swift project authors
8+
9+
add_library(_TestingInterop
10+
FallbackEventHandler.swift)
11+
12+
target_link_libraries(_TestingInterop PRIVATE
13+
_TestingInternals)
14+
if(NOT BUILD_SHARED_LIBS)
15+
# When building a static library, tell clients to autolink the internal
16+
# libraries.
17+
target_compile_options(_TestingInterop PRIVATE
18+
"SHELL:-Xfrontend -public-autolink-library -Xfrontend _TestingInternals")
19+
endif()
20+
target_compile_options(_TestingInterop PRIVATE
21+
-enable-library-evolution
22+
-emit-module-interface -emit-module-interface-path $<TARGET_PROPERTY:_TestingInterop,Swift_MODULE_DIRECTORY>/_TestingInterop.swiftinterface)
23+
24+
_swift_testing_install_target(_TestingInterop)
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
//
2+
// This source file is part of the Swift.org open source project
3+
//
4+
// Copyright (c) 2025 Apple Inc. and the Swift project authors
5+
// Licensed under Apache License v2.0 with Runtime Library Exception
6+
//
7+
// See https://swift.org/LICENSE.txt for license information
8+
// See https://swift.org/CONTRIBUTORS.txt for Swift project authors
9+
//
10+
11+
#if SWT_TARGET_OS_APPLE && !SWT_NO_OS_UNFAIR_LOCK
12+
private import _TestingInternals
13+
#else
14+
private import Synchronization
15+
#endif
16+
17+
#if SWT_TARGET_OS_APPLE && !SWT_NO_OS_UNFAIR_LOCK
18+
/// The installed event handler.
19+
private nonisolated(unsafe) let _fallbackEventHandler = {
20+
let result = ManagedBuffer<FallbackEventHandler?, os_unfair_lock>.create(
21+
minimumCapacity: 1,
22+
makingHeaderWith: { _ in nil }
23+
)
24+
result.withUnsafeMutablePointerToHeader { $0.initialize(to: nil) }
25+
return result
26+
}()
27+
#else
28+
/// The installed event handler.
29+
private nonisolated(unsafe) let _fallbackEventHandler = Atomic<UnsafeRawPointer?>(nil)
30+
#endif
31+
32+
/// A type describing a fallback event handler to invoke when testing API is
33+
/// used while the testing library is not running.
34+
///
35+
/// - Parameters:
36+
/// - recordJSONSchemaVersionNumber: The JSON schema version used to encode
37+
/// the event record.
38+
/// - recordJSONBaseAddress: A pointer to the first byte of the encoded event.
39+
/// - recordJSONByteCount: The size of the encoded event in bytes.
40+
/// - reserved: Reserved for future use.
41+
@usableFromInline
42+
package typealias FallbackEventHandler = @Sendable @convention(c) (
43+
_ recordJSONSchemaVersionNumber: UnsafePointer<CChar>,
44+
_ recordJSONBaseAddress: UnsafeRawPointer,
45+
_ recordJSONByteCount: Int,
46+
_ reserved: UnsafeRawPointer?
47+
) -> Void
48+
49+
/// Get the current fallback event handler.
50+
///
51+
/// - Returns: The currently-set handler function, if any.
52+
///
53+
/// - Important: This operation is thread-safe, but is not atomic with respect
54+
/// to calls to ``setFallbackEventHandler(_:)``. If you need to atomically
55+
/// exchange the previous value with a new value, call
56+
/// ``setFallbackEventHandler(_:)`` and store its returned value.
57+
@_cdecl("_swift_testing_getFallbackEventHandler")
58+
@usableFromInline
59+
package func _swift_testing_getFallbackEventHandler() -> FallbackEventHandler? {
60+
#if SWT_TARGET_OS_APPLE && !SWT_NO_OS_UNFAIR_LOCK
61+
return _fallbackEventHandler.withUnsafeMutablePointers { fallbackEventHandler, lock in
62+
os_unfair_lock_lock(lock)
63+
defer {
64+
os_unfair_lock_unlock(lock)
65+
}
66+
return fallbackEventHandler.pointee
67+
}
68+
#else
69+
return _fallbackEventHandler.load(ordering: .sequentiallyConsistent).flatMap { fallbackEventHandler in
70+
unsafeBitCast(fallbackEventHandler, to: FallbackEventHandler?.self)
71+
}
72+
#endif
73+
}
74+
75+
/// Set the current fallback event handler if one has not already been set.
76+
///
77+
/// - Parameters:
78+
/// - handler: The handler function to set.
79+
///
80+
/// - Returns: Whether or not `handler` was installed.
81+
///
82+
/// The fallback event handler can only be installed once per process, typically
83+
/// by the first testing library to run. If this function has already been
84+
/// called and the handler set, it does not replace the previous handler.
85+
@_cdecl("_swift_testing_installFallbackEventHandler")
86+
@usableFromInline
87+
package func _swift_testing_installFallbackEventHandler(_ handler: FallbackEventHandler) -> CBool {
88+
#if SWT_TARGET_OS_APPLE && !SWT_NO_OS_UNFAIR_LOCK
89+
return _fallbackEventHandler.withUnsafeMutablePointers { fallbackEventHandler, lock in
90+
os_unfair_lock_lock(lock)
91+
defer {
92+
os_unfair_lock_unlock(lock)
93+
}
94+
guard fallbackEventHandler.pointee == nil else {
95+
return false
96+
}
97+
fallbackEventHandler.pointee = handler
98+
return true
99+
}
100+
#else
101+
let handler = unsafeBitCast(handler, to: UnsafeRawPointer.self)
102+
return _fallbackEventHandler.compareExchange(expected: nil, desired: handler, ordering: .sequentiallyConsistent).exchanged
103+
#endif
104+
}

0 commit comments

Comments
 (0)