Skip to content

Commit

Permalink
fix(developer): create Server config directory before options save
Browse files Browse the repository at this point in the history
Also use back-off logic for saving in case config file is locked by
another process.

Fixes: #12607
  • Loading branch information
mcdurdin committed Nov 4, 2024
1 parent 6144265 commit 8846cb0
Showing 1 changed file with 37 additions and 30 deletions.
67 changes: 37 additions & 30 deletions developer/src/tike/main/KeymanDeveloperOptions.pas
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ TKeymanDeveloperOptions = class
procedure optWriteInt(const nm: string; value: Integer);
procedure WriteServerConfigurationJson;
class function Get_Initial_DefaultProjectPath: string; static;
function BackOffAndSaveJson(const Filename: string; const JSON: TJSONObject): Boolean;
public
procedure Read;
procedure Write;
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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;
Expand All @@ -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;
Expand Down

0 comments on commit 8846cb0

Please sign in to comment.