From 3cd28d4f9f35bd6374847a629c7b0321375c06a2 Mon Sep 17 00:00:00 2001 From: Marc Durdin Date: Mon, 4 Nov 2024 08:04:43 +0700 Subject: [PATCH] fix(developer): create Server config directory before options save Also use back-off logic for saving in case config file is locked by another process. Cherry-pick-of: #12608 Fixes: #12607 Fixes: KEYMAN-DEVELOPER-1Z1 --- .../src/tike/main/KeymanDeveloperOptions.pas | 67 ++++++++++--------- 1 file changed, 37 insertions(+), 30 deletions(-) diff --git a/developer/src/tike/main/KeymanDeveloperOptions.pas b/developer/src/tike/main/KeymanDeveloperOptions.pas index 6eb05730964..5f8613499ff 100644 --- a/developer/src/tike/main/KeymanDeveloperOptions.pas +++ b/developer/src/tike/main/KeymanDeveloperOptions.pas @@ -83,6 +83,7 @@ TKeymanDeveloperOptions = class procedure optWriteInt(const nm: string; value: Integer); procedure WriteServerConfigurationJson; class function Default_DefaultProjectPath: string; static; + function BackOffAndSaveJson(const Filename: string; const JSON: TJSONObject): Boolean; public procedure Read; procedure Write; @@ -406,8 +407,6 @@ procedure TKeymanDeveloperOptions.Read; end; procedure TKeymanDeveloperOptions.Write; -var - count: Integer; begin // If the output file does not exist, this creates it. Note that we load // from the existing file before writing out our set of options, which is @@ -464,33 +463,7 @@ procedure TKeymanDeveloperOptions.Write; ForceDirectories(TKeymanDeveloperPaths.OptionsPath); try - count := 0; - repeat - try - SaveJSONToFile(TKeymanDeveloperPaths.OptionsPath + TKeymanDeveloperPaths.S_OptionsJson, json); - Break; - except - on E:EOSError do - begin - if E.ErrorCode = ERROR_ACCESS_DENIED then - begin - // Back off and retry 0.5 sec later, it's probably another - // instance happening to read or write the settings file - Inc(count); - Sleep(500); - end - else - raise; - end - else - raise; - end; - until count >= Max_Retries; - if count >= Max_Retries then - begin - if TKeymanSentryClient.Instance <> nil then - TKeymanSentryClient.Instance.ReportMessage('TKeymanDeveloperOptions: Failed to write settings after 5 tries'); - end; + BackOffAndSaveJson(TKeymanDeveloperPaths.OptionsPath + TKeymanDeveloperPaths.S_OptionsJson, json); except on E:Exception do begin @@ -507,6 +480,39 @@ procedure TKeymanDeveloperOptions.Write; WriteServerConfigurationJson; end; +function TKeymanDeveloperOptions.BackOffAndSaveJson(const Filename: string; const JSON: TJSONObject): Boolean; +var + count: Integer; +begin + count := 0; + repeat + try + SaveJSONToFile(Filename, JSON); + Exit(True); + except + on E:EOSError do + begin + if E.ErrorCode = ERROR_ACCESS_DENIED then + begin + // Back off and retry 0.5 sec later, it's probably another + // instance happening to read or write the settings file + Inc(count); + Sleep(500); + end + else + raise; + end + else + raise; + end; + until count >= Max_Retries; + + if TKeymanSentryClient.Instance <> nil then + TKeymanSentryClient.Instance.ReportMessage('TKeymanDeveloperOptions: Failed to write '+Filename+' after 5 tries'); + + Result := False; +end; + procedure TKeymanDeveloperOptions.WriteServerConfigurationJson; var o: TJSONObject; @@ -517,7 +523,8 @@ procedure TKeymanDeveloperOptions.WriteServerConfigurationJson; o.AddPair('ngrokToken', FServerNgrokToken); o.AddPair('useNgrok', TJSONBool.Create(FServerUseNgrok)); o.AddPair('ngrokVisible', TJSONBool.Create(FServerServerShowConsoleWindow)); - SaveJSONToFile(TKeymanDeveloperPaths.ServerDataPath + TKeymanDeveloperPaths.S_ServerConfigJson, o); + ForceDirectories(TKeymanDeveloperPaths.ServerDataPath); + BackOffAndSaveJSON(TKeymanDeveloperPaths.ServerDataPath + TKeymanDeveloperPaths.S_ServerConfigJson, o); finally o.Free; end;