563f1b773a
When `update()` is called while another update (typically the automatic updater thread) is already running, the Rust updater previously bailed with `UpdateError::UpdateAlreadyInProgress`, which surfaced in Dart as `UpdateException: Update already in progress (unknown)`. This is the single highest-volume `UpdateException` in customer telemetry, yet the underlying situation is benign — the in-flight update continues on its own, the caller simply did not start a new one. Add a new `UpdateStatus::UpdateInProgress` variant and matching C status code `SHOREBIRD_UPDATE_IN_PROGRESS = 4`. `updater::update()` catches the `UpdateAlreadyInProgress` error from the lock helper and maps it to `Ok(UpdateStatus::UpdateInProgress)`. The Dart wrapper treats the new status as a successful return alongside `SHOREBIRD_UPDATE_INSTALLED`, so `update()` no longer throws for this case. Update the existing `usage_during_hung_update` c_api test to assert the new contract, and add a Dart test covering the in-progress return path. Version skew: new Dart on an old engine still sees the legacy `SHOREBIRD_UPDATE_ERROR` + "Update already in progress" message and will still throw. The fix lands once both sides ship. Partially addresses shorebirdtech/shorebird#3682 — does not resolve the broader asymmetry of `update()` semantics (it still does not wait for someone else's in-flight update to finish), which remains as v2 design work in shorebirdtech/shorebird#3684.
247 lines
7.4 KiB
C
247 lines
7.4 KiB
C
#ifndef updater_h
|
|
#define updater_h
|
|
|
|
/* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */
|
|
|
|
#include <stdarg.h>
|
|
#include <stdbool.h>
|
|
#include <stdint.h>
|
|
#include <stdlib.h>
|
|
#ifdef _WIN32
|
|
#define SHOREBIRD_EXPORT __declspec(dllexport)
|
|
#else
|
|
#define SHOREBIRD_EXPORT __attribute__((visibility("default")))
|
|
#endif
|
|
|
|
|
|
/**
|
|
* An unknown error occurred while updating. The update was not installed.
|
|
* This is a catch-all for errors that don't fit into the other categories.
|
|
*/
|
|
#define SHOREBIRD_UPDATE_ERROR -1
|
|
|
|
/**
|
|
* No update is available (e.g. the app is already up-to-date)
|
|
*/
|
|
#define SHOREBIRD_NO_UPDATE 0
|
|
|
|
/**
|
|
* An update was installed successfully. It will boot from the update on the
|
|
* next app launch.
|
|
*/
|
|
#define SHOREBIRD_UPDATE_INSTALLED 1
|
|
|
|
/**
|
|
* An error occurred while updating. The update was not installed.
|
|
*/
|
|
#define SHOREBIRD_UPDATE_HAD_ERROR 2
|
|
|
|
/**
|
|
* The downloaded patch was not installed because it was invalid.
|
|
*/
|
|
#define SHOREBIRD_UPDATE_IS_BAD_PATCH 3
|
|
|
|
/**
|
|
* Another update was already in progress when this call was made. The
|
|
* already-running update will continue; the caller did not start a new one.
|
|
* This is a benign outcome, not an error.
|
|
*/
|
|
#define SHOREBIRD_UPDATE_IN_PROGRESS 4
|
|
|
|
/**
|
|
* Struct containing configuration parameters for the updater.
|
|
* Passed to all updater functions.
|
|
* NOTE: If this struct is changed all language bindings must be updated.
|
|
*/
|
|
typedef struct AppParameters {
|
|
/**
|
|
* release_version, required. Named version of the app, off of which
|
|
* updates are based. Can be either a version number or a hash.
|
|
*/
|
|
const char *release_version;
|
|
/**
|
|
* Array of paths to the original aot library, required. For Flutter apps
|
|
* these are the paths to the bundled libapp.so. May be used for
|
|
* compression downloaded artifacts.
|
|
*/
|
|
const char *const *original_libapp_paths;
|
|
/**
|
|
* Length of the original_libapp_paths array.
|
|
*/
|
|
int original_libapp_paths_size;
|
|
/**
|
|
* Path to app storage directory where the updater will store serialized
|
|
* state and other data that persists between releases.
|
|
*/
|
|
const char *app_storage_dir;
|
|
/**
|
|
* Path to cache directory where the updater will store downloaded
|
|
* artifacts and data that can be deleted when a new release is detected.
|
|
*/
|
|
const char *code_cache_dir;
|
|
} AppParameters;
|
|
|
|
typedef struct FileCallbacks {
|
|
/**
|
|
* Opens the "file" (actually an in-memory buffer) and returns a handle.
|
|
*/
|
|
void *(*open)(void);
|
|
/**
|
|
* Reads count bytes from the file into buffer. Returns the number of
|
|
* bytes read.
|
|
*/
|
|
uintptr_t (*read)(void *file_handle, uint8_t *buffer, uintptr_t count);
|
|
/**
|
|
* Moves the file pointer to the given offset relative from whence (one of
|
|
* libc::SEEK_SET, libc::SEEK_CUR, or libc::SEEK_END). Returns the new
|
|
* offset relative to the start of the file.
|
|
*/
|
|
int64_t (*seek)(void *file_handle, int64_t offset, int32_t whence);
|
|
/**
|
|
* Closes and frees the file handle.
|
|
*/
|
|
void (*close)(void *file_handle);
|
|
} FileCallbacks;
|
|
|
|
typedef struct UpdateResult {
|
|
int32_t status;
|
|
const char *message;
|
|
} UpdateResult;
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif // __cplusplus
|
|
|
|
/**
|
|
* Configures updater. First parameter is a struct containing configuration
|
|
* from the running app. Second parameter is a YAML string containing
|
|
* configuration compiled into the app. Returns true on success and false on
|
|
* failure. If false is returned, the updater library will not be usable.
|
|
*/
|
|
SHOREBIRD_EXPORT
|
|
bool shorebird_init(const struct AppParameters *c_params,
|
|
struct FileCallbacks c_file_callbacks,
|
|
const char *c_yaml);
|
|
|
|
/**
|
|
* Returns if the app should run the updater automatically on launch.
|
|
*/
|
|
SHOREBIRD_EXPORT bool shorebird_should_auto_update(void);
|
|
|
|
/**
|
|
* The currently running patch number, or 0 if the release has not been
|
|
* patched.
|
|
*/
|
|
SHOREBIRD_EXPORT uintptr_t shorebird_current_boot_patch_number(void);
|
|
|
|
/**
|
|
* The patch number that will boot on the next run of the app, or 0 if there is
|
|
* no next patch.
|
|
*/
|
|
SHOREBIRD_EXPORT uintptr_t shorebird_next_boot_patch_number(void);
|
|
|
|
/**
|
|
* Performs integrity checks on the next boot patch. If the patch fails these checks, the patch
|
|
* will be deleted and the next boot patch will be set to the last successfully booted patch or
|
|
* the base release if there is no last successfully booted patch.
|
|
*/
|
|
SHOREBIRD_EXPORT
|
|
void shorebird_validate_next_boot_patch(void);
|
|
|
|
/**
|
|
* The path to the patch that will boot on the next run of the app, or NULL if
|
|
* there is no next patch.
|
|
*/
|
|
SHOREBIRD_EXPORT char *shorebird_next_boot_patch_path(void);
|
|
|
|
/**
|
|
* Free a string returned by the updater library.
|
|
* # Safety
|
|
*
|
|
* If this function is called with a non-null pointer, it must be a pointer
|
|
* returned by the updater library.
|
|
*/
|
|
SHOREBIRD_EXPORT void shorebird_free_string(const char *c_string);
|
|
|
|
/**
|
|
* Frees an `UpdateResult` previously returned by `shorebird_check_for_update`.
|
|
*
|
|
* # Safety
|
|
*
|
|
* `result` must be a valid pointer returned by `shorebird_check_for_update`,
|
|
* or null (in which case this is a no-op).
|
|
*/
|
|
SHOREBIRD_EXPORT void shorebird_free_update_result(struct UpdateResult *result);
|
|
|
|
/**
|
|
* Check for an update. Returns true if an update is available.
|
|
*/
|
|
SHOREBIRD_EXPORT bool shorebird_check_for_update(void);
|
|
|
|
/**
|
|
* Check for an update on the first non-null channel of:
|
|
* 1. `c_channel`
|
|
* 2. The channel specified in shorebird.yaml
|
|
* 3. The default "stable" channel
|
|
*
|
|
* Returns true if an update exists that has not yet been downloaded.
|
|
*/
|
|
SHOREBIRD_EXPORT
|
|
bool shorebird_check_for_downloadable_update(const char *c_channel);
|
|
|
|
/**
|
|
* Synchronously download an update if one is available.
|
|
*/
|
|
SHOREBIRD_EXPORT void shorebird_update(void);
|
|
|
|
/**
|
|
* Synchronously download an update on the first non-null channel of:
|
|
* 1. `c_channel`
|
|
* 2. The channel specified in shorebird.yaml
|
|
* 3. The default "stable" channel
|
|
*
|
|
* Returns an [UpdateResult] indicating whether the update was successful.
|
|
*/
|
|
SHOREBIRD_EXPORT
|
|
const struct UpdateResult *shorebird_update_with_result(const char *c_channel);
|
|
|
|
/**
|
|
* Start a thread to download an update if one is available.
|
|
*/
|
|
SHOREBIRD_EXPORT void shorebird_start_update_thread(void);
|
|
|
|
/**
|
|
* Tell the updater that we're launching from what it told us was the
|
|
* next patch to boot from. This will copy the next boot patch to be the
|
|
* `current_boot` patch.
|
|
*
|
|
* It is required to call this function before calling
|
|
* `shorebird_report_launch_success` or `shorebird_report_launch_failure`.
|
|
*/
|
|
SHOREBIRD_EXPORT void shorebird_report_launch_start(void);
|
|
|
|
/**
|
|
* Report that the app failed to launch. This will cause the updater to
|
|
* attempt to roll back to the previous version if this version has not
|
|
* been launched successfully before.
|
|
*/
|
|
SHOREBIRD_EXPORT void shorebird_report_launch_failure(void);
|
|
|
|
/**
|
|
* Report that the app launched successfully. This will mark the current
|
|
* as having been launched successfully. We don't currently do anything
|
|
* with this information, but it could be used to record a point at which
|
|
* we will not roll back from.
|
|
*
|
|
* This is not currently wired up to be called from the Engine. It's unclear
|
|
* where best to connect it. Expo waits 5 seconds after the app launches
|
|
* and then marks the launch as successful. We could do something similar.
|
|
*/
|
|
SHOREBIRD_EXPORT void shorebird_report_launch_success(void);
|
|
|
|
#ifdef __cplusplus
|
|
} // extern "C"
|
|
#endif // __cplusplus
|
|
|
|
#endif /* updater_h */
|