Skip to content

fix: Property wrappers are now thread safe #10

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 3 commits into from
Feb 5, 2025

Conversation

adrien-coye
Copy link
Contributor

@adrien-coye adrien-coye commented Jan 21, 2025

An interesting rare bug pointed me to the fact that DI resolution was thread safe but property wrappers were not.
This is fixing that. Now a class can expose a @LazyInjectService (or @InjectService), access it from any random thread concurrently and it won't cause a crash.

InjectService is made @unchecked Sendable.
LazyInjectService is now @unchecked Sendable with a GCD DispatchSemaphore as I need to express the Lazy part of it.

Reworked the PR to maintain a swift 5.8<->6.0 compatibility while maintaining thread safety.

This PR is now backward and forward compatible. Can be published as a 2.0.3

Copy link
Member

@PhilippeWeidmann PhilippeWeidmann left a comment

Choose a reason for hiding this comment

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

I'm wondering if Service now needs to be Sendable this will be breaking a change ?

Copy link

@adrien-coye
Copy link
Contributor Author

adrien-coye commented Jan 21, 2025

@PhilippeWeidmann I made changes to be certain this is not a breaking change in pure Swift6 mode while maintaining thread safety. In 5.8 mode, it was not an issue.

@adrien-coye adrien-coye added the bug Something isn't working label Jan 21, 2025
Copy link
Member

@PhilippeWeidmann PhilippeWeidmann left a comment

Choose a reason for hiding this comment

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

I did some tests (but not all use cases / all views) on the mail.
While I didn't see any direct performance impact I'm still not fond of potentially waiting on a semaphore on the main thread.

I think it would be better to have a new thread safe property wrapper to resolve the concurrency issues we have outside the main thread. We could also annotate @mainactor the LazyInjectService or create a new type only for the main actor.

I'm open to discussion

@adrien-coye
Copy link
Contributor Author

adrien-coye commented Feb 4, 2025

Open to discuss this tomorrow @PhilippeWeidmann
We do not wait unless an unlikely call is made at the exact same time from another thread.

It is super unlikely, it exists in one place on the Drive App.
It does not append on the Mail app to my knowledge as most things are @mainActor

If we do not wait during this unlikely event, this is a bug that breaks the "thread safety contract" of this library.

I'm open to create a @mainactor property wrapper if you want to migrate Mail on it, but that is a feature that should be done separately IMO.
Also, you can refrain from using the lazyInject if you want a resolution made at the exact init of the object in the same thread.

Maybe we should put in context how long you wait, only the time needed for the resolution to be performed.
The resolution is a O(1) operation (fixed time, dictionary object for key call).
Worst case the UI waits for a resolution, plus some latency, it should not stall the UI thread, or any thread.
We rely on this resolution massively in our apps, it is not stalling our apps today.

Finally, the resolution engine already runs on a serial queue, so technically today the main thread waits already.

If what you wish for is a nice Swift6 interface that prevents issues at compile time, this is not ready or built that way and requires a near complete rewrite.

@PhilippeWeidmann
Copy link
Member

After live review we will merge this PR as is. Property wrapper used in view bodies in Mail will be modified to not be lazy.

@adrien-coye adrien-coye merged commit f7724f3 into main Feb 5, 2025
14 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants