Skip to content

Commit 9f20c5d

Browse files
JusterZhuclaude
andcommitted
fix: address Copilot review comments on PR #456/#458
- Remove empty try/finally block in ClientUpdateStrategy download path - Add thread-safety note for shared custom executor in DefaultDownloadOrchestrator - Add ResolveExtensionType<T>() to avoid wasteful instantiation for pipeline factory - Restore ClientUpdateStrategy(IDownloadOrchestrator?) constructor for backward compat Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
1 parent 861e75c commit 9f20c5d

5 files changed

Lines changed: 22 additions & 107 deletions

File tree

src/c#/GeneralUpdate.Core/Bootstrap/GeneralUpdateBootstrap.cs

Lines changed: 6 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -94,35 +94,26 @@ private async Task<GeneralUpdateBootstrap> LaunchWithStrategy(IStrategy roleStra
9494
token.ThrowIfCancellationRequested();
9595
ApplyRuntimeOptions();
9696

97-
// Resolve hooks and reporter from extensions
98-
var hooks = ResolveExtension<Hooks.IUpdateHooks>() ?? new Hooks.NoOpUpdateHooks();
99-
var reporter = ResolveExtension<Download.Reporting.IUpdateReporter>() ??
100-
new Download.Reporting.NoOpUpdateReporter();
101-
10297
// ── Network-level extensions (global, applied before any HTTP call) ──
10398
var sslPolicy = ResolveExtension<Security.ISslValidationPolicy>();
10499
if (sslPolicy != null) Network.VersionService.SetSslValidationPolicy(sslPolicy);
105100
var authProvider = ResolveExtension<Security.IHttpAuthProvider>();
106101
if (authProvider != null) Network.VersionService.SetDefaultAuthProvider(authProvider);
107102

108-
// ── Phase 1: inject all dependencies before Create ──
103+
// Resolve hooks from extensions
104+
var hooks = ResolveExtension<Hooks.IUpdateHooks>() ?? new Hooks.NoOpUpdateHooks();
109105
roleStrategy.Hooks = hooks;
110-
roleStrategy.Reporter = reporter;
111106

112-
var binaryDiffer = ResolveExtension<IBinaryDiffer>();
113-
var dirtyStrategy = ResolveExtension<IDirtyStrategy>();
107+
// ── Download components ──
114108
var downloadOrchestrator = ResolveExtension<Download.Abstractions.IDownloadOrchestrator>();
115109
var downloadPolicy = ResolveExtension<Download.Abstractions.IDownloadPolicy>();
116110
var downloadExecutor = ResolveExtension<Download.Abstractions.IDownloadExecutor>();
117111

118-
// Build download pipeline factory from registered extension type.
119-
// Tries a string constructor first (for passing the expected hash, à la DefaultDownloadPipeline),
120-
// falls back to parameterless constructor otherwise.
112+
// Build download pipeline factory from registered extension type
121113
Func<string?, Download.Abstractions.IDownloadPipeline>? downloadPipelineFactory = null;
122-
var downloadPipeline = ResolveExtension<Download.Abstractions.IDownloadPipeline>();
123-
if (downloadPipeline != null)
114+
var pipelineType = ResolveExtensionType<Download.Abstractions.IDownloadPipeline>();
115+
if (pipelineType != null)
124116
{
125-
var pipelineType = downloadPipeline.GetType();
126117
var stringCtor = pipelineType.GetConstructor([typeof(string)]);
127118
if (stringCtor != null)
128119
downloadPipelineFactory = hash => (Download.Abstractions.IDownloadPipeline)stringCtor.Invoke([hash]);
@@ -142,8 +133,6 @@ private async Task<GeneralUpdateBootstrap> LaunchWithStrategy(IStrategy roleStra
142133

143134
await CallSmallBowlHomeAsync(_configInfo.Bowl).ConfigureAwait(false);
144135

145-
if (binaryDiffer != null) cs.SetBinaryDiffer(binaryDiffer);
146-
if (dirtyStrategy != null) cs.SetDirtyStrategy(dirtyStrategy);
147136
cs.SetDiffPipeline(diffPipeline);
148137
if (downloadOrchestrator != null) cs.SetOrchestrator(downloadOrchestrator);
149138
if (downloadPolicy != null) cs.SetDownloadPolicy(downloadPolicy);
@@ -152,10 +141,6 @@ private async Task<GeneralUpdateBootstrap> LaunchWithStrategy(IStrategy roleStra
152141
break;
153142

154143
case UpgradeUpdateStrategy us:
155-
if (binaryDiffer != null)
156-
us.SetBinaryDiffer(binaryDiffer);
157-
if (dirtyStrategy != null)
158-
us.SetDirtyStrategy(dirtyStrategy);
159144
us.SetDiffPipeline(diffPipeline);
160145
break;
161146
}

src/c#/GeneralUpdate.Core/Configuration/AbstractBootstrap.cs

Lines changed: 6 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -129,36 +129,6 @@ protected T GetOption<T>(UpdateOption<T>? option)
129129
return (TBootstrap)this;
130130
}
131131

132-
public TBootstrap DirtyStrategy<T>() where T : Differential.IDirtyStrategy, new()
133-
{
134-
_extensions[typeof(Differential.IDirtyStrategy)] = typeof(T);
135-
return (TBootstrap)this;
136-
}
137-
138-
public TBootstrap BinaryDiffer<T>() where T : IBinaryDiffer, new()
139-
{
140-
_extensions[typeof(IBinaryDiffer)] = typeof(T);
141-
return (TBootstrap)this;
142-
}
143-
144-
public TBootstrap ConfigureBlackList(BlackListConfig config)
145-
{
146-
_instances[typeof(BlackListConfig)] = config ?? BlackListConfig.Empty;
147-
return (TBootstrap)this;
148-
}
149-
150-
/// <summary>
151-
/// Configure blacklist via fluent builder action.
152-
/// Usage: <c>.ConfigureBlackList(cfg => cfg.AddBlackFiles("*.log").AddBlackFormats(".pdb"))</c>
153-
/// </summary>
154-
public TBootstrap ConfigureBlackList(Action<FileSystem.BlackListConfigBuilder> configure)
155-
{
156-
var builder = new FileSystem.BlackListConfigBuilder();
157-
configure(builder);
158-
_instances[typeof(BlackListConfig)] = builder.Build();
159-
return (TBootstrap)this;
160-
}
161-
162132
protected TExtension? ResolveExtension<TExtension>() where TExtension : class
163133
{
164134
if (_extensions.TryGetValue(typeof(TExtension), out var t))
@@ -167,5 +137,11 @@ public TBootstrap ConfigureBlackList(Action<FileSystem.BlackListConfigBuilder> c
167137
return instance as TExtension;
168138
return null;
169139
}
140+
141+
/// <summary>Resolves the registered extension type without instantiating it.</summary>
142+
protected Type? ResolveExtensionType<TExtension>() where TExtension : class
143+
{
144+
return _extensions.TryGetValue(typeof(TExtension), out var t) ? t : null;
145+
}
170146
}
171147
}

src/c#/GeneralUpdate.Core/Download/Orchestrators/DefaultDownloadOrchestrator.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ public class DefaultDownloadOrchestrator : IDownloadOrchestrator
3030
private readonly DownloadOrchestratorOptions _options;
3131
private readonly IDownloadExecutor? _customExecutor;
3232
private readonly Func<string?, IDownloadPipeline>? _pipelineFactory;
33+
// Note: _customExecutor is a single shared instance used across parallel asset downloads.
34+
// Implementations of IDownloadExecutor must be thread-safe. HttpClient-based executors
35+
// satisfy this as HttpClient is designed for concurrent use.
3336

3437
public DefaultDownloadOrchestrator(
3538
HttpClient httpClient,

src/c#/GeneralUpdate.Core/Strategy/ClientUpdateStrategy.cs

Lines changed: 7 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,9 @@ private enum UpdateScenario
6262

6363
public ClientUpdateStrategy() { }
6464

65+
public ClientUpdateStrategy(Download.Abstractions.IDownloadOrchestrator? orchestrator)
66+
=> _orchestrator = orchestrator;
67+
6568
/// <summary>Sets a custom OS-level strategy (injected via <c>.Strategy&lt;T&gt;()</c>).
6669
/// When set, this replaces the automatic platform detection in <see cref="ResolveOsStrategy"/>.</summary>
6770
public void SetOsStrategy(IStrategy? strategy) => _customOsStrategy = strategy;
@@ -84,8 +87,6 @@ public void Create(GlobalConfigInfo parameter)
8487
_osStrategy = ResolveOsStrategy();
8588
if (_osStrategy is AbstractStrategy abs)
8689
{
87-
if (_pendingDirtyStrategy != null) abs.DirtyStrategy = _pendingDirtyStrategy;
88-
if (_pendingBinaryDiffer != null) abs.BinaryDiffer = _pendingBinaryDiffer;
8990
if (_pendingDiffPipeline != null) abs.DiffPipeline = _pendingDiffPipeline;
9091
}
9192
}
@@ -110,29 +111,8 @@ public async Task ExecuteAsync()
110111
}
111112
}
112113

113-
private IDirtyStrategy? _pendingDirtyStrategy;
114-
private IBinaryDiffer? _pendingBinaryDiffer;
115114
private DiffPipeline? _pendingDiffPipeline;
116115

117-
/// <summary>Sets the directory-level dirty strategy on the underlying OS-level strategy for differential patch updates.
118-
/// Safe to call before or after Create(). If called before, the strategy is cached and applied when Create() resolves _osStrategy.</summary>
119-
public void SetDirtyStrategy(IDirtyStrategy? dirtyStrategy)
120-
{
121-
if (_osStrategy is AbstractStrategy abs)
122-
abs.DirtyStrategy = dirtyStrategy;
123-
else
124-
_pendingDirtyStrategy = dirtyStrategy;
125-
}
126-
127-
/// <summary>Sets the file-level binary differ on the underlying OS-level strategy.</summary>
128-
public void SetBinaryDiffer(IBinaryDiffer? binaryDiffer)
129-
{
130-
if (_osStrategy is AbstractStrategy abs)
131-
abs.BinaryDiffer = binaryDiffer;
132-
else
133-
_pendingBinaryDiffer = binaryDiffer;
134-
}
135-
136116
/// <summary>Sets the DiffPipeline on the underlying OS-level strategy for parallel patch application.</summary>
137117
public void SetDiffPipeline(DiffPipeline? diffPipeline)
138118
{
@@ -292,16 +272,10 @@ private async Task ExecuteStandardWorkflowAsync()
292272
else
293273
{
294274
var httpClient = GeneralUpdate.Core.Network.HttpClientProvider.Shared;
295-
try
296-
{
297-
var orchestrator = new Download.Orchestrators.DefaultDownloadOrchestrator(
298-
httpClient, orchOptions, _customDownloadPolicy,
299-
_customDownloadExecutor, _customDownloadPipelineFactory);
300-
await orchestrator.ExecuteAsync(downloadPlan, _configInfo.TempPath).ConfigureAwait(false);
301-
}
302-
finally
303-
{
304-
}
275+
var orchestrator = new Download.Orchestrators.DefaultDownloadOrchestrator(
276+
httpClient, orchOptions, _customDownloadPolicy,
277+
_customDownloadExecutor, _customDownloadPipelineFactory);
278+
await orchestrator.ExecuteAsync(downloadPlan, _configInfo.TempPath).ConfigureAwait(false);
305279
}
306280

307281
await SafeReportDownloadCompletedAsync(hooksCtx).ConfigureAwait(false);

src/c#/GeneralUpdate.Core/Strategy/UpgradeUpdateStrategy.cs

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,6 @@ public void Create(GlobalConfigInfo parameter)
4545
_osStrategy = ResolveOsStrategy();
4646
if (_osStrategy is AbstractStrategy abs)
4747
{
48-
if (_pendingDirtyStrategy != null) abs.DirtyStrategy = _pendingDirtyStrategy;
49-
if (_pendingBinaryDiffer != null) abs.BinaryDiffer = _pendingBinaryDiffer;
5048
if (_pendingDiffPipeline != null) abs.DiffPipeline = _pendingDiffPipeline;
5149
}
5250
}
@@ -116,29 +114,8 @@ public async Task ExecuteAsync()
116114
}
117115
}
118116

119-
private IDirtyStrategy? _pendingDirtyStrategy;
120-
private IBinaryDiffer? _pendingBinaryDiffer;
121117
private DiffPipeline? _pendingDiffPipeline;
122118

123-
/// <summary>Sets the directory-level dirty strategy on the underlying OS-level strategy for differential patch updates.
124-
/// Safe to call before or after Create(). If called before, the strategy is cached and applied when Create() resolves _osStrategy.</summary>
125-
public void SetDirtyStrategy(IDirtyStrategy? dirtyStrategy)
126-
{
127-
if (_osStrategy is AbstractStrategy abs)
128-
abs.DirtyStrategy = dirtyStrategy;
129-
else
130-
_pendingDirtyStrategy = dirtyStrategy;
131-
}
132-
133-
/// <summary>Sets the file-level binary differ on the underlying OS-level strategy.</summary>
134-
public void SetBinaryDiffer(IBinaryDiffer? binaryDiffer)
135-
{
136-
if (_osStrategy is AbstractStrategy abs)
137-
abs.BinaryDiffer = binaryDiffer;
138-
else
139-
_pendingBinaryDiffer = binaryDiffer;
140-
}
141-
142119
/// <summary>Sets the DiffPipeline on the underlying OS-level strategy for parallel patch application.</summary>
143120
public void SetDiffPipeline(DiffPipeline? diffPipeline)
144121
{

0 commit comments

Comments
 (0)