Jump to content

A.M. Hoornweg

Members
  • Content Count

    446
  • Joined

  • Last visited

  • Days Won

    8

Posts posted by A.M. Hoornweg


  1.  

    2 hours ago, Cristian Peța said:
    
    procedure Foo; stdcall;
    begin
      SetFPCR;
      ...
      RestoreFPCR;
    end;

    Do you think is so cumbersome to do this for every exposed function?

    The SetFPCR and RestoreFPCR you need to write for yourself but only once.

    It would be a can of worms for me I'm afraid. I am thinking of all my Delphi COM DLL's that expose interfaces and class factories (see image). Each interface is basically an object that can have dozens of methods so we're not talking about just a few functions, it's more like hundreds of exposed methods.  And many of these methods call each other, which complicates matters further because setfpcr/restorefpcr would have to support nesting.  And multi-threading would make matters even more complicated.

    ridl.png


  2. 11 minutes ago, David Heffernan said:

    One very obvious problem is that you need to write code like this everywhere. Not just at the top level. I stand by my advice before. Either:

     

    1. Make FPCR part of the contract, or

    2. Take control on entry, and restore on exit.

    "Take control on entry and restore on exit" would be very cumbersome in the case of DLL's written in Delphi.  It would need to be done in every exposed function / method.

    (edit) or at least in every method that has to do with FP calculations.


  3. 2 hours ago, Dalija Prasnikar said:

    FPCR is part of the FPU and its state is preserved during context switch. So each thread works with its own state that is independent of others. If you only handle floating point state directly through FPCR it will be thread-safe. Problem with Delphi is that it throws global variable into the equation and then handles FPCR with the help of that global in thread-unsafe manner, which can then leak "wrong" state into different thread.

    That is not what I mean, since a "context switch" is done when the scheduler switches from one thread to another (so one is halted, its state is saved and the next thread resumes after its state was restored). 

    My question is about threads literally running simultaneously on different CPU cores, does each core always have an independent FPU + FPCR so one thread cannot jeopardize another ? 

     

     

     

     

     

     

     

     

     


  4. 10 hours ago, David Heffernan said:

    DLLs should take charge of this. They should either make it part of their contract that the host sets the fp control state to a specific state. Or they should set it on entry, and restore it on exit. My DLL does the latter. 

     

    Since threads run concurrently on multi-core systems, isn't the fp control state global to all threads? Or does every cpu core have its own fpu core?

     


  5. 34 minutes ago, Lajos Juhász said:

    It really depends how many Delphi versions you have to support. The change is documented and was made to make life easier to integrate with other programming languages. Mora information:

    https://dalijap.blogspot.com/2023/09/coming-in-delphi-12-disabled-floating.html

    https://docwiki.embarcadero.com/RADStudio/Athens/en/Floating_Point_Operation_Exception_Masks

     

    Sure it is documented. But it may be off the radar that existing binaries such as DLL's may suddenly behave differently if they were written in Delphi.  I use some third-party DLL's that were written in Delphi.

     

     

     

     

     

     

     


  6. Hello all,

     

    I've noticed that an application I'm working on was behaving differently when compiled in Delphi 11 or 12.  In Delphi 12 I was getting all sorts of overflows and unexpected behavior. It took me a while to figure out the cause. 

     

    Now the surprising thing: The cause wasn't even in this executable I was working on, it was in a DLL that my program is using. This DLL was built with a previous version of Delphi.

     

    This particular DLL expects that a FP error such as a division by zero will throw an exception.  A breaking change in Delphi 12 is that the executables it produces mask the FP exceptions. This change in behavior affects all code running in the process, including all DLLs. In other words, this DLL no longer threw exceptions where it was supposed to.

     

    My workaround was to re-enable FPU exceptions in the executable and the situation instantly went "back to normal".

     

    But it got me thinking, how does one write code that copes with both FP situations and can run safely with *any* Delphi version?  Should I do something like this?

    Try
       c:=SomeHighlyComplexCalculation();
       if IsNan (c) then
         raise eDivByZero.Create('Something went wrong');
    except
       // the exception is triggered either automatically (Old Delphi version) or manually (New Delphi version)
    end;

     

    Currently, my gut feeling tells me to manually enable FPU exceptions in all my Delphi exe projects to prevent such nasty surprises in the future. I use tons of tried-and-tested DLLs written in Delphi and I really don't want them to break.

     

     

     

     

     

     

     

     

     

     

     

    • Like 1

  7. I am getting "Disconnected Session" all the time whilst working on a 64-bit project.  Never had this problem in 12.0.

     

    The error dialog window contains this information:

     

    [69E0B1C0]{dbkdebugide290.bpl} Debug.TDebugKernel.msgBox (Line 5951, "Debug.pas" + 30) + $0
    [2BCC6EB3]{bordbk290.dll} Unknown function at DllUnregisterServer + $5037
    [2BD3B88C]{bordbk290.dll} Unknown function at @isDbkLoggingOn$qv + $619B4
    [2BD3B90B]{bordbk290.dll} Unknown function at @isDbkLoggingOn$qv + $61A33
    [2BD3C351]{bordbk290.dll} Unknown function at @isDbkLoggingOn$qv + $62479
    [2BD3C5A1]{bordbk290.dll} Unknown function at @isDbkLoggingOn$qv + $626C9
    [2BD6C3D3]{bordbk290.dll} Unknown function at @isDbkLoggingOn$qv + $924FB
    [69E0B6FD]{dbkdebugide290.bpl} Debug.TThread.DoGetCurAddr (Line 6080, "Debug.pas" + 1) + $4
    [2BCD9D72]{bordbk290.dll} Unknown function at DllUnregisterServer + $17EF6
    [2BD6A91D]{bordbk290.dll} Unknown function at @isDbkLoggingOn$qv + $90A45
    [2BD3C577]{bordbk290.dll} Unknown function at @isDbkLoggingOn$qv + $6269F
    [2BD3C7E0]{bordbk290.dll} Unknown function at @isDbkLoggingOn$qv + $62908
    [2BCC6A4B]{bordbk290.dll} Unknown function at DllUnregisterServer + $4BCF
    [2BD5AB32]{bordbk290.dll} Unknown function at @isDbkLoggingOn$qv + $80C5A
    [2BD5AB3C]{bordbk290.dll} Unknown function at @isDbkLoggingOn$qv + $80C64
    [69E17AFD]{dbkdebugide290.bpl} Debug.GetNextEvent (Line 11764, "Debug.pas" + 1) + $24
    [6DC6037A]{rtl290.bpl  } System.@IsClass (Line 18904, "System.pas" + 1) + $8
    [69E185E1]{dbkdebugide290.bpl} Debug.TDebugger.UpdateEventLog (Line 11776, "Debug.pas" + 9) + $1
    [69E11931]{dbkdebugide290.bpl} Debug.TProcess.ntfyNewEventLogData (Line 9020, "Debug.pas" + 1) + $7
    [2BD5B3A0]{bordbk290.dll} Unknown function at @isDbkLoggingOn$qv + $814C8
    [2BD3B90B]{bordbk290.dll} Unknown function at @isDbkLoggingOn$qv + $61A33
    [2BD3C577]{bordbk290.dll} Unknown function at @isDbkLoggingOn$qv + $6269F
    [2BD570FB]{bordbk290.dll} Unknown function at @isDbkLoggingOn$qv + $7D223
    [2BD57136]{bordbk290.dll} Unknown function at @isDbkLoggingOn$qv + $7D25E
    [2BD6C4C7]{bordbk290.dll} Unknown function at @isDbkLoggingOn$qv + $925EF
    [2BCD9D72]{bordbk290.dll} Unknown function at DllUnregisterServer + $17EF6
    [2BD3B8B0]{bordbk290.dll} Unknown function at @isDbkLoggingOn$qv + $619D8
    [2BD3C7E0]{bordbk290.dll} Unknown function at @isDbkLoggingOn$qv + $62908
    [2BCE4247]{bordbk290.dll} Unknown function at @isDbkLoggingOn$qv + $A36F
    [625AA5EA]{CnWizards_D120A.DLL} Unknown function at __dbk_fcall_wrapper + $788AA
    [6CB305FD]{vcl290.bpl  } Vcl.Themes.TStyleHook.WndProc (Line 7733, "Vcl.Themes.pas" + 38) + $6
    [6CC598FC]{vcl290.bpl  } Vcl.Styles.TStyleEngine.HandleMessage (Line 3616, "Vcl.Styles.pas" + 20) + $C
    [6340DEA9]{mmx_bds23.dll} IDEEdtHooks.GetMessageHook (Line 66, "IDEEdtHooks.pas" + 7) + $12
    [6279DE04]{CnWizards_D120A.DLL} Unknown function at INITWIZARD0001 + $17CC00
    [6CB7392F]{vcl290.bpl  } Vcl.Forms.TApplication.ProcessMessage (Line 13284, "Vcl.Forms.pas" + 25) + $1
    [6CB73952]{vcl290.bpl  } Vcl.Forms.TApplication.ProcessMessages (Line 13304, "Vcl.Forms.pas" + 1) + $4
    [00487F2B]{bds.exe     } AppMain.TAppBuilder.ApplicationActivated + $7
    [6CBCD24F]{vcl290.bpl  } Vcl.AppEvnts.TCustomApplicationEvents.DoActivate (Line 210, "Vcl.AppEvnts.pas" + 1) + $16
    [6CBCD8ED]{vcl290.bpl  } Vcl.AppEvnts.TMultiCaster.DoActivate (Line 438, "Vcl.AppEvnts.pas" + 5) + $10
    [6CB72DD0]{vcl290.bpl  } Vcl.Forms.TApplication.WndProc (Line 12854, "Vcl.Forms.pas" + 140) + $C
    [6DD8477C]{rtl290.bpl  } System.Classes.StdWndProc (Line 19085, "System.Classes.pas" + 9) + $2
    [6CB73927]{vcl290.bpl  } Vcl.Forms.TApplication.ProcessMessage (Line 13282, "Vcl.Forms.pas" + 23) + $1
    [6CB7396A]{vcl290.bpl  } Vcl.Forms.TApplication.HandleMessage (Line 13312, "Vcl.Forms.pas" + 1) + $4
    [6CB73CA9]{vcl290.bpl  } Vcl.Forms.TApplication.Run (Line 13451, "Vcl.Forms.pas" + 27) + $3
    [004D8AD2]{bds.exe     } bds.bds + $DE


  8. 2 hours ago, Anders Melander said:

    Yes, that much was clear. So how do you accomplish that?

    If you'd ask me to attempt a thing like that, I'd first look for a suitable bidirectional RPC framework. Something having capabilities like COM.  COM was used by IDE's such as Visual Basic to host (in-process) VBX controls but it works across processes as well. The "helper processes" would only need to contain the designtime part of the components, at runtime they do nothing.  Such an approach would move the design time components out of the address space of the IDE.  Also, the "bitness" of the helper processes and the IDE would be independent from each other.


  9. 15 hours ago, RonaldK said:

    What benefits do you expect from this?
    Performance: I doubt that an ARM IDE will really be faster than the current x86 version?

     

    Please remember that all installed design time packages (BPL files) and their associated runtime packages must be compiled to the same architecture and bitness as the IDE itself.  BPLs are just DLLs and the IDE loads them directly, in-process. This means that moving away from 32-bit x86 would be a huge breaking change for the deployment of third party components.

     

    Frankly, I've never understood why the design time packages are hosted in the IDE process itself because bugs in components will affect the IDE. Many of us will know the phenomenon that the IDE may throw an AV exception in some BPL when you terminate Delphi.  

     


  10. 17 hours ago, dummzeuch said:

    After playing around with debug visualizers, I found that there is a very simple way to provide a visualiser for many record types: Just add a special method (I called it Dump) to those records that returns a string that you can then evaluate in the debugger and display in the debug windows.
    There are two caveats though:

     

    read on in the blog post...

    Can't you make a read-only property out of it instead of a plain method? That way you could make it show up in the watch list of the debugger.

     

     

     


  11. On 11/4/2022 at 7:08 PM, dummzeuch said:

    I actually used a goto for debugging today. The reason was that at the end of a function I check the result and for a particular result I want to repeat the code in the function in order to step through it:

    
    function whatever: SomeType;
    label
      RepeatMe;
    begin
    RepeatMe:
      // some code that generates the result
      if Result = ResultIWantToDebug then
        goto RepeatMe; // <== put a breakpoint here
      // some more code
     end;

     

     

    No need for this, the debugger has that functionality built-in:

     

    -Set a breakpoint on your corner case condition and wait until it fires

    -Right-click the line where you want to "go to"

    -Enter submenu "Debug"

    -Select menu item "Set next statement". The debugger will jump here when you single-step through your code.

     

     

     


  12. 1 hour ago, dormky said:

    Except that I'm putting the timer in a thread for a reason. If it's on the main thread, it runs the risk of getting delay by other work (namely, heavy drawing). If the timer is delayed, the event is delayed too.

    A thread using


     

    While not terminated do
    begin
        Dosomething;
        Sleep(xxx);
    end;

     

     

    is perfectly OK if the thread has to do something useful periodically and if xxx isn't a terribly small number.

     

    But if "Dosomething()" is merely polling for some state change to respond to, then it's rather inefficient to have this thread constantly wakeup, consume CPU time and sleep again. 

    The "Sleep(xxx)" also limits the response time and the number of state changes that can be handled per second.

     

    For such a scenario it's much more efficient if you can wake up the thread "from the outside" ASAP when it has to react. This is where events really shine. 

     

     


  13. 13 hours ago, Stefan Glienke said:

    You can trick/hack a bit and do what you want to achieve manually:

     

    This is basically what a regular ctor call on the class type does (see System._ClassCreate).

    
    function TSomeClass<T>.CreateNewInstance: ISomeInterface<T>;
    var
      obj: TSomeClass<T>;
    begin
      obj := TSomeClass<T>(ClassType.NewInstance);
      obj.Create(fSomeParameters);
      Result := obj;
    end;

     

     

    Thank you! That seems to work indeed !

     



  14. The reason I'm trying to achieve this is the following.

     

    I have made a generic tStreamableInterfaceList<T:iInterface> which is basically a managed Tlist<T> that implements iInterfacelist<T>. The elements are interfaces.

     

    This list object knows several special tricks such as saving its contents to XML and BSON.  It can also load those elements back from such data files, which necessitates a virtual ClassFactory() method to create the correct objects "on the fly" based on the class names found in the data. So far, this all works very nicely. 
     

    I wanted to enhance this list with a few LinQ-like methods such as:

     

    Function .Where(aCondition:tPredicate<T>):iInterfacelist<T>;
    Function .OrderBy(aCompare:tComparison<T>):iInterfacelist<T>;

     
     ... but in order to achieve that, the base class must be able to dynamically instantiate derived classes or else the resulting objects would have the base nonfunctional Classfactory() method.   The "ugly" solution would be to put an abstract virtual Clone() method in the base class, but I'd very much like to avoid that.


  15. That doesn't do the trick, because 

     

    function TMyClass<T>.CloneObject: TMyClass<T>;

    ... will just construct an instance of the base class,  I would have to re-implement that method in each derived class.

     

    With non-generic classes it is possible to achieve what I want (see below).  With generics, the "class of..." syntax does not work.

     

    type
      SomeClassType = class of tSomeClass;
    
      tSomeClass = class
      protected
        SomeNumber: Integer;
      public
        constructor Create(aSomeNumber: Integer); virtual;
        function Copy: tSomeClass;
        procedure Whoami;
      end;
    
      tSomeClass2=Class(tSomeClass)
      End;
    
      tSomeClass3=Class(tSomeClass2)
      End;
    
    
    function tSomeClass.Copy: tSomeClass;
    var ctype: SomeClassType;
    begin
      ctype := SomeClassType(self.ClassType);
      result := ctype.Create(SomeNumber+1);
    end;
    
    constructor tSomeClass.Create(aSomeNumber: Integer);
    begin
      inherited Create;
      SomeNumber := aSomeNumber;
    end;
    
    procedure tSomeClass.Whoami;
    begin
      OutputDebugString(pchar( format('Class name = %s, number is %d',[self.classname,somenumber]  )));
    end;
    
    
    procedure TForm1.Button2Click(Sender: TObject);
    begin
         tSomeclass3.Create(1).Copy.Copy.Copy.Whoami; //yes, this test leaks some memory.
    end;

     

     

     

     


  16. Hello all,

     

    suppose I have a generic class that's going to be  inherited.

     

    I want to give the base class a method that creates a new instance of the same object type (a class factory so to speak).  How can I do that? I need to somehow tell the compiler which constructor to call.  

     

     

     

    Type tSomeclass<T:iInterface>=CLass (tInterfacedObject, iSomeInterface<T>)
         //many methods here
         Procedure SomeMethod(intf: T);
         Function SomeFunction:T;
         Constructor Create(someparameters);
         Function CreateNewInstance:iSomeInterface<T>;
      end;
        
      ...
      
      Function tSomeclass<T>.CreateNewInstance:iSomeInterface<T>
      begin
          result:=Self.Classtype.Create(fSomeParameters); //Does not compile
      end;
      

     


  17. 2 hours ago, Sherlock said:

    Yeah, the formatter is no longer maintained, marked as deprecated and should be handled with caution.

    Aside from that, you may highlight a specific code section and format only that. Or you could go a different way and choose to use the GExperts code formatter. It knows source directives to prevent code formatting.

    Eh what, the internal IDE source code formatter is deprecated? Are they just abandoning it without any replacement? I find it rather essential. 

    • Like 3

  18. Hello all,

     

    I see that the Delphi 12 source code formatter (Ctrl-D) still mutilates generic class declarations, it inserts inappropriate line feeds between keywords and ruins indentation.

    Is there a way to set markers in the source code that tell the formatter to skip those areas from formatting? Or can you recommend a better code formatter that handles generics properly?

     

     

    Have a nice day!

    Arthur

      

     

     

     

     

     

     

    before_formatting.thumb.png.749f19a7b6ea9f5dddad8e601baf7fb5.pngafter_formatting.thumb.png.38eba439af27f81b35394ab766a05265.png

×