Skip to content

Make _swift_willThrow atomic #62349

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
1 change: 0 additions & 1 deletion stdlib/private/StdlibUnittest/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ add_swift_target_library(swiftStdlibUnittest ${SWIFT_STDLIB_LIBRARY_BUILD_TYPES}
StdlibCoreExtras.swift
StringConvertible.swift
StringTestHelpers.swift
SymbolLookup.swift
TestHelpers.swift
TypeIndexed.swift

Expand Down
55 changes: 0 additions & 55 deletions stdlib/private/StdlibUnittest/SymbolLookup.swift

This file was deleted.

13 changes: 9 additions & 4 deletions stdlib/public/runtime/ErrorObjectCommon.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,20 @@

using namespace swift;

void (*swift::_swift_willThrow)(SwiftError *error);
std::atomic<void (*)(SwiftError *error)> swift::_swift_willThrow;

void swift::_swift_setWillThrowHandler(void (* handler)(SwiftError *error)) {
_swift_willThrow.store(handler, std::memory_order_release);
}

/// Breakpoint hook for debuggers, and calls _swift_willThrow if set.
SWIFT_CC(swift) void
swift::swift_willThrow(SWIFT_CONTEXT void *unused,
SWIFT_ERROR_RESULT SwiftError **error) {
// Cheap check to bail out early, since we expect there to be no callbacks
// the vast majority of the time.
if (SWIFT_LIKELY(!_swift_willThrow))
return;
_swift_willThrow(*error);
auto handler = _swift_willThrow.load(std::memory_order_acquire);
if (SWIFT_UNLIKELY(handler)) {
(* handler)(*error);
}
}
12 changes: 11 additions & 1 deletion stdlib/public/runtime/ErrorObjectTestSupport.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,20 @@
#ifndef SWIFT_RUNTIME_ERROROBJECT_TEST_SUPPORT_H
#define SWIFT_RUNTIME_ERROROBJECT_TEST_SUPPORT_H

#include <atomic>

namespace swift {

SWIFT_RUNTIME_EXPORT void (*_swift_willThrow)(SwiftError *error);
#if defined(__cplusplus)
SWIFT_RUNTIME_EXPORT std::atomic<void (*)(SwiftError *error)> _swift_willThrow;
#endif

/// Set the value of @c _swift_willThrow atomically.
///
/// This function is present for use by the standard library's test suite only.
SWIFT_CC(swift)
SWIFT_RUNTIME_STDLIB_SPI
void _swift_setWillThrowHandler(void (* handler)(SwiftError *error));
}

#endif
47 changes: 27 additions & 20 deletions test/stdlib/Error.swift
Original file line number Diff line number Diff line change
Expand Up @@ -219,28 +219,35 @@ func throwJazzHands() throws {
throw SillyError.JazzHands
}

ErrorTests.test("willThrow") {
if #available(SwiftStdlib 5.2, *) {
// Error isn't allowed in a @convention(c) function when ObjC interop is
// not available, so pass it through an OpaquePointer.
typealias WillThrow = @convention(c) (OpaquePointer) -> Void
let willThrow = pointerToSwiftCoreSymbol(name: "_swift_willThrow")!
let callback: WillThrow = {
errors.append(unsafeBitCast($0, to: Error.self))
}
willThrow.storeBytes(of: callback, as: WillThrow.self)
expectTrue(errors.isEmpty)
do {
try throwNegativeOne()
} catch {}
expectEqual(UnsignedError.self, type(of: errors.last!))
// Error isn't allowed in a @convention(c) function when ObjC interop is
// not available, so pass it through an UnsafeRawPointer.
@available(SwiftStdlib 5.8, *)
@_silgen_name("_swift_setWillThrowHandler")
func setWillThrowHandler(
_ handler: (@convention(c) (UnsafeRawPointer) -> Void)?
)

do {
try throwJazzHands()
} catch {}
expectEqual(2, errors.count)
expectEqual(SillyError.self, type(of: errors.last!))
ErrorTests.test("willThrow") {
guard #available(SwiftStdlib 5.8, *) else {
return
}
setWillThrowHandler {
errors.append(unsafeBitCast($0, to: Error.self))
}
defer {
setWillThrowHandler(nil)
}
expectTrue(errors.isEmpty)
do {
try throwNegativeOne()
} catch {}
expectEqual(UnsignedError.self, type(of: errors.last!))

do {
try throwJazzHands()
} catch {}
expectEqual(2, errors.count)
expectEqual(SillyError.self, type(of: errors.last!))
}

runAllTests()
Expand Down