-
Notifications
You must be signed in to change notification settings - Fork 205
[Proposal] ProgressManager
Post-Review Update
#1519
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
base: main
Are you sure you want to change the base?
Conversation
@swift-ci please test |
Proposals/0023-progress-manager.md
Outdated
We initially allowed the full flexibility of allowing developers to declare `ProgressManager.Property` types to be of any type, including structs. However, we realized that this has a severely negative impact on performance of the API. Thus, for now, we allow developers to only declare `ProgressManager.Property` with only certain `Value` and `Summary` types. | ||
|
||
### Introduce `withProperties` closure as an entry point to atomically mutate multiple properties | ||
We initially considered introducing a withProperties closure as an entry point to atomically mutate multiple properties. This approach would have used a dedicated ProgressManager.Values struct with `@dynamicMemberLookup` to provide a unified interface for modifying all properties within a single atomic operation. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We initially considered introducing a withProperties closure as an entry point to atomically mutate multiple properties. This approach would have used a dedicated ProgressManager.Values struct with `@dynamicMemberLookup` to provide a unified interface for modifying all properties within a single atomic operation. | |
We initially considered introducing a `withProperties` closure as an entry point to atomically mutate multiple properties. This approach would have used a dedicated `ProgressManager.Values` struct with `@dynamicMemberLookup` to provide a unified interface for modifying all properties within a single atomic operation. |
Proposals/0023-progress-manager.md
Outdated
We initially considered introducing a withProperties closure as an entry point to atomically mutate multiple properties. This approach would have used a dedicated ProgressManager.Values struct with `@dynamicMemberLookup` to provide a unified interface for modifying all properties within a single atomic operation. | ||
|
||
However, this design had several drawbacks: | ||
- Limited atomicity: While it allowed atomic mutation of `totalCount` and `completedCount`, it could not achieve the same atomicity for additional properties due to their differences in backing storage |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would add a few sentences here about why backing storage (an implementation detail) would affect this change of design. Why does it matter whether they're all stored into one single storage or multiple separate ones?
Either way, is it correct to say that if we are to enforce a whole atomicity for all of the additional properties, we would have to synchronize the access with a big lock for all of the properties, and would hinder performances?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For reasons related to backing storage, my line of thinking is that If we were to allow totalFileCount
and completedFileCount
to be stored in a struct the way totalCount
and completedCount
are, we should then also allow developers to do the same. But this will then then mean we have to allow developers to declare any ProgressManager.Property
, making us store (any Sendable)
. But for performance reasons, we do restrict them to only certain types such as Int
, UInt64
etc. I think it would be more about consistency between what our pre-declared custom properties can do vs what developers' custom properties can do.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why should an API user care about how totalFileCount
and completedFileCount
are stored, whether they're stored in a struct or not 😉 ? It could also be guarded by a semaphore for concurrent access. Would that allow atomically mutating everything?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm you're right, those are implementation details, I think the thing that would matter most to developers, especially UI developers, will be the performance cost that comes with having withProperties
. Because withProperties
acquires the lock and does the access and mutation of additional properties after, and releasing the lock before returning the values, the way we make these properties Observable
is much less performant. We have to register all access and mutations of additional properties onto only one keypath because the access
and withMutation
calls on the ObservationRegistrar
, which may cause unexpected and unnecessary redraws of the UI, putting performance cost on developers who opt to report additional properties with their progress via withProperties
.
Proposals/0023-progress-manager.md
Outdated
|
||
However, this design had several drawbacks: | ||
- Limited atomicity: While it allowed atomic mutation of `totalCount` and `completedCount`, it could not achieve the same atomicity for additional properties due to their differences in backing storage | ||
- Less intuitive API: The withProperties closure promoted a less natural pattern for accessing and mutating additional properties, particularly when combined with `@dynamicMemberLookup` on the nested `ProgressManager.Values` struct |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- Less intuitive API: The withProperties closure promoted a less natural pattern for accessing and mutating additional properties, particularly when combined with `@dynamicMemberLookup` on the nested `ProgressManager.Values` struct | |
- Less intuitive API: The `withProperties` closure promoted a less natural pattern for accessing and mutating additional properties, particularly when combined with `@dynamicMemberLookup` on the nested `ProgressManager.Values` struct |
Proposals/0023-progress-manager.md
Outdated
However, this design had several drawbacks: | ||
- Limited atomicity: While it allowed atomic mutation of `totalCount` and `completedCount`, it could not achieve the same atomicity for additional properties due to their differences in backing storage | ||
- Less intuitive API: The withProperties closure promoted a less natural pattern for accessing and mutating additional properties, particularly when combined with `@dynamicMemberLookup` on the nested `ProgressManager.Values` struct | ||
- Inconsistent access patterns: We would need to use different patterns for accessing fraction-related properties and additional properties |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Were there discussions or feedback in the current version that was supportive of this pattern? What was the justification back then, and are they no longer valid?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually the discussion that I had in mind was covered under less intuitive API, this is not needed.
Proposals/0023-progress-manager.md
Outdated
- Inconsistent access patterns: We would need to use different patterns for accessing fraction-related properties and additional properties | ||
|
||
We ultimately decided to replace this approach with a more streamlined design: | ||
- `setCounts` closure: Atomically update `totalCount` and `completedCount` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you clarify why only totalCount
and completedCount
are considered here, and why do you think that there needs to be a closure to atomically update both of them?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, sounds good!
Proposals/0023-progress-manager.md
Outdated
With this change, we can atomically update `totalCount` and `completedCount` via the `setCounts` closure, and access and mutate additional properties via dot syntax. | ||
|
||
### Minimal requirements for `ProgressManager.Property` protocol | ||
We initially considered a simpler version of the ProgressManager.Property protocol that required only two components: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We initially considered a simpler version of the ProgressManager.Property protocol that required only two components: | |
We initially considered a simpler version of the `ProgressManager.Property` protocol that had two requirements: |
I think we just call them requirements
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll change this!
Proposals/0023-progress-manager.md
Outdated
- `Value` - The type for individual property values | ||
- `defaultValue` - A default value when the property isn't explicitly set | ||
|
||
In this simplified approach, the framework would automatically: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you clarify what "the framework" refers to? Did you mean Foundation would do the below?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, but let me reword this!
@swift-ci please test |
Proposal change to reflect minor implementation change due to performance concerns of previously-proposed version of
ProgressManager
API.Updated implementation found here: #1468