-
Notifications
You must be signed in to change notification settings - Fork 290
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
Rework OstreeAsyncProgress API to use GVariant #819
Conversation
OstreeAsyncProgress currently does some contortions to try and avoid allocating space for guints and guint64s (on 64-bit platforms), but this means it uses two GHashTables. A GHashTable allocates 8 buckets even when empty. Given that the largest usage of OstreeAsyncProgress in libostree puts 13 uints and 5 uint64s in it, this optimisation does not save significant (if any) memory. Instead, change OstreeAsyncProgress to store values internally as GVariants, and expose this with some new API: • ostree_async_progress_get_variant() • ostree_async_progress_set_variant() Each GVariant is allocated on the heap. As they are immutable, they are thread-safe once returned by a getter. The existing API continues to work as before, except in the case where a key is set/got as both a uint and a uint64 — there will now be a collision (and a GVariant type checking failure) whereas previously there was no collision. Nothing in OSTree uses OstreeAsyncProgress this way though. The new API can be used to share more complex data via the progress API. Signed-off-by: Philip Withnall <withnall@endlessm.com>
OstreeAsyncProgress is thread-safe: it can have keys changed by one thread while another is getting the same keys (modulo some locking contention). However, the thread safety is done at the function call level: if some code calls an OstreeAsyncProgress getter several times, the key fetches are not atomic with respect to each other. In the case of contention on the lock, this can result in consumers of OstreeAsyncProgress data seeing an inconsistent state between the properties they query, which could result in progress reporting inaccuracies. In the uncontested case, this results in the OstreeAsyncProgress lock being locked and unlocked many times more than necessary. Try to improve this by adding new API, which supports getting and setting multiple keys atomically: • ostree_async_progress_get() • ostree_async_progress_set() The new API uses GVariants and varargs: keys are passed as a GVariantType string followed by arguments as for g_variant_new() or g_variant_get(), followed by the next key, etc. Signed-off-by: Philip Withnall <withnall@endlessm.com>
This will eliminate most of the potential races in progress reporting. ostree_repo_pull_default_console_progress_changed() still calls three getters, so there may still be races there, however. Signed-off-by: Philip Withnall <withnall@endlessm.com>
Rework how the status is handled in OstreeAsyncProgress so that it’s now a well-known key in the hash table. This means that it can be retrieved and set atomically with other keys using ostree_async_progress_[get|set](). The behaviour of ostree_async_progress_[get|set]_status() is preserved, with the caveat that `status` can now also be accessed using the other API on OstreeAsyncProgress, and has to be accessed with the right GVariant type. Internally, a NULL status is represented by an empty status string (since ostree_async_progress_[get|set]_variant() deliberately don’t allow NULL variants to be set against keys, since that would break the ostree_async_progress_get() API). Signed-off-by: Philip Withnall <withnall@endlessm.com>
Use the new well-known `status` key for OstreeAsyncProgress to get and set the status atomically with other keys in an OstreeAsyncProgress instance. Signed-off-by: Philip Withnall <withnall@endlessm.com>
The flatpak failure isn't your fault, I submitted a PR to fix it. The travis one though looks real: I think we just need an Otherwise: Awesome! A++, would review again! |
OstreeAsyncProgress is thread-safe: it can have keys changed by one thread while another is getting the same keys (modulo some locking contention). However, the thread safety is done at the function call level: if some code calls an OstreeAsyncProgress getter several times, the key fetches are not atomic with respect to each other. In the case of contention on the lock, this can result in consumers of OstreeAsyncProgress data seeing an inconsistent state between the properties they query, which could result in progress reporting inaccuracies. In the uncontested case, this results in the OstreeAsyncProgress lock being locked and unlocked many times more than necessary. Try to improve this by adding new API, which supports getting and setting multiple keys atomically: • ostree_async_progress_get() • ostree_async_progress_set() The new API uses GVariants and varargs: keys are passed as a GVariantType string followed by arguments as for g_variant_new() or g_variant_get(), followed by the next key, etc. Signed-off-by: Philip Withnall <withnall@endlessm.com> Closes: #819 Approved by: cgwalters
This will eliminate most of the potential races in progress reporting. ostree_repo_pull_default_console_progress_changed() still calls three getters, so there may still be races there, however. Signed-off-by: Philip Withnall <withnall@endlessm.com> Closes: #819 Approved by: cgwalters
Rework how the status is handled in OstreeAsyncProgress so that it’s now a well-known key in the hash table. This means that it can be retrieved and set atomically with other keys using ostree_async_progress_[get|set](). The behaviour of ostree_async_progress_[get|set]_status() is preserved, with the caveat that `status` can now also be accessed using the other API on OstreeAsyncProgress, and has to be accessed with the right GVariant type. Internally, a NULL status is represented by an empty status string (since ostree_async_progress_[get|set]_variant() deliberately don’t allow NULL variants to be set against keys, since that would break the ostree_async_progress_get() API). Signed-off-by: Philip Withnall <withnall@endlessm.com> Closes: #819 Approved by: cgwalters
Use the new well-known `status` key for OstreeAsyncProgress to get and set the status atomically with other keys in an OstreeAsyncProgress instance. Signed-off-by: Philip Withnall <withnall@endlessm.com> Closes: #819 Approved by: cgwalters
☀️ Test successful - status-atomicjenkins |
I'm hitting an assertion with this:
This seems to happen from
|
If one of the progress keys is set in a pull operation, a ::changed signal is emitted on the progress object, and the callback for that could query any of the progress keys — so they all need to be set, otherwise we get an assertion failure in ostree_async_progress_get() due to a named key not existing. Spotted by Dan Nicholson in PR ostreedev#819. Signed-off-by: Philip Withnall <withnall@endlessm.com>
I can’t reproduce the exact crash you’re seeing, but I think I can see where the problem is. ⇒ #835. |
If one of the progress keys is set in a pull operation, a ::changed signal is emitted on the progress object, and the callback for that could query any of the progress keys — so they all need to be set, otherwise we get an assertion failure in ostree_async_progress_get() due to a named key not existing. Spotted by Dan Nicholson in PR #819. Signed-off-by: Philip Withnall <withnall@endlessm.com> Closes: #835 Approved by: cgwalters
I spent a little while today looking at different progress reporting APIs, and it turns out that despite @cgwalters’ reservations,
OstreeAsyncProgress
is pretty much ideal. I’ve pasted my analysis at the bottom of this PR.This PR adds a few missing features to
OstreeAsyncProgress
which will allow it to be used by the LAN/USB work (PR #782) to give structured information about each ref it’s pulling, for example, by passingGVariant
dicts in the progress information.The PR also improves the atomicity of accesses to an
OstreeAsyncProgress
instance, which fixes some potential bugs in reporting inconsistent pull progress.Options for progress reporting:
EvPrintOperation
: have an object for the entire operation, report progress via its properties or methodsFlatpakProgressCallback
,GFileProgressCallback
: caller provides a callback which has a fixed set of parametersOstreeAsyncProgress
: caller provides an object which has a notification callback associated with it