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

Enable strict concurrency checking in CI #132

Merged
merged 3 commits into from Aug 15, 2023

Conversation

czechboy0
Copy link
Contributor

Summary

To help avoid concurrency bugs, this PR enables complete concurrency checking mode and addresses all the current concurrency warnings.

Some notable changes:

  • Moved some locking primitives into SPI of Instrumentation, to be usable in the rest of the package
  • Created a LockedValueBox type, used in several places to protect mutable state
  • added sendability annotations where it made sense
  • introduced a small workaround for accessing stdout/stderr (which emitted a warning, as it's a read-write global variable), using fdopen, which I think should be fine, but calling it out
  • reformatted using ./scripts/validate_format.sh, so might be worth reviewing the diff with ?w=1

Test Plan

Tested that all builds and tests pass on macOS and Linux.

@tomerd
Copy link
Member

tomerd commented Aug 11, 2023

@swift-server-bot add to allowlist

@ktoso ktoso self-requested a review August 14, 2023 02:27
@@ -27,13 +27,17 @@ final class ActorTracingTests: XCTestCase {
func work() async {}

actor Foo {
var bar = 0
let bar: LockedValueBox<Int> = .init(0)
Copy link
Member

@ktoso ktoso Aug 14, 2023

Choose a reason for hiding this comment

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

This part is why adding @sendable to the operation closure is wrong, the withSpan must not be sendable and thus the bar does not have to do synchronization -- this would make tracing a complete pain in actors otherwise.

I.e. this patch by adding Sendable here forces this locking or causes errors warning: actor-isolated property 'bar' can not be mutated from a Sendable closure; this is an error in Swift 6 which we must not be causing here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Right, when I remove sendable from the closures in withSpan, I get:

Passing argument of non-sendable type '(any Span) async -> ()' outside of actor-isolated context may introduce data races

and

Passing argument of non-sendable type '() -> ServiceContext' outside of actor-isolated context may introduce data races

One for each closure being passed in. How should we fix that?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Actually, is the answer here to duplicate the withSpan methods in extension Actor, which implement the methods again using the building blocks of startAnySpan etc? That seems to make the warnings go away for me.

Copy link
Member

Choose a reason for hiding this comment

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

But why would you want the operation to be sendable? We're always going to invoke it right away and never store it anywhere for "execute somewhere else"; they don't need to be sendable

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm not saying they should be sendable :) I'm trying to fix the warnings I pasted above, and now exploring the idea of the extension on Actor.

Copy link
Member

Choose a reason for hiding this comment

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

I'm a bit surprised the extension methods + Sendable does not cause warnings? Either way I think the answer here is just that operation closure just isn't sendable and that's ok 🆗 😄

Copy link
Contributor Author

@czechboy0 czechboy0 Aug 14, 2023

Choose a reason for hiding this comment

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

The problem is that this test will then produce warnings when complete concurrency checking is enabled. And client code that tries to do this as well. I think we need some solution here (even if making the closures sendable isn't the right one), the code, as written, produces warnings with strict concurrency checking, today.

Copy link
Member

Choose a reason for hiding this comment

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

We're backed into a corner here to be honest. Complete checking is strict, but the language does not offer right tools to solve this. The workaround (that even the stdlib uses) is done via @_unsafeInheritExecutor but that's incredibly unsafe and we cannot use it here -- we'd literarily be introducing real dataraces in correct looking code (this attribute removes ALL hop-to operations).

I'd be too worried to put it onto withSpan which is intended to wrap "entire functions" because it'll end up causing actual race conditions at runtime -- by now making any actor hops.

I think we must wait it out and get to the safe version that would become @inheritExecutor and only then would complete checking be correct here... (and in the stdlib as well for that matter).

As potential workaround, I have explored the "overload" on Actor but it also hits a number of problems. We could do this: 9484f4b

I talked this over with @FranzBusch and @Lukasa a bunch, and end up coming back to the safe @inheritExecutor whenever we hit those issues. This should probably inform the priority of finding a solution for these issues as we plan for enabling complete checking in the near future.

FYI @DougGregor @hborla @tomerd

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Understood - I'll back out the public API change and we should just land this PR with this warning, and address it separately. I wouldn't want to introduce new public API unless we want to maintain it long term.

Sources/Tracing/Tracer.swift Outdated Show resolved Hide resolved
Copy link
Member

@ktoso ktoso left a comment

Choose a reason for hiding this comment

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

Overall good, but we must not change the operation to be sendable, it breaks how tracing is intended to be used, comment here: https://github.com/apple/swift-distributed-tracing/pull/132/files#r1293083353

Once we revert changing operation closure types this should be okey. I'm also unsure why context closure would want to be sendable, seems iffy 🤔

@czechboy0
Copy link
Contributor Author

Thanks for the review, @ktoso, will address your feedback!

@czechboy0
Copy link
Contributor Author

@ktoso, ok I commented out the strict concurrency flags for now, but I think taking these changes would still be valuable. Once we have an answer for making withSpan safe, we can just uncomment the flags.

@ktoso
Copy link
Member

ktoso commented Aug 14, 2023

It's a good change over all, I'll give it another skim and merge.
Thank you @czechboy0 !

Related radars: rdar://113846321 rdar://113848512

@ktoso ktoso self-requested a review August 14, 2023 12:37
}

/// Returns the globally configured ``Instrument``.
///
/// Defaults to a no-op ``Instrument`` if ``bootstrap(_:)`` wasn't called before.
public static var instrument: Instrument {
self.lock.withReaderLock { self._instrument }
shared.instrument
}
}

@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *) // for TaskLocal ServiceContext
extension InstrumentationSystem {
/// INTERNAL API: Do Not Use
public static func _findInstrument(where predicate: (Instrument) -> Bool) -> Instrument? {
Copy link
Member

Choose a reason for hiding this comment

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

TODO: We can SPI this, I'll take that on :)

@ktoso
Copy link
Member

ktoso commented Aug 15, 2023

Looks good, thank you @czechboy0 !

And we'll follow up on those radars about strict checking and closures

@ktoso ktoso merged commit 632a837 into apple:main Aug 15, 2023
6 checks passed
@ktoso ktoso added this to the 1.0.2 milestone Aug 15, 2023
@czechboy0 czechboy0 deleted the hd-complete-concurrency-checking branch August 15, 2023 12:00
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.

None yet

3 participants