Skip to content

Commit f4e5b9e

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 f4e5b9e

File tree

3 files changed

+131
-0
lines changed

3 files changed

+131
-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: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
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_NO_INTEROP
12+
#if SWT_TARGET_OS_APPLE && !SWT_NO_OS_UNFAIR_LOCK
13+
private import _TestingInternals
14+
#else
15+
private import Synchronization
16+
#endif
17+
18+
#if SWT_TARGET_OS_APPLE && !SWT_NO_OS_UNFAIR_LOCK
19+
/// The installed event handler.
20+
private nonisolated(unsafe) let _fallbackEventHandler = {
21+
let result = ManagedBuffer<FallbackEventHandler?, os_unfair_lock>.create(
22+
minimumCapacity: 1,
23+
makingHeaderWith: { _ in nil }
24+
)
25+
result.withUnsafeMutablePointerToHeader { $0.initialize(to: nil) }
26+
return result
27+
}()
28+
#else
29+
/// The installed event handler.
30+
private nonisolated(unsafe) let _fallbackEventHandler = Atomic<UnsafeRawPointer?>(nil)
31+
#endif
32+
33+
/// A type describing a fallback event handler to invoke when testing API is
34+
/// used while the testing library is not running.
35+
///
36+
/// - Parameters:
37+
/// - recordJSONSchemaVersionNumber: The JSON schema version used to encode
38+
/// the event record.
39+
/// - recordJSONBaseAddress: A pointer to the first byte of the encoded event.
40+
/// - recordJSONByteCount: The size of the encoded event in bytes.
41+
/// - reserved: Reserved for future use.
42+
@usableFromInline
43+
package typealias FallbackEventHandler = @Sendable @convention(c) (
44+
_ recordJSONSchemaVersionNumber: UnsafePointer<CChar>,
45+
_ recordJSONBaseAddress: UnsafeRawPointer,
46+
_ recordJSONByteCount: Int,
47+
_ reserved: UnsafeRawPointer?
48+
) -> Void
49+
50+
/// Get the current fallback event handler.
51+
///
52+
/// - Returns: The currently-set handler function, if any.
53+
///
54+
/// - Important: This operation is thread-safe, but is not atomic with respect
55+
/// to calls to ``setFallbackEventHandler(_:)``. If you need to atomically
56+
/// exchange the previous value with a new value, call
57+
/// ``setFallbackEventHandler(_:)`` and store its returned value.
58+
@_cdecl("_swift_testing_getFallbackEventHandler")
59+
@usableFromInline
60+
package func _swift_testing_getFallbackEventHandler() -> FallbackEventHandler? {
61+
#if SWT_TARGET_OS_APPLE && !SWT_NO_OS_UNFAIR_LOCK
62+
return _fallbackEventHandler.withUnsafeMutablePointers { fallbackEventHandler, lock in
63+
os_unfair_lock_lock(lock)
64+
defer {
65+
os_unfair_lock_unlock(lock)
66+
}
67+
return fallbackEventHandler.pointee
68+
}
69+
#else
70+
return _fallbackEventHandler.load(ordering: .sequentiallyConsistent).flatMap { fallbackEventHandler in
71+
unsafeBitCast(fallbackEventHandler, to: FallbackEventHandler?.self)
72+
}
73+
#endif
74+
}
75+
76+
/// Set the current fallback event handler if one has not already been set.
77+
///
78+
/// - Parameters:
79+
/// - handler: The handler function to set.
80+
///
81+
/// - Returns: Whether or not `handler` was installed.
82+
///
83+
/// The fallback event handler can only be installed once per process, typically
84+
/// by the first testing library to run. If this function has already been
85+
/// called and the handler set, it does not replace the previous handler.
86+
@_cdecl("_swift_testing_installFallbackEventHandler")
87+
@usableFromInline
88+
package func _swift_testing_installFallbackEventHandler(_ handler: FallbackEventHandler) -> CBool {
89+
#if SWT_TARGET_OS_APPLE && !SWT_NO_OS_UNFAIR_LOCK
90+
return _fallbackEventHandler.withUnsafeMutablePointers { fallbackEventHandler, lock in
91+
os_unfair_lock_lock(lock)
92+
defer {
93+
os_unfair_lock_unlock(lock)
94+
}
95+
guard fallbackEventHandler.pointee == nil else {
96+
return false
97+
}
98+
fallbackEventHandler.pointee = handler
99+
return true
100+
}
101+
#else
102+
let handler = unsafeBitCast(handler, to: UnsafeRawPointer.self)
103+
return _fallbackEventHandler.compareExchange(expected: nil, desired: handler, ordering: .sequentiallyConsistent).exchanged
104+
#endif
105+
}
106+
#endif

0 commit comments

Comments
 (0)