I’m developing a postgres extension with Swift. Overall using Swift in this context been a good experience and once this issue is ironed out I’d be happy to open-source the work I’ve put into making Postgres extensions in Swift usable and performant.
My biggest issue is “random” segfaults that occur after swift::runtime::trace::setupLogs
is called on macOS. Depending on the timing (this is a race condition that seems to depend on when the extension code is called / loaded), I get a segfault with traces like this:
Exception Type: EXC_BAD_ACCESS (SIGSEGV)
Exception Subtype: KERN_INVALID_ADDRESS at 0x000000010520487d
Exception Codes: 0x0000000000000001, 0x000000010520487d
Termination Reason: Namespace SIGNAL, Code 11, Segmentation fault: 11
Terminating Process: exc handler [36640]
Application Specific Information:
crashed on child side of fork pre-exec
Thread 0 Crashed:
0 libsystem_trace.dylib 0x19669d0b4 _os_log_find + 64
1 libsystem_trace.dylib 0x19669cdd4 os_log_create + 304
2 libswiftCore.dylib 0x1a9a6d42c swift::runtime::trace::setupLogs(void*) + 44
3 libdispatch.dylib 0x1967ccaa4 _dispatch_client_callout + 16
4 libdispatch.dylib 0x1967b5a40 _dispatch_once_callout + 32
5 libswiftCore.dylib 0x1a9e6fba4 swift_conformsToProtocolMaybeInstantiateSuperclasses(swift::TargetMetadata<swift::InProcess> const*, swift::TargetProtocolDescriptor<swift::InProcess> const*, bool) (.cold.4) + 44
6 libswiftCore.dylib 0x1a9a652f4 swift_conformsToProtocolMaybeInstantiateSuperclasses(swift::TargetMetadata<swift::InProcess> const*, swift::TargetProtocolDescriptor<swift::InProcess> const*, bool) + 5216
7 libswiftCore.dylib 0x1a9a623a8 swift_conformsToProtocolWithExecutionContext + 72
8 libswiftCore.dylib 0x1a99f4f84 swift::_conformsToProtocol(swift::OpaqueValue const*, swift::TargetMetadata<swift::InProcess> const*, swift::TargetProtocolDescriptorRef<swift::InProcess>, swift::TargetWitnessTable<swift::InProcess> const**, swift::ConformanceExecutionContext*) + 152
9 libswiftCore.dylib 0x1a9a60214 swift::_checkGenericRequirements(__swift::__runtime::llvm::ArrayRef<swift::GenericParamDescriptor>, __swift::__runtime::llvm::ArrayRef<swift::TargetGenericRequirementDescriptor<swift::InProcess>>, __swift::__runtime::llvm::SmallVectorImpl<void const*>&, std::__1::function<void const* (unsigned int, unsigned int)>, std::__1::function<void const* (unsigned int, unsigned int)>, std::__1::function<swift::TargetWitnessTable<swift::InProcess> const* (swift::TargetMetadata<swift::InProcess> const*, unsigned int)>, swift::ConformanceExecutionContext*) + 9584
10 libswiftCore.dylib 0x1a9a45b2c _gatherGenericParameters(swift::TargetContextDescriptor<swift::InProcess> const*, __swift::__runtime::llvm::ArrayRef<swift::MetadataPackOrValue>, swift::TargetMetadata<swift::InProcess> const*, __swift::__runtime::llvm::SmallVectorImpl<unsigned int>&, __swift::__runtime::llvm::SmallVectorImpl<void const*>&, swift::Demangle::__runtime::Demangler&) + 2408
11 libswiftCore.dylib 0x1a9a53bb4 (anonymous namespace)::DecodedMetadataBuilder::createBoundGenericType(swift::TargetContextDescriptor<swift::InProcess> const*, __swift::__runtime::llvm::ArrayRef<swift::MetadataPackOrValue>, swift::MetadataPackOrValue) const + 264
12 libswiftCore.dylib 0x1a9a4fd98 swift::Demangle::__runtime::TypeDecoder<(anonymous namespace)::DecodedMetadataBuilder>::decodeMangledType(swift::Demangle::__runtime::Node*, unsigned int, bool) + 9016
13 libswiftCore.dylib 0x1a9a480cc swift_getTypeByMangledNodeImpl(swift::MetadataRequest, swift::Demangle::__runtime::Demangler&, swift::Demangle::__runtime::Node*, void const* const*, std::__1::function<void const* (unsigned int, unsigned int)>, std::__1::function<swift::TargetWitnessTable<swift::InProcess> const* (swift::TargetMetadata<swift::InProcess> const*, unsigned int)>) + 880
14 libswiftCore.dylib 0x1a9a439dc swift_getTypeByMangledNode + 368
15 libswiftCore.dylib 0x1a9a48b60 swift_getTypeByMangledNameImpl(swift::MetadataRequest, __swift::__runtime::llvm::StringRef, void const* const*, std::__1::function<void const* (unsigned int, unsigned int)>, std::__1::function<swift::TargetWitnessTable<swift::InProcess> const* (swift::TargetMetadata<swift::InProcess> const*, unsigned int)>) + 1204
16 libswiftCore.dylib 0x1a9a41520 swift_getTypeByMangledName + 368
17 libswiftCore.dylib 0x1a9a41a60 swift_getTypeByMangledNameInContextImpl(char const*, unsigned long, swift::TargetContextDescriptor<swift::InProcess> const*, void const* const*) + 192
18 libPerformanceAnalysis.dylib 0x10575ee04 __swift_instantiateConcreteTypeFromMangledName + 52
This crash does not occur on Linux because swift::runtime::trace::setupLogs
is not called there.
For some backstory: postgres seem to work by fork
ing worker processes but never runs exec
in those processes. They simply run to completion (or crash with a segfault, as the case may be). Apparently this puts us into dangerous “async signal unsafe” territory.
The issue is: I have no idea what that territory means or implies. I just want to write some Swift code that is called by postgres. I don’t want tracing or logs from Swift’s runtime.
Other than re-building the Swift compiler myself with SWIFT_STDLIB_TRACING
disabled (see swift/stdlib/public/runtime/Tracing.cpp at 9be05988fbf2f086758a507037c99cf281092da3 · swiftlang/swift · GitHub ) is there a “userland” workaround for this issue?
It has really drained my productivity and proven very difficult to track down and debug – the limited clarity I’m able to discuss here was gained after weeks of “random” crashes that were unexplainable. I also don’t know a huge amount about postgres’ internals and feel I shouldn’t need to, to get some compiled Swift code running.
Would it help to use Embedded Swift, for example?