Skip to content

Commit f88d53f

Browse files
authored
Merge pull request #919 from drewmcelhany/fix/startup-freeze-update-check
Fixed: Launcher freezing on startup when fetching mod update metadata
2 parents 0bdd9cd + e5f22c3 commit f88d53f

3 files changed

Lines changed: 63 additions & 36 deletions

File tree

source/Reloaded.Mod.Launcher.Lib/Models/ViewModel/Dialog/ModUpdateDialogViewModel.cs

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,15 +41,26 @@ public class ModUpdateDialogViewModel : ObservableObject
4141
/// </summary>
4242
public bool CanDownload { get; set; }
4343

44-
/// <summary/>
45-
public ModUpdateDialogViewModel(Updater updater, ModUpdateSummary summary)
44+
/// <summary>
45+
/// Creates the ViewModel using precomputed update info.
46+
/// </summary>
47+
public ModUpdateDialogViewModel(Updater updater, ModUpdateSummary summary, ModUpdate[] updateInfo)
4648
{
4749
Updater = updater;
4850
Summary = summary;
49-
UpdateInfo = Summary.GetUpdateInfo();
51+
UpdateInfo = updateInfo;
5052
TotalSize = UpdateInfo.Sum(x => x.UpdateSize);
51-
SelectedUpdate = UpdateInfo[0];
52-
CanDownload = true;
53+
54+
if (UpdateInfo.Length > 0)
55+
{
56+
SelectedUpdate = UpdateInfo[0];
57+
CanDownload = true;
58+
}
59+
else
60+
{
61+
SelectedUpdate = null;
62+
CanDownload = false;
63+
}
5364
}
5465

5566
/// <summary>

source/Reloaded.Mod.Launcher.Lib/Update.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,9 +130,14 @@ public static async Task<bool> CheckForModUpdatesAsync()
130130

131131
if (updateDetails.HasUpdates())
132132
{
133+
// Fetch update metadata (download sizes, changelogs) on a background thread.
134+
// Doing this inside the ViewModel constructor blocked the UI thread on
135+
// unbounded network I/O, freezing the launcher on startup. See issue #910.
136+
var updateInfo = await updateDetails.GetUpdateInfoAsync();
137+
133138
Actions.SynchronizationContext.Send(_ =>
134139
{
135-
Actions.ShowModUpdateDialog.Invoke(new ModUpdateDialogViewModel(updater, updateDetails));
140+
Actions.ShowModUpdateDialog.Invoke(new ModUpdateDialogViewModel(updater, updateDetails, updateInfo));
136141
}, null);
137142

138143
return true;

source/Reloaded.Mod.Loader.Update/Structures/ModUpdateSummary.cs

Lines changed: 41 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -39,61 +39,72 @@ public bool HasUpdates()
3939

4040
/// <summary>
4141
/// Retrieves info about the individual updates.
42+
/// Synchronous wrapper; do not call from the UI thread. Prefer <see cref="GetUpdateInfoAsync"/>.
4243
/// </summary>
4344
/// <returns></returns>
44-
public ModUpdate[] GetUpdateInfo()
45+
public ModUpdate[] GetUpdateInfo() => Task.Run(GetUpdateInfoAsync).GetAwaiter().GetResult();
46+
47+
/// <summary>
48+
/// Retrieves info about the individual updates without blocking the calling thread.
49+
/// </summary>
50+
/// <returns></returns>
51+
public async Task<ModUpdate[]> GetUpdateInfoAsync()
4552
{
4653
if (_updates != null)
4754
return _updates;
4855

49-
_updates = new ModUpdate[ManagerModResultPairs.Count];
50-
Task.Run(async () =>
56+
var updates = new ModUpdate[ManagerModResultPairs.Count];
57+
for (var x = 0; x < ManagerModResultPairs.Count; x++)
5158
{
52-
for (var x = 0; x < ManagerModResultPairs.Count; x++)
59+
var resultPairs = ManagerModResultPairs[x];
60+
var modName = resultPairs.ModTuple.Config.ModName;
61+
var modId = resultPairs.ModTuple.Config.ModId;
62+
var oldVersion = resultPairs.ModTuple.Config.ModVersion;
63+
var newVersion = resultPairs.Result.LastVersion;
64+
var resolver = resultPairs.Manager.Resolver;
65+
var updateSize = (long)0;
66+
string? changelog = null;
67+
68+
if (resolver is IPackageResolverDownloadSize hasDownloadSize)
5369
{
54-
var resultPairs = ManagerModResultPairs[x];
55-
var modName = resultPairs.ModTuple.Config.ModName;
56-
var modId = resultPairs.ModTuple.Config.ModId;
57-
var oldVersion = resultPairs.ModTuple.Config.ModVersion;
58-
var newVersion = resultPairs.Result.LastVersion;
59-
var resolver = ((IPackageResolverDownloadSize)resultPairs.Manager.Resolver);
60-
var updateSize = (long)0;
61-
string? changelog = null;
62-
6370
try
6471
{
65-
updateSize = await resolver.GetDownloadFileSizeAsync(newVersion!, resultPairs.ModTuple.GetVerificationInfo());
72+
updateSize = await hasDownloadSize.GetDownloadFileSizeAsync(newVersion!, resultPairs.ModTuple.GetVerificationInfo()).ConfigureAwait(false);
6673
}
6774
catch (Exception) { /* Ignored */ }
75+
}
6876

69-
// Get changelog from supported resolver.
70-
if (resolver is IPackageResolverGetLatestReleaseMetadata getMetadata)
77+
// Get changelog from supported resolver.
78+
if (resolver is IPackageResolverGetLatestReleaseMetadata getMetadata)
79+
{
80+
try
7181
{
72-
try
73-
{
74-
var releaseMetadata = await getMetadata.GetReleaseMetadataAsync(default);
75-
var extraData = releaseMetadata?.GetExtraData<ReleaseMetadataExtraData>();
76-
if (extraData != null)
77-
changelog = extraData.Changelog;
78-
}
79-
catch (Exception) { /* Ignored */ }
82+
var releaseMetadata = await getMetadata.GetReleaseMetadataAsync(default).ConfigureAwait(false);
83+
var extraData = releaseMetadata?.GetExtraData<ReleaseMetadataExtraData>();
84+
if (extraData != null)
85+
changelog = extraData.Changelog;
8086
}
87+
catch (Exception) { /* Ignored */ }
88+
}
8189

82-
// NuGet has special case, since it doesn't support release metadata but supports changelogs in nuspec.
83-
if (string.IsNullOrEmpty(changelog) && resolver is NuGetUpdateResolver nugetResolver)
90+
// NuGet has special case, since it doesn't support release metadata but supports changelogs in nuspec.
91+
if (string.IsNullOrEmpty(changelog) && resolver is NuGetUpdateResolver nugetResolver)
92+
{
93+
try
8494
{
8595
var copiedSettings = nugetResolver.GetResolverSettings();
8696
var repository = NugetRepository.FromSourceUrl(copiedSettings.NugetRepository!.SourceUrl);
87-
var reader = await repository.DownloadNuspecReaderAsync(new PackageIdentity(copiedSettings.PackageId, newVersion!));
97+
var reader = await repository.DownloadNuspecReaderAsync(new PackageIdentity(copiedSettings.PackageId, newVersion!)).ConfigureAwait(false);
8898
if (reader != null)
8999
changelog = reader?.GetReleaseNotes();
90100
}
91-
92-
_updates[x] = new ModUpdate(modId, NuGetVersion.Parse(oldVersion), newVersion!, updateSize, changelog, modName);
101+
catch (Exception) { /* Ignored */ }
93102
}
94103

95-
}).Wait();
104+
updates[x] = new ModUpdate(modId, NuGetVersion.Parse(oldVersion), newVersion!, updateSize, changelog, modName);
105+
}
96106

107+
_updates = updates;
97108
return _updates;
98109
}
99110

0 commit comments

Comments
 (0)