Skip to content

Commit 8e1c9bf

Browse files
authored
Merge pull request #1961 from tyrielv/tyrielv/streamlined-upgrade-fixes
Fix staged upgrade races and installer error handling
2 parents 3a0f1b2 + 58a00e8 commit 8e1c9bf

2 files changed

Lines changed: 39 additions & 19 deletions

File tree

GVFS/GVFS.Installers/Setup.iss

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ DestDir: "{app}"; Flags: ignoreversion; Source:"{#LayoutDir}\GVFS.Service.exe";
6666
; goes directly to {app} so the restarted service has PendingUpgradeHandler code.
6767
; The service is briefly stopped/restarted (mounts are independent processes).
6868
DestDir: "{app}\PendingUpgrade"; Flags: ignoreversion recursesubdirs; Source:"{#LayoutDir}\*"; Check: IsStagingInstall
69-
DestDir: "{app}"; Flags: ignoreversion; Source:"{#LayoutDir}\GVFS.Service.exe"; AfterInstall: StagingUpdateService; Check: IsStagingInstall
69+
DestDir: "{app}"; Flags: ignoreversion; Source:"{#LayoutDir}\GVFS.Service.exe"; Check: IsStagingInstall
7070

7171
[Dirs]
7272
Name: "{app}\ProgramData\{#ServiceName}"; Permissions: users-readexec
@@ -166,11 +166,17 @@ var
166166
ResultCode: integer;
167167
begin
168168
Log('StopService: stopping: ' + ServiceName);
169-
// ErrorCode 1060 means service not installed, 1062 means service not started
170-
if not Exec(ExpandConstant('{sys}\SC.EXE'), 'stop ' + ServiceName, '', SW_HIDE, ewWaitUntilTerminated, ResultCode) and (ResultCode <> 1060) and (ResultCode <> 1062) then
169+
if not Exec(ExpandConstant('{sys}\SC.EXE'), 'stop ' + ServiceName, '', SW_HIDE, ewWaitUntilTerminated, ResultCode) then
171170
begin
171+
Log('StopService: Failed to launch sc.exe');
172172
RaiseException('Fatal: Could not stop service: ' + ServiceName);
173173
end;
174+
// 1060 = service not installed, 1062 = service not started
175+
if (ResultCode <> 0) and (ResultCode <> 1060) and (ResultCode <> 1062) then
176+
begin
177+
Log('StopService: sc stop returned error code ' + IntToStr(ResultCode));
178+
RaiseException('Fatal: Could not stop service: ' + ServiceName + ' (exit code ' + IntToStr(ResultCode) + ')');
179+
end;
174180
end;
175181
176182
procedure WaitForServiceProcessToExit(ServiceName: string);
@@ -328,9 +334,14 @@ begin
328334
329335
try
330336
Log('StagingUpdateService: Starting service with new binary');
331-
if not Exec(ExpandConstant('{sys}\SC.EXE'), 'start GVFS.Service', '', SW_HIDE, ewWaitUntilTerminated, ResultCode) then
337+
if Exec(ExpandConstant('{sys}\SC.EXE'), 'start GVFS.Service', '', SW_HIDE, ewWaitUntilTerminated, ResultCode) then
338+
begin
339+
if ResultCode <> 0 then
340+
Log('StagingUpdateService: Warning - sc start returned error code ' + IntToStr(ResultCode));
341+
end
342+
else
332343
begin
333-
Log('StagingUpdateService: Warning - could not start service: ' + SysErrorMessage(ResultCode));
344+
Log('StagingUpdateService: Warning - could not launch sc.exe');
334345
end;
335346
336347
WriteOnDiskVersion16CapableFile();
@@ -721,6 +732,11 @@ begin
721732
// complete and safe to apply.
722733
SaveStringToFile(ExpandConstant('{app}\PendingUpgrade\.ready'), '', False);
723734
Log('CurStepChanged: Wrote PendingUpgrade .ready marker');
735+
736+
// Start the service AFTER .ready is written. Previously this
737+
// was an AfterInstall hook on GVFS.Service.exe, but that races:
738+
// the service's debounce timer could fire before .ready exists.
739+
StagingUpdateService();
724740
end;
725741
MigrateConfigAndStatusCacheFiles();
726742
if (not KeepMountsRunning) and (ExpandConstant('{param:REMOUNTREPOS|true}') = 'true') then

GVFS/GVFS.Service/Handlers/RequestHandler.cs

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ public class RequestHandler
2929
private ITracer tracer;
3030
private IRepoRegistry repoRegistry;
3131
private Timer pendingUpgradeTimer;
32+
private readonly object pendingUpgradeTimerLock = new object();
3233

3334
public RequestHandler(ITracer tracer, string etwArea, IRepoRegistry repoRegistry)
3435
{
@@ -146,21 +147,24 @@ private void TryDeferredPendingUpgradeCheck(ITracer tracer)
146147
// Debounce: reset the timer on each unmount so the check fires
147148
// once after the last unmount settles. If multiple repos unmount
148149
// in quick succession, only one upgrade attempt runs.
149-
if (this.pendingUpgradeTimer == null)
150+
lock (this.pendingUpgradeTimerLock)
150151
{
151-
this.pendingUpgradeTimer = new Timer(
152-
_ =>
153-
{
154-
tracer.RelatedInfo("TryDeferredPendingUpgradeCheck: Checking pending upgrade after unmount");
155-
PendingUpgradeHandler.TryApplyPendingUpgrade(tracer);
156-
},
157-
null,
158-
PendingUpgradeDelayMs,
159-
Timeout.Infinite);
160-
}
161-
else
162-
{
163-
this.pendingUpgradeTimer.Change(PendingUpgradeDelayMs, Timeout.Infinite);
152+
if (this.pendingUpgradeTimer == null)
153+
{
154+
this.pendingUpgradeTimer = new Timer(
155+
_ =>
156+
{
157+
tracer.RelatedInfo("TryDeferredPendingUpgradeCheck: Checking pending upgrade after unmount");
158+
PendingUpgradeHandler.TryApplyPendingUpgrade(tracer);
159+
},
160+
null,
161+
PendingUpgradeDelayMs,
162+
Timeout.Infinite);
163+
}
164+
else
165+
{
166+
this.pendingUpgradeTimer.Change(PendingUpgradeDelayMs, Timeout.Infinite);
167+
}
164168
}
165169
}
166170
}

0 commit comments

Comments
 (0)