Trying trunk 3.3.1 aka the larva of 3.2.
I was glad to see the exception handling in threads created by a DLL is working now out of the box. Interestingly, it seems having no conflicts with existing Win32 SEH handlers (as each driver DLL you load likely installs its own, I tested and confirmed that) but RTL 3.3.1 doesn't install its own handler.
I wasn't able to figure it out *how* exception handling of 3.3.1 works, it's a black box for me now.
Test:
Code:
program thrtesta;
{$mode objfpc}
{$apptype console}
{$longstrings on}
uses
{$ifdef unix}
cthreads,
{$endif}
SysUtils,
Classes
{$ifdef unix}
, dl
{$else}
, windows
{$endif}
;
type
TTestThread = class(TThread)
protected
procedure Execute; override;
end;
procedure TTestThread.Execute;
begin
WriteLn('> A');
try
byte(nil^):= 0;
except
WriteLn('exe thread ID=',GetCurrentThreadId()
,' catch: ',(ExceptObject as Exception).Message);
end;
WriteLn('< A');
end;
function PCharToString(P: PAnsiChar): Utf8String;
var
i: integer;
p2: PAnsiChar;
begin
if not Assigned(p) then Result:= ''
else begin
p2:= p;
i:= 0;
While p2^ <> #0 do begin
inc(p2);
inc(i);
end;
SetLength(Result, i);
MOVE(p^, Result[1], i);
end;
end;
var
t: TTestThread;
dllhandle: {$ifdef unix} pointer {$else} THandle {$endif};
mypath: string;
{$ifdef unix}
ufn, upn: Utf8String;
{$else}
wfn: UnicodeString;
wpn: AnsiString;
{$endif}
thrproc: procedure; cdecl = nil;
begin
WriteLn('the exe is built using fpc '
,{$I %FPCVERSION%},'/',{$I %FPCTARGETOS%},'/',{$I %FPCTARGETCPU%});
WriteLn('main thread ID=', GetCurrentTHreadId());
t := TTestThread.Create(False);
WriteLn('exe thread created');
try
t.WaitFor;
finally
t.Free;
end;
WriteLn('exe thread terminated');
WriteLn('loading the DLL...');
mypath:= ExtractFilePath(ParamStr(0))
+ {$ifdef unix} 'libthrtestb.so' {$else} 'thrtestb.dll' {$endif};
WriteLn('path is ', mypath);
{$ifdef unix}
ufn:= mypath;
dllhandle:= dlopen(PAnsiChar(nu8), RTLD_NOW);
if not Assigned(DLL) then begin
WriteLn('failed to load: ',PCharToString(dlerror()));
Halt(0);
end;
upn:= 'thrproc';
pointer(thrproc):= dlsym(dllhandle, PAnsiChar(upn));
{$else}
wfn:= mypath;
SetLastError(0);
dllhandle:= LoadLibraryW(PUcs2Char(wfn));
if dllhandle = 0 then begin
WriteLn('failed to load.');
Halt(0);
end;
wpn:= 'thrproc';
pointer(thrproc):= windows.GetProcAddress(dllhandle, PAnsiChar(wpn));
{$endif}
if not Assigned(pointer(thrproc)) then begin
WriteLn('failed to load the procedure.');
Halt(0);
end;
WriteLn('invoking the dll...');
try
thrproc;
except
WriteLn('exe thread ID=',GetCurrentThreadId()
,' catch: ',(ExceptObject as Exception).Message);
end;
WriteLn('unloading the dll...');
{$ifdef unix}
dlClose(dllhandle);
{$else}
FreeLibrary(dllhandle);
{$endif}
WriteLn('done.');
end.
Code:
library thrtestb;
{$mode objfpc}
{$apptype console}
{$longstrings on}
uses
{$ifdef unix}
cthreads,
{$endif}
SysUtils,
Classes;
type
TTestThread = class(TThread)
protected
procedure Execute; override;
end;
procedure TTestThread.Execute;
begin
WriteLn('> X');
try
WriteLn('> Y');
try
WriteLn('> Z');
try
byte(nil^):= 0;
except
WriteLn('dll thread ID=',GetCurrentThreadId()
,' catch in block Z: ',(ExceptObject as Exception).Message);
end;
WriteLn('< Z');
except
WriteLn('dll thread ID=',GetCurrentThreadId()
,' catch in block Y: ',(ExceptObject as Exception).Message);
end;
WriteLn('< Y');
except
WriteLn('dll thread ID=',GetCurrentThreadId()
,' catch in block X: ',(ExceptObject as Exception).Message);
end;
WriteLn('< X');
end;
procedure MyMainProc; cdecl;
var t: TThread;
begin
WriteLn('the dll is built using fpc '
,{$I %FPCVERSION%},'/',{$I %FPCTARGETOS%},'/',{$I %FPCTARGETCPU%});
try
t := TTestThread.Create(False);
WriteLn('dll thread created');
try
t.WaitFor;
finally
t.Free;
end;
except
WriteLn('dll thread ID=',GetCurrentThreadId()
,' catch in main proc: ',(ExceptObject as Exception).Message);
end;
WriteLn('the dll is done.')
end;
exports
MyMainProc name 'thrproc';
begin
// do nothing.
// The initialization sections DO NOT WORK in Linux for DLLs. And never will.
end.
Still a crash-to-desktop in 3.0.4:
the exe is built using fpc 3.0.4/Win32/i386
main thread ID=6444
exe thread created
> A
exe thread ID=6736 catch: Access violation
< A
exe thread terminated
loading the DLL...
path is d:chentrahmodulesteststhrtestb.dll
invoking the dll...
the dll is built using fpc 3.0.4/Win32/i386
dll thread created
> X
> Y
> Z
An unhandled exception occurred at $1000165F:
EAccessViolation: Access violation
Works perfectly in 3.3.1, the 17-year old bug finally closed:
the exe is built using fpc 2.6.4/Win32/i386
main thread ID=780
exe thread created
> A
exe thread ID=6232 catch: Access violation
< A
exe thread terminated
loading the DLL...
path is d:chentrahmodulesteststhrtestb.dll
invoking the dll...
the dll is built using fpc 3.3.1/Win32/i386
dll thread created
> X
> Y
> Z
dll thread ID=6076 catch in block Z: Access violation
< Z
< Y
< X
the dll is done.
unloading the dll...
done.
On the engine front, I postpone trying 3.3.1 to autumn 2020 (sadly also postponing x86-64 support that requires it) and concentrate on making my engine usable using 3.0.4.
The reason is difference in RTTI that necessitates making adjustments to my persistency system (and debugging them, dammit!)
It will be ready in a month or two, with a simple asteroids game so that I could finally shave after all those years.
Bookmarks