Results 1 to 10 of 48

Thread: Cheb's project will be here.

Hybrid View

Previous Post Previous Post   Next Post Next Post
  1. #1
    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.
    Last edited by Chebmaster; 22-03-2020 at 09:24 AM.

  2. #2
    I give too little time to this project.

    Made my engine compile and work using 3.2.0-rc1, check. Pain in the butt with strings, you *cannot* use the "+" operator to concatenate them, they are guaranteed to be wrecked horribly.

    Now bringing Win98 support back. Restored a lot of neglected/commented out code designed to work in 98: it has so many vital WinAPI functions missing! I made a dedicated conditional CGE_PLATFORM_HAS_WINDOWS98 and wrap any relevant code in it. Because that would only work if the mother exe and the module DLL are compiled using fpc 2.6.4 (because lots of WinAPI functions have to be loaded manually, a program built using fpc 3 would crash in Win98 due to unsatisfied dependencies). So I have to support fpc 2.6.4 for all eternity patching all of its bugs by myself (the lack of Unicode support, the inability to catch AVs in a DLL, the incorrectly working functions like BsfDword - I have successfully patched them all in my engine. Took a lot of effort and dedication).

    The mother EXE for Win32 will always be compiled using fpc 2.6.4, with optimization set to pentium 3/x87 to keep compatibility with Athlon XP/Pentium III. The module DLL will normally be compiled using fpc 3.2/pentium m/sse2 but there will always be a separate legacy version built using fpc 2.6.4/pentium 3/x87

    Scavenged me a Radeon 9800 Pro 128Mb AGP (it has a molex power connector, LOL). Downloaded specific Catalyst for it to work in 98. Now I only have to install it into that machine and actually install Win98 on it.

    Examples of dirty tricks Win98 support requires:
    Code:
      function TWinApiFramework.GetScreenRect(fullscreen: boolean): TWindowManagerRect;
      var
        rc: TRect;
        Monitor: HMONITOR;
        mi: TMonitorInfo;
      begin
        {$ifdef CGE_PLATFORM_HAS_WINDOWS98}
         if (Mother^.State.OS in [ostWin98, OstWin2k]) 
           and (not Assigned(GetMonitorInfo) or not Assigned(MonitorFromRect))
         then with Result do begin
           //Use legacy method fow windozes older than XP
           left:= 0;
           top := 0;
           width:= GetSystemMetrics(SM_CXSCREEN);
           height:= GetSystemMetrics(SM_CYSCREEN);
           //Assuming that taskbar is at the bottom and is 28 pixels high:
           if not Fullscreen then Height-= 28;
         end
         else 
        {$endif}
        begin
          if windowhandle = 0 then begin
            //Not created yet. Use default monitor.
             with rc do begin
               left:= 0;
               top:= 0;
               right:= 1;
               bottom:= 1;
             end;
             Monitor:= MonitorFromRect(rc, MONITOR_DEFAULTTOPRIMARY);
          end else begin
            //get monitor from window
            GetWindowRect(windowhandle, rc);
            Monitor:= MonitorFromRect(rc, MONITOR_DEFAULTTONEAREST);
          end;
          mi.cbSize:= sizeof(mi);
          GetMonitorInfo(Monitor, @mi);
          if fullscreen
            then rc:= mi.rcMonitor
            else rc:= mi.rcWork;
          Result.left:= rc.left;
          Result.top:= rc.top;
          Result.width:= rc.right - rc.left;
          Result.height:= rc.bottom - rc.top;
        end;
      end;

  3. #3
    PGDCE Developer de_jean_7777's Avatar
    Join Date
    Nov 2006
    Location
    Bosnia and Herzegovina (Herzegovina)
    Posts
    287
    That's a lot of dedication to support windows 98. I was thinking about it too, but it may make more sense to eventually try to add a win9x platform back into fpc 3.2 rather than use 2.6.4, if at all possible. Since it can work for DOS, I assume it should be able to work for Windows 9x. Since I depend on generics a lot, I can't go back below 3.0.4. But this is not a priority for now, or maybe ever.
    Existence is pain

  4. #4
    Well, I started developing my engine back in 2003 when I was still using Win98. I kept going at it when I finally moved to Win2k in 2004.

    Quote Originally Posted by de_jean_7777 View Post
    but it may make more sense to eventually try to add a win9x platform back into fpc 3.2
    Not a chance.
    Why? Because it would require making Windows unit use dynamic loading, making a lot of the functions declared there procedural variables.
    The Windows unit in 2.6.4 was effectively *crippled* because of Win98 support in that compiler version. It misses a lot of really useful functions you have to add by yourself if you use 2.6.4.

    I also believe a lot of string handling routines would have to be adapted to detect Win98 and hack around its limitations... which is not always possible, because how do you determine the system locale? It's not always possible. I use a dirty hack "if no Unicode support found assume Russian/cp1251", such things would not fly for the fpc rtl.

    There are too few people interested in Win98.

    WinXP, on the other hand... But WinXP already has all of the WinAPI functions there and working -- except maybe some exotic ones endemic to Win7/8/10. But these should be exceedingly rare.

    Also, there is an abyss of time between WinXP (supported til 2014) and Win98 SE installed from an original CD of 1999 I am aiming to support.

  5. #5
    Postponed again, due to one definitely, decidedly, I swear I'll never do that again, rehaul.

    1. Compatibility with fpc 2.6.4 dropped forever (now requires 3.2 on Windows and 3.0.4 on Linux)
    2. Support of Windows 98 dropped forever (now requires XP and up).
    3. My unique split architecture is limited to developer mode only, which is limited to Win32 only. Now by default the modules are parts of the main executable.
    Reason: exception handling in threads created by DLLs still does not work in Linux, requiring an exception hack. Doable (I made one for Win32, after all) but why waste so much effort?
    Also saves extra effort on not having to interface a lot of bells and whistles things between the mother exe and the module dll (which is a royal pain because you have to dumb everything down to a C-like API with pointers instead of arrays and strings). Lots of purely cosmetic things are cut out of the devmode DLL now. The DLL doesn't know anything about mother's modules list, on clicking module select it passes control to the hub module built into the exe. It can't have custom loading screen background. And so on.

    P.S. Just look at that double wrapper which allows the DLL working with TSTream instances created by the mother executable. First it is cast to ptruint and called "handle" and a small API made to access its methods via flat cdecl functions accepting handle as one of the parameters. Then, on the DLL side, a class is derived from TStream where methods call on that mother API. Results in a perfectly working TStream on both sides, but implementing it was so much pain

  6. #6
    An embarrassing oopsnik about forgetting edge cases.
    I was using per-thread records to store exception data, error messages & backtraces, self-profiling counters.
    Instead of threadvar it was organized as a linked list so every time pointer to it was asked, the chain was searched for one with the right thread id.
    Last Sunday I finally redid it using threadvar.
    I did not change the API, the function is still called and still allocates the record the first time it is asked for. It just uses threadvar to store and retrieve it. But the linked list is still used, reserved for exactly two cases: the module's thread manager collating error messages from all threads at shutdown and the histogram renderer, which iterates over all known threads.
    Looking at histograms made me realize the blunder causing uncaught exceptions on exit.

    See that mysterious unnamed thread and the main thread's bar being empty?
    That's where I forgot that the record for the main thread should not be allocated via New, the pointer must point to a specific global variable which is the start of the linked list.

    I also had enough with the horribly convoluted and distributed state machine of the control panel (which I am still trying to wrangle into a separate built-inmodule) playing tricks and taking me on wild goose chases.
    I nuked it and am now re-making it from scratch. As I should have from the start.

    Also, I decided to make the control of the verbose log (aka verbal diarrhea mode) granular instead of "on" and "off". Finding points of interest in kilometer-long walls of text was becoming tiresome. Soon I will be able to switch verbose logging separately for gapi, database, framework, path processing, loading external function pointers and so on.
    The debugging executable version would be left for cases of extreme debugging.

  7. #7
    I'm having problems with my engine logs too, so I'll redesign it. I think I'll define a empty "Log" class that just skips logs and extend it in a class that actually writes the log files. Then you can create as many log objects you want (maybe just an empty one) then you can assign them to each engine's subsystem. This way you can organize them as you want. For example, if you want no logs, assign the empty one to all; if you want a different log file per subsystem, create as many as you need; for only one big huge file, create one and assign it.
    No signature provided yet.

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •