Skip to content
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

Add a flag to parse serially #1063

Merged
merged 4 commits into from
May 11, 2022
Merged

Conversation

erichoracek
Copy link
Collaborator

@erichoracek erichoracek commented May 6, 2022

Parsing in parallel is leading to unexpected EXC_BAD_ACCESSes in our codebase. By switching to compactMap, the crashes go away.

The specific crash is deep in the internals of SwiftSyntax mid-parse, which leads me to believe that there's some internal global thread-unsafe state that's being shared between parser instances. I'm not sure if there's any guarantees about thread safety in SwiftSyntax, but I couldn't find any. Here's an example (abbreviated) stacktrace:

#0	0x00000001001209e4 in protocol witness for SyntaxProtocol._syntaxNode.getter in conformance Syntax ()
#1	0x0000000100121ec3 in SyntaxProtocol.raw.getter at /Users/eric_horacek/Library/Developer/Xcode/DerivedData/Sourcery-hibchsafsxkdcjaxxaylrbeyoydh/SourcePackages/checkouts/swift-syntax/Sources/SwiftSyntax/Syntax.swift:129
#2	0x0000000100b85c32 in SimpleTypeIdentifierSyntax.init(_:) at /Users/eric_horacek/Library/Developer/Xcode/DerivedData/Sourcery-hibchsafsxkdcjaxxaylrbeyoydh/SourcePackages/checkouts/swift-syntax/Sources/SwiftSyntax/gyb_generated/syntax_nodes/SyntaxTypeNodes.swift:68
#3	0x0000000100b89979 in protocol witness for SyntaxProtocol.init(_:) in conformance SimpleTypeIdentifierSyntax ()
#4	0x00000001001acab7 in TypeSyntax.as<τ_0_0>(_:) at /Users/eric_horacek/Library/Developer/Xcode/DerivedData/Sourcery-hibchsafsxkdcjaxxaylrbeyoydh/SourcePackages/checkouts/swift-syntax/Sources/SwiftSyntax/gyb_generated/SyntaxBaseNodes.swift:421
…
#165	0x0000000100566152 in SyntaxVisitor.visitImplCodeBlockItemSyntax(_:) at /Users/eric_horacek/Library/Developer/Xcode/DerivedData/Sourcery-hibchsafsxkdcjaxxaylrbeyoydh/SourcePackages/checkouts/swift-syntax/Sources/SwiftSyntax/gyb_generated/SyntaxVisitor.swift:2584
#166	0x00000001005abbca in SyntaxVisitor.visit(_:) at /Users/eric_horacek/Library/Developer/Xcode/DerivedData/Sourcery-hibchsafsxkdcjaxxaylrbeyoydh/SourcePackages/checkouts/swift-syntax/Sources/SwiftSyntax/gyb_generated/SyntaxVisitor.swift:5215
#167	0x00000001005b33bc in SyntaxVisitor.visitChildren<τ_0_0>(_:) at /Users/eric_horacek/Library/Developer/Xcode/DerivedData/Sourcery-hibchsafsxkdcjaxxaylrbeyoydh/SourcePackages/checkouts/swift-syntax/Sources/SwiftSyntax/gyb_generated/SyntaxVisitor.swift:5694
#168	0x0000000100566602 in SyntaxVisitor.visitImplCodeBlockItemListSyntax(_:) at /Users/eric_horacek/Library/Developer/Xcode/DerivedData/Sourcery-hibchsafsxkdcjaxxaylrbeyoydh/SourcePackages/checkouts/swift-syntax/Sources/SwiftSyntax/gyb_generated/SyntaxVisitor.swift:2595
#169	0x00000001005abc43 in SyntaxVisitor.visit(_:) at /Users/eric_horacek/Library/Developer/Xcode/DerivedData/Sourcery-hibchsafsxkdcjaxxaylrbeyoydh/SourcePackages/checkouts/swift-syntax/Sources/SwiftSyntax/gyb_generated/SyntaxVisitor.swift:5217
#170	0x00000001005b33bc in SyntaxVisitor.visitChildren<τ_0_0>(_:) at /Users/eric_horacek/Library/Developer/Xcode/DerivedData/Sourcery-hibchsafsxkdcjaxxaylrbeyoydh/SourcePackages/checkouts/swift-syntax/Sources/SwiftSyntax/gyb_generated/SyntaxVisitor.swift:5694
#171	0x0000000100583162 in SyntaxVisitor.visitImplSourceFileSyntax(_:) at /Users/eric_horacek/Library/Developer/Xcode/DerivedData/Sourcery-hibchsafsxkdcjaxxaylrbeyoydh/SourcePackages/checkouts/swift-syntax/Sources/SwiftSyntax/gyb_generated/SyntaxVisitor.swift:3673
#172	0x00000001005aea95 in SyntaxVisitor.visit(_:) at /Users/eric_horacek/Library/Developer/Xcode/DerivedData/Sourcery-hibchsafsxkdcjaxxaylrbeyoydh/SourcePackages/checkouts/swift-syntax/Sources/SwiftSyntax/gyb_generated/SyntaxVisitor.swift:5413
#173	0x0000000100557854 in SyntaxVisitor.walk<τ_0_0>(_:) at /Users/eric_horacek/Library/Developer/Xcode/DerivedData/Sourcery-hibchsafsxkdcjaxxaylrbeyoydh/SourcePackages/checkouts/swift-syntax/Sources/SwiftSyntax/gyb_generated/SyntaxVisitor.swift:32
#174	0x0000000100c35418 in FileParserSyntax.parse() at /Users/eric_horacek/Developer/Sourcery/SourceryFramework/Sources/Parsing/SwiftSyntax/FileParserSyntax.swift:51

I was able to reproduce this in the debugger, but there was no information available with either TSAN or ASAN.

Fixes #1009. With this change Sourcery 1.8.1 now works on our very large codebase where we run one instance of Sourcery per module. However it's important to note that this crash was occurring consistently on a single module—even if no other Sourcery processes were running concurrently.

I know that a lot of folks rely on the performance of parsing in parallel and it only seems to be crashing on our codebase, so perhaps we could put this behind an argument?

Parsing in parallel is leading to unexpected `EXC_BAD_ACCESS`es in our codebase.  By switching to `compactMap`, the crashes go away.

The specific crash is deep in the internals of `SwiftSyntax`, which leads me to believe that there's some internal global thread-unsafe state that's being shared bewteen parsing instances. Here's an example (abbreviated) stacktrace:
```
#0	0x00000001001209e4 in protocol witness for SyntaxProtocol._syntaxNode.getter in conformance Syntax ()
#1	0x0000000100121ec3 in SyntaxProtocol.raw.getter at /Users/eric_horacek/Library/Developer/Xcode/DerivedData/Sourcery-hibchsafsxkdcjaxxaylrbeyoydh/SourcePackages/checkouts/swift-syntax/Sources/SwiftSyntax/Syntax.swift:129
#2	0x0000000100b85c32 in SimpleTypeIdentifierSyntax.init(_:) at /Users/eric_horacek/Library/Developer/Xcode/DerivedData/Sourcery-hibchsafsxkdcjaxxaylrbeyoydh/SourcePackages/checkouts/swift-syntax/Sources/SwiftSyntax/gyb_generated/syntax_nodes/SyntaxTypeNodes.swift:68
#3	0x0000000100b89979 in protocol witness for SyntaxProtocol.init(_:) in conformance SimpleTypeIdentifierSyntax ()
#4	0x00000001001acab7 in TypeSyntax.as<τ_0_0>(_:) at /Users/eric_horacek/Library/Developer/Xcode/DerivedData/Sourcery-hibchsafsxkdcjaxxaylrbeyoydh/SourcePackages/checkouts/swift-syntax/Sources/SwiftSyntax/gyb_generated/SyntaxBaseNodes.swift:421
```

I was able to reproduce this in the debugger, but there was no information available with either TSAN or ASAN.

Fixes #1009.

I know that a lot of folks rely on the performance of parsing in parallel and it only seems to be crashing on our codebase, so perhaps we could put this behind an argument?
func incrementFileParsedCount() {
OSAtomicIncrement32(&numberOfFilesThatHadToBeParsed)
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When I ran TSAN on the codebase this increment was causing some violations to be printed, and they went away when I switched to a DispatchQueue.sync as a barrier. However even with that change I still encountered the crashes.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh that's really interesting, would not expect that for sure

@dfed
Copy link

dfed commented May 9, 2022

This change matches my expectations re working with SwiftSyntax. We currently do all SwiftSyntax work on main because we have encountered crashes in the past trying to use SwiftSyntax on background queues.

I do not believe that SwiftSyntax intends to be thread-unsafe (see forum post). But in my experience it is. Probably worth filing an issue on the repo

@krzysztofzablocki
Copy link
Owner

@dfed @erichoracek yeah I talked to @akyrtzi and he said the intention is to be thread-safe, and suggested using the debug build of the parser library to debug.

In the meantime could we add a flag for single threaded processing @erichoracek ? I can merge that as optional one but don't want to move whole threading model to single threaded since this crash doesn't happen often and it's a significant perf regression

@erichoracek
Copy link
Collaborator Author

@dfed @erichoracek yeah I talked to @akyrtzi and he said the intention is to be thread-safe, and suggested using the debug build of the parser library to debug.

Is there any easy way to run Sourcery with a debug build of SwiftSyntax?

In the meantime could we add a flag for single threaded processing @erichoracek ? I can merge that as optional one but don't want to move whole threading model to single threaded since this crash doesn't happen often and it's a significant perf regression

Sure, let me update the PR to include a flag for serial parsing

@erichoracek erichoracek changed the title Don't parse in parallel Add a flag to parse serially May 10, 2022
@akyrtzi
Copy link

akyrtzi commented May 10, 2022

https://github.com/apple/swift-syntax/tree/main/Documentation mentions instructions how to build lib_InternalSwiftSyntaxParser.dylib. Note that the swift repo that the parser library is compiled from needs to be from the same "Swift Release Tag" as the SwiftSyntax that is going to use it.
For investigation you could:

  • Build the parser library with assertions enabled
  • Modify the script that builds the library and also pass -DLLVM_USE_SANITIZER=Address for the CMake invocation. This would also build it with the address sanitizer.
  • Set to use the locally built parser library using DYLD_LIBRARY_PATH

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Threading crashes in Composer.resolveType
4 participants