1
0
mirror of synced 2024-09-28 13:06:09 +00:00

Fixed crashes on shutdown due to Lua API being unloaded before the last TLua instance is destroyed

This commit is contained in:
Mark van Renswoude 2017-06-01 20:19:19 +02:00
parent 197349f515
commit 6dbea6f211
7 changed files with 166 additions and 172 deletions

View File

@ -263,13 +263,14 @@ object ButtonFunctionForm: TButtonFunctionForm
TreeOptions.PaintOptions = [toShowButtons, toShowDropmark, toShowTreeLines, toThemeAware, toUseBlendedImages]
TreeOptions.SelectionOptions = [toFullRowSelect]
OnFocusChanged = vstFunctionsFocusChanged
OnFreeNode = vstFunctionsFreeNode
OnGetText = vstFunctionsGetText
OnPaintText = vstFunctionsPaintText
OnIncrementalSearch = vstFunctionsIncrementalSearch
Columns = <
item
Position = 0
Width = 253
Width = 257
WideText = 'Available functions'
end>
end

View File

@ -15,6 +15,7 @@ uses
LEDColorIntf,
LEDFunctionIntf,
LEDFunctionRegistry,
LEDStateIntf,
Profile, Vcl.ActnList;
@ -60,6 +61,7 @@ type
procedure FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
procedure FormKeyPress(Sender: TObject; var Key: Char);
procedure FormShow(Sender: TObject);
procedure vstFunctionsFreeNode(Sender: TBaseVirtualTree; Node: PVirtualNode);
procedure vstFunctionsFocusChanged(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex);
procedure vstFunctionsGetText(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType; var CellText: string);
procedure vstFunctionsIncrementalSearch(Sender: TBaseVirtualTree; Node: PVirtualNode; const SearchText: string; var Result: Integer);
@ -73,8 +75,9 @@ type
FSelectedProvider: ILEDFunctionProvider;
FSelectedFunction: ILEDFunction;
FStateControls: TStateControlInfoList;
FFunctionRegistry: TLEDFunctionRegistry;
protected
procedure Initialize(AProfile: TProfile; AButtonIndex: Integer);
procedure Initialize(AFunctionRegistry: TLEDFunctionRegistry; AProfile: TProfile; AButtonIndex: Integer);
procedure LoadFunctions;
procedure ApplyFilter(const AFilter: string);
@ -91,8 +94,9 @@ type
property Profile: TProfile read FProfile;
property ButtonIndex: Integer read FButtonIndex;
property FunctionRegistry: TLEDFunctionRegistry read FFunctionRegistry;
public
class function Execute(AProfile: TProfile; AButtonIndex: Integer): Boolean;
class function Execute(AFunctionRegistry: TLEDFunctionRegistry; AProfile: TProfile; AButtonIndex: Integer): Boolean;
end;
@ -118,7 +122,6 @@ uses
System.SysUtils,
Winapi.Windows,
LEDFunctionRegistry,
LEDResources;
@ -150,11 +153,11 @@ const
{ TButtonFunctionForm }
class function TButtonFunctionForm.Execute(AProfile: TProfile; AButtonIndex: Integer): Boolean;
class function TButtonFunctionForm.Execute(AFunctionRegistry: TLEDFunctionRegistry; AProfile: TProfile; AButtonIndex: Integer): Boolean;
begin
with Self.Create(nil) do
try
Initialize(AProfile, AButtonIndex);
Initialize(AFunctionRegistry, AProfile, AButtonIndex);
Result := (ShowModal = mrOk);
finally
Free;
@ -247,7 +250,7 @@ begin
categoryNodes := TDictionary<string, PVirtualNode>.Create;
try
for provider in TLEDFunctionRegistry.Providers do
for provider in FunctionRegistry.Providers do
begin
isCurrentProvider := Assigned(CurrentProvider) and (provider.GetUID = CurrentProvider.GetUID);
@ -371,8 +374,9 @@ begin
end;
procedure TButtonFunctionForm.Initialize(AProfile: TProfile; AButtonIndex: Integer);
procedure TButtonFunctionForm.Initialize(AFunctionRegistry: TLEDFunctionRegistry; AProfile: TProfile; AButtonIndex: Integer);
begin
FFunctionRegistry := AFunctionRegistry;
FProfile := AProfile;
FButtonIndex := AButtonIndex;
FButton := nil;
@ -384,7 +388,7 @@ begin
if Profile.HasButton(ButtonIndex) then
begin
FButton := Profile.Buttons[ButtonIndex];
FCurrentProvider := TLEDFunctionRegistry.Find(Button.ProviderUID);
FCurrentProvider := FunctionRegistry.Find(Button.ProviderUID);
if Assigned(CurrentProvider) then
FCurrentFunction := CurrentProvider.Find(Button.FunctionUID);
@ -519,6 +523,16 @@ begin
end;
procedure TButtonFunctionForm.vstFunctionsFreeNode(Sender: TBaseVirtualTree; Node: PVirtualNode);
var
nodeData: PFunctionNodeData;
begin
nodeData := Sender.GetNodeData(Node);
Finalize(nodeData^);
end;
procedure TButtonFunctionForm.vstFunctionsFocusChanged(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex);
var
nodeData: PFunctionNodeData;

View File

@ -24,6 +24,7 @@ uses
X2UtPersistIntf,
ControlIntf,
LEDFunctionRegistry,
FSXSimConnectIntf,
LEDStateConsumer,
Profile,
@ -163,6 +164,8 @@ type
FSettingsFileName: string;
FSettings: TSettings;
FLoadingSettings: Boolean;
FFunctionRegistry: TLEDFunctionRegistry;
protected
procedure RegisterDeviceArrival;
procedure UnregisterDeviceArrival;
@ -219,6 +222,7 @@ type
property StateConsumerTask: IOmniTaskControl read FStateConsumerTask;
property Log: IX2Log read FLog;
property FunctionRegistry: TLEDFunctionRegistry read FFunctionRegistry;
end;
@ -249,7 +253,6 @@ uses
G940LEDStateConsumer,
LEDColorIntf,
LEDFunctionIntf,
LEDFunctionRegistry,
StaticLEDFunction,
StaticResources;
@ -324,19 +327,20 @@ begin
SetFSXState(TextFSXDisconnected, False);
TLEDFunctionRegistry.Register(TStaticLEDFunctionProvider.Create);
FFunctionRegistry := TLEDFunctionRegistry.Create;
FunctionRegistry.Register(TStaticLEDFunctionProvider.Create);
SetLength(scriptPaths, 2);
scriptPaths[0] := App.Path + FSXScriptsPath;
scriptPaths[1] := App.UserPath + UserDataPath + FSXScriptsPath;
TLEDFunctionRegistry.Register(TFSXLEDFunctionProvider.Create(scriptPaths));
FunctionRegistry.Register(TFSXLEDFunctionProvider.Create(scriptPaths));
FEventMonitor := TOmniEventMonitor.Create(Self);
Log.Info('Starting G940 LED state consumer thread');
worker := TG940LEDStateConsumer.Create(TX2GlobalLog.Category('G940 LED state consumer'));
worker := TG940LEDStateConsumer.Create(TX2GlobalLog.Category('G940 LED state consumer'), FunctionRegistry);
FStateConsumerTask := EventMonitor.Monitor(CreateTask(worker));
EventMonitor.OnTaskMessage := EventMonitorMessage;
@ -379,6 +383,14 @@ begin
TX2LogObserverMonitorForm.CloseInstance(TX2GlobalLog.Instance);
TX2LogObserverMonitorForm.UnlockInstance(TX2GlobalLog.Instance);
if Assigned(StateConsumerTask) then
begin
StateConsumerTask.Stop;
StateConsumerTask.WaitFor(INFINITE);
end;
FreeAndNil(FFunctionRegistry);
end;
@ -726,7 +738,7 @@ begin
end;
buttonFunction := nil;
provider := TLEDFunctionRegistry.Find(providerUID);
provider := FunctionRegistry.Find(providerUID);
if Assigned(provider) then
buttonFunction := provider.Find(functionUID);
@ -925,7 +937,7 @@ begin
end;
buttonIndex := (Sender as TComponent).Tag;
if TButtonFunctionForm.Execute(profile, buttonIndex) then
if TButtonFunctionForm.Execute(FunctionRegistry, profile, buttonIndex) then
begin
if newProfile then
AddProfile(profile);
@ -1308,7 +1320,7 @@ var
fsxProvider: IFSXLEDFunctionProvider;
begin
if Supports(TLEDFunctionRegistry.Find(FSXProviderUID), IFSXLEDFunctionProvider, fsxProvider) then
if Supports(FunctionRegistry.Find(FSXProviderUID), IFSXLEDFunctionProvider, fsxProvider) then
fsxProvider.SetProfileMenu(Settings.ProfileMenu, Settings.ProfileMenuCascaded);
end;
@ -1318,7 +1330,7 @@ var
fsxProvider: IFSXLEDFunctionProvider;
begin
if Supports(TLEDFunctionRegistry.Find(FSXProviderUID), IFSXLEDFunctionProvider, fsxProvider) then
if Supports(FunctionRegistry.Find(FSXProviderUID), IFSXLEDFunctionProvider, fsxProvider) then
fsxProvider.SetProfileMenu(False, False);
end;

View File

@ -181,10 +181,10 @@ end;
destructor TFSXLEDFunctionProvider.Destroy;
begin
inherited Destroy;
FreeAndNil(FScriptSimConnect);
FreeAndNil(FSimConnectLock);
inherited Destroy;
end;

View File

@ -13,24 +13,16 @@ type
TLEDFunctionRegistry = class(TObject)
private
FProviders: TLEDFunctionProviderList;
protected
class function Instance: TLEDFunctionRegistry;
procedure DoRegister(AProvider: ILEDFunctionProvider);
procedure DoUnregister(AProvider: ILEDFunctionProvider);
function DoFind(const AUID: string): ILEDFunctionProvider;
function GetProviders: TLEDFunctionProviderList;
public
constructor Create;
destructor Destroy; override;
class procedure Register(AProvider: ILEDFunctionProvider);
class procedure Unregister(AProvider: ILEDFunctionProvider);
procedure Register(AProvider: ILEDFunctionProvider);
procedure Unregister(AProvider: ILEDFunctionProvider);
class function Find(const AUID: string): ILEDFunctionProvider;
function Find(const AUID: string): ILEDFunctionProvider;
class function Providers: TLEDFunctionProviderList;
function Providers: TLEDFunctionProviderList;
end;
@ -64,44 +56,7 @@ uses
SysUtils;
var
RegistryInstance: TLEDFunctionRegistry;
{ TLEDFunctionRegistry }
class procedure TLEDFunctionRegistry.Register(AProvider: ILEDFunctionProvider);
begin
Instance.DoRegister(AProvider);
end;
class procedure TLEDFunctionRegistry.Unregister(AProvider: ILEDFunctionProvider);
begin
Instance.DoUnregister(AProvider);
end;
class function TLEDFunctionRegistry.Find(const AUID: string): ILEDFunctionProvider;
begin
Result := Instance.DoFind(AUID);
end;
class function TLEDFunctionRegistry.Providers: TLEDFunctionProviderList;
begin
Result := Instance.GetProviders;
end;
class function TLEDFunctionRegistry.Instance: TLEDFunctionRegistry;
begin
if not Assigned(RegistryInstance) then
RegistryInstance := TLEDFunctionRegistry.Create;
Result := RegistryInstance;
end;
constructor TLEDFunctionRegistry.Create;
begin
inherited Create;
@ -118,25 +73,25 @@ begin
end;
procedure TLEDFunctionRegistry.DoRegister(AProvider: ILEDFunctionProvider);
procedure TLEDFunctionRegistry.Register(AProvider: ILEDFunctionProvider);
begin
FProviders.Add(AProvider);
end;
procedure TLEDFunctionRegistry.DoUnregister(AProvider: ILEDFunctionProvider);
procedure TLEDFunctionRegistry.Unregister(AProvider: ILEDFunctionProvider);
begin
FProviders.Remove(AProvider);
end;
function TLEDFunctionRegistry.DoFind(const AUID: string): ILEDFunctionProvider;
function TLEDFunctionRegistry.Find(const AUID: string): ILEDFunctionProvider;
begin
Result := FProviders.Find(AUID);
end;
function TLEDFunctionRegistry.GetProviders: TLEDFunctionProviderList;
function TLEDFunctionRegistry.Providers: TLEDFunctionProviderList;
begin
Result := FProviders;
end;
@ -208,9 +163,4 @@ begin
Result := ((inherited GetCurrent) as ILEDFunctionProvider);
end;
initialization
finalization
FreeAndNil(RegistryInstance);
end.

View File

@ -11,6 +11,7 @@ uses
LEDColorIntf,
LEDFunctionIntf,
LEDFunctionRegistry,
Profile;
@ -28,6 +29,7 @@ type
FButtonColors: TInterfaceList;
FHasTickTimer: Boolean;
FLog: IX2Log;
FFunctionRegistry: TLEDFunctionRegistry;
protected
function Initialize: Boolean; override;
procedure Cleanup; override;
@ -45,11 +47,12 @@ type
procedure Update; virtual; abstract;
property Log: IX2Log read FLog;
property FunctionRegistry: TLEDFunctionRegistry read FFunctionRegistry;
protected
procedure TMLoadProfile(var Msg: TOmniMessage); message TM_LOADPROFILE;
procedure TMTick(var Msg: TOmniMessage); message TM_TICK;
public
constructor Create(ALog: IX2Log);
constructor Create(ALog: IX2Log; AFunctionRegistry: TLEDFunctionRegistry);
end;
@ -59,7 +62,6 @@ uses
System.SysUtils,
Winapi.Windows,
LEDFunctionRegistry,
LEDStateIntf;
@ -82,11 +84,12 @@ type
{ TLEDStateConsumer }
constructor TLEDStateConsumer.Create(ALog: IX2Log);
constructor TLEDStateConsumer.Create(ALog: IX2Log; AFunctionRegistry: TLEDFunctionRegistry);
begin
inherited Create;
FLog := ALog;
FFunctionRegistry := AFunctionRegistry;
end;
@ -118,7 +121,7 @@ var
begin
Result := nil;
provider := TLEDFunctionRegistry.Find(AProfileButton.ProviderUID);
provider := FunctionRegistry.Find(AProfileButton.ProviderUID);
if Assigned(provider) then
begin
ledFunction := provider.Find(AProfileButton.FunctionUID);

View File

@ -6,6 +6,8 @@ uses
System.SysUtils,
System.Types,
X2Log.Intf,
LEDFunction,
LEDFunctionIntf,
LEDStateIntf,
@ -50,6 +52,12 @@ type
protected
function CreateLuaLEDFunction(AInfo: ILuaTable; ASetup: ILuaFunction): TCustomLuaLEDFunction; virtual; abstract;
procedure AppendVariable(ABuilder: TStringBuilder; AVariable: ILuaVariable);
procedure AppendTable(ABuilder: TStringBuilder; ATable: ILuaTable);
procedure DoLog(AContext: ILuaContext; ALevel: TX2LogLevel);
procedure DoLogMessage(AContext: ILuaContext; ALevel: TX2LogLevel; const AMessage: string);
procedure ScriptRegisterFunction(Context: ILuaContext);
procedure ScriptSetState(Context: ILuaContext);
@ -91,7 +99,6 @@ uses
System.IOUtils,
Lua.API,
X2Log.Intf,
X2Log.Global,
LEDColorIntf,
@ -99,18 +106,15 @@ uses
type
TLuaLogProc = reference to procedure(AContext: ILuaContext; ALevel: TX2LogLevel);
TLuaLog = class(TPersistent)
private
FInterpreter: TLua;
FOnLog: TLuaLogProc;
protected
procedure AppendVariable(ABuilder: TStringBuilder; AVariable: ILuaVariable);
procedure AppendTable(ABuilder: TStringBuilder; ATable: ILuaTable);
procedure Log(AContext: ILuaContext; ALevel: TX2LogLevel);
property Interpreter: TLua read FInterpreter;
property OnLog: TLuaLogProc read FOnLog;
public
constructor Create(AInterpreter: TLua);
constructor Create(AOnLog: TLuaLogProc);
published
procedure Verbose(Context: ILuaContext);
procedure Info(Context: ILuaContext);
@ -149,7 +153,7 @@ begin
FWorkers := TDictionary<string, TCustomLuaLEDFunctionWorker>.Create;
FInterpreter := TLua.Create;
FScriptFolders := AScriptFolders;
FScriptLog := TLuaLog.Create(FInterpreter);
FScriptLog := TLuaLog.Create(DoLog);
InitInterpreter;
@ -159,11 +163,11 @@ end;
destructor TCustomLuaLEDFunctionProvider.Destroy;
begin
inherited Destroy;
FreeAndNil(FInterpreter);
FreeAndNil(FScriptLog);
FreeAndNil(FWorkers);
inherited Destroy;
end;
@ -189,6 +193,87 @@ begin
end;
procedure TCustomLuaLEDFunctionProvider.AppendVariable(ABuilder: TStringBuilder; AVariable: ILuaVariable);
begin
case AVariable.VariableType of
VariableBoolean:
if AVariable.AsBoolean then
ABuilder.Append('true')
else
ABuilder.Append('false');
VariableTable:
AppendTable(ABuilder, AVariable.AsTable);
else
ABuilder.Append(AVariable.AsString);
end;
end;
procedure TCustomLuaLEDFunctionProvider.AppendTable(ABuilder: TStringBuilder; ATable: ILuaTable);
var
firstItem: Boolean;
item: TLuaKeyValuePair;
begin
ABuilder.Append('{ ');
firstItem := True;
for item in ATable do
begin
if firstItem then
firstItem := False
else
ABuilder.Append(', ');
AppendVariable(ABuilder, item.Key);
ABuilder.Append(' = ');
AppendVariable(ABuilder, item.Value);
end;
ABuilder.Append(' }');
end;
procedure TCustomLuaLEDFunctionProvider.DoLog(AContext: ILuaContext; ALevel: TX2LogLevel);
var
msg: TStringBuilder;
parameter: ILuaVariable;
begin
msg := TStringBuilder.Create;
try
for parameter in AContext.Parameters do
begin
AppendVariable(msg, parameter);
msg.Append(' ');
end;
DoLogMessage(AContext, ALevel, msg.ToString);
finally
FreeAndNil(msg);
end;
end;
procedure TCustomLuaLEDFunctionProvider.DoLogMessage(AContext: ILuaContext; ALevel: TX2LogLevel; const AMessage: string);
var
debug: lua_Debug;
fileName: string;
begin
fileName := 'Lua';
if Interpreter.HasState and (lua_getstack(Interpreter.State, 1, debug) <> 0) then
begin
lua_getinfo(Interpreter.State, 'Sl', debug);
fileName := fileName + ' - ' + string(debug.source)
end;
TX2GlobalLog.Log(ALevel, AMessage, filename);
end;
procedure TCustomLuaLEDFunctionProvider.ScriptRegisterFunction(Context: ILuaContext);
var
info: ILuaTable;
@ -210,6 +295,7 @@ begin
if not info.HasValue('uid') then
raise ELuaScriptError.Create('"uid" value is required for RegisterFunction parameter 1');
DoLogMessage(Context, TX2LogLevel.Info, Format('Registering function: %s', [info.GetValue('uid').AsString]));
RegisterFunction(CreateLuaLEDFunction(info, setup));
end;
@ -237,6 +323,7 @@ begin
if not Assigned(worker) then
raise ELuaScriptError.Create('Context expected for SetState parameter 1');
DoLogMessage(Context, TX2LogLevel.Info, Format('Setting state for %s to: %s', [worker.GetFunctionUID, stateUID]));
worker.SetCurrentState(stateUID);
end;
@ -397,108 +484,35 @@ end;
{ TLuaLog }
constructor TLuaLog.Create(AInterpreter: TLua);
constructor TLuaLog.Create(AOnLog: TLuaLogProc);
begin
inherited Create;
FInterpreter := AInterpreter;
end;
procedure TLuaLog.AppendVariable(ABuilder: TStringBuilder; AVariable: ILuaVariable);
begin
case AVariable.VariableType of
VariableBoolean:
if AVariable.AsBoolean then
ABuilder.Append('true')
else
ABuilder.Append('false');
VariableTable:
AppendTable(ABuilder, AVariable.AsTable);
else
ABuilder.Append(AVariable.AsString);
end;
end;
procedure TLuaLog.AppendTable(ABuilder: TStringBuilder; ATable: ILuaTable);
var
firstItem: Boolean;
item: TLuaKeyValuePair;
begin
ABuilder.Append('{ ');
firstItem := True;
for item in ATable do
begin
if firstItem then
firstItem := False
else
ABuilder.Append(', ');
AppendVariable(ABuilder, item.Key);
ABuilder.Append(' = ');
AppendVariable(ABuilder, item.Value);
end;
ABuilder.Append(' }');
end;
procedure TLuaLog.Log(AContext: ILuaContext; ALevel: TX2LogLevel);
var
debug: lua_Debug;
fileName: string;
msg: TStringBuilder;
parameter: ILuaVariable;
begin
fileName := 'Lua';
if Interpreter.HasState and (lua_getstack(Interpreter.State, 1, debug) <> 0) then
begin
lua_getinfo(Interpreter.State, 'Sl', debug);
fileName := fileName + ' - ' + string(debug.source)
end;
msg := TStringBuilder.Create;
try
for parameter in AContext.Parameters do
begin
AppendVariable(msg, parameter);
msg.Append(' ');
end;
TX2GlobalLog.Log(ALevel, msg.ToString, fileName);
finally
FreeAndNil(msg);
end;
FOnLog := AOnLog;
end;
procedure TLuaLog.Verbose(Context: ILuaContext);
begin
Log(Context, TX2LogLevel.Verbose);
OnLog(Context, TX2LogLevel.Verbose);
end;
procedure TLuaLog.Info(Context: ILuaContext);
begin
Log(Context, TX2LogLevel.Info);
OnLog(Context, TX2LogLevel.Info);
end;
procedure TLuaLog.Warning(Context: ILuaContext);
begin
Log(Context, TX2LogLevel.Warning);
OnLog(Context, TX2LogLevel.Warning);
end;
procedure TLuaLog.Error(Context: ILuaContext);
begin
Log(Context, TX2LogLevel.Error);
OnLog(Context, TX2LogLevel.Error);
end;
end.