Jump to content

JonRobertson

Members
  • Content Count

    289
  • Joined

  • Last visited

  • Days Won

    7

Posts posted by JonRobertson


  1. Just now, havrlisan said:

    whether it is even noticeable on medium-sized projects (~500.000 lines of code)

    Yes, it is noticeable. My last four projects have been migrating applications from Delphi 7 to 11.3. There were dozens of units referenced that could be either moved to implementation or removed completely. I've been able to get rid of numerous circular dependencies. Although the compile time reduction is not huge, it is noticeable even on a 200K loc project.

     

    Sadly, it takes Azure DevOps longer to check out the repo than building the source. :classic_biggrin:

    • Like 1

  2. 1 hour ago, David Schwartz said:

    If you just have any random form that's got edits and grids and checkboxes and whatnot on it, and they are NOT DB-aware controls, then what do you do?

    The data shown and potentially edited on a form comes from somewhere. So I populate the controls from the source and update if/when the user makes changes. If this is in the app with the base form and UpdateUIState implementation I described, I am still able to override methods like UpdateUIState for that form, that code doesn't care about a dataset. Another option is to use LiveBinding, although I've avoided LiveBinding myself.

     

    1 hour ago, David Schwartz said:

    This wide disparity between forms that are used for interacting with DBs that have DB-aware controls on them vs. those that don't -- because you're not using  DB -- has always perplexed me with Delphi.

    Just with Delphi? Do you use other languages with frameworks that provide a cleaner solution?

     

    22 minutes ago, Tom Chamberlain said:

    We force the users to click an Edit button to change 99% of any data and then an Save or Cancel button.

    The projects that I currently maintain do this as well. Although I am not a fan of requiring extra mouse clicks. It is easy to detect once a user has changed data and require the user to click Save or Cancel before closing the form.


  3. 7 minutes ago, Willicious said:

    "Clear;" is called from Clone. But, it isn't in the call stack (not directly, anyway). So, (and this is possibly one of those noobish questions), without prior knowledge of where "Clear" is being called, how can the call stack be used to trace this by itself?

    Perhaps it can't without knowledge of the helper class TListHelper. Although you were able to trace back to the point of code where Clear was called and it was shown in your screenshot.

     

     

    image.png


  4. 23 minutes ago, Willicious said:

    It's still not 100% clear how we got to the "Free" call, though... I've highlighted the most recent thing that isn't in System.Generics

    TGadgetAnimations is a TObjectList that holds a list of TGadgetAnimation:

      TGadgetAnimations = class(TObjectList<TGadgetAnimation>)

    There are multiple instances of TGadetAnimations created here:

    constructor TGadgetMetaInfo.Create;
    var
      i: Integer;
    begin
      inherited;
      for i := 0 to ALIGNMENT_COUNT-1 do
      begin
        fVariableInfo[i].Animations := TGadgetAnimations.Create;

    The TObjectList.OwnsObjects property defaults to True and is not set to False for any instance of TGadgetAnimations. This means that TGadgetAnimations will call Free on the instances of TGadgetAnimation that it holds (which are added by the call to Add).

     

    The TObjectList.Clear method will remove all instances of TGadgetAnimation that were added to that instance of TGadgetAnimations. Since OwnsObjects is True, calling Clear will also call Free on each instance.

     

    When AddPrimary is called the first time for the instance, it assigns a reference to TGadgetAnimation to fPrimaryAnimation.

    procedure TGadgetAnimations.Clone(aSrc: TGadgetAnimations);
    var
      i: Integer;
      NewAnim: TGadgetAnimation;
    begin
      Clear;
    
      NewAnim := TGadgetAnimation.Create(aSrc.PrimaryAnimation.fMainObjectWidth, aSrc.PrimaryAnimation.fMainObjectHeight);
      NewAnim.Clone(aSrc.PrimaryAnimation);
      AddPrimary(NewAnim); <-- This will assign fPrimaryAnimation if it is not assigned.

    When Clone is called, it calls Clear, which frees the TGadgetAnimation instances that were previously created. But, it does not clear the fPrimaryAnimation reference, which is now pointing to unallocated memory.

     

    Use the solution posted by Anders of overriding the Notify method.

    • Thanks 1

  5. 7 hours ago, Kas Ob. said:

    as for the UnicodeString leak, it might be simply a confusion/confliction in madshi with MemoryManager

     

    16 hours ago, Anders Melander said:

    My guess is that it's the TGadgetAnimation.fName string which is leaked (based on the string content and the call to UpperCase) but that it a bit strange because strings are reference counted and it's usually pretty impossible to leak them.

    If TGadgetAnimation is not being freed, then it would have a reference to fName and the string would also be a leak.


  6. 7 hours ago, Kas Ob. said:

    i can't figure out where "GadgetAccessor: TGadgetMetaAccessor;" is freed

    GadgetAccessor is assigned by the result of GetInterface. GetInterface creates the TGadgetMetaAccessor and assigns the reference to the FInterfaces array:

    function TGadgetMetaInfo.GetInterface(Flip, Invert, Rotate: Boolean): TGadgetMetaAccessor;
    var
      i: Integer;
    begin
      i := GetImageIndex(Flip, Invert, Rotate);
      if fInterfaces[i] = nil then
        fInterfaces[i] := TGadgetMetaAccessor.Create(self, Flip, Invert, Rotate); <-- Created reference assigned to fInterfaces
      Result := fInterfaces[i];
    end;

    TGadgetMetaInfo.Destroy loops through fInterfaces and calls Free on each:

    destructor TGadgetMetaInfo.Destroy;
    var
      i: Integer;
    begin
      for i := 0 to ALIGNMENT_COUNT-1 do
      begin
        fVariableInfo[i].Animations.Free;
        fInterfaces[i].Free;
      end;
      inherited;
    end;

    Despite the misleading names of GetInterface and fInterfaces, these are not Delphi interfaces. TGadgetMetaAccessor is a class derived from TObject.


  7. 4 minutes ago, JonRobertson said:

    I do not see TGadgetAnimations in that code.

    Never mind, found it. TGadgetAnimations is a class of TObjectList. TObjectList has a property named OwnsObjects that is True by default. Unless OwnsObjects is set to False, TObjectList will call Free to release all objects that are added to it. See the documentation for TObjectList.OwnsObjects.

     

    The only place in the SupperLemmix code where I see OwnsObjects set to False is here:

      fTalismanButtons.OwnsObjects := false; // Because TFLevelSelect itself will take care of any that remain

     

     


  8. 48 minutes ago, Willicious said:

    fPrimaryAnimation doesn't get freed, and PrimaryAnimation (property referring to fPrimaryAnimation) doesn't get freed. NewInstance and NewAnim also don't get freed. I honestly don't know where to start.

    fPrimaryAnimation is likely only a reference to which animation is currently the primary animation.

     

    TGadgetAnimations adds NewAnim to a list of some kind (see the calls to Add(NewAnim) and Add(aPrimary). I suspect TGadgetAnimations is responsible for freeing all animations that are added.

     

    Is this still SupperLemmix? I downloaded SupperLemmix source just now from https://github.com/Willicious/SuperLemmixPlayer and I do not see TGadgetAnimations in that code.


  9. 12 minutes ago, Attila Kovacs said:

    Is your question technical?

    Yes. Several questions, about the Database Comparer VCL components.

     

    I downloaded the trial and samples of Database Comparer. With the prebuilt DBComparerDemoAdo, I attempted to compare two versions of one of our databases. The result was "Update script is empty" even though there are certainly differences in the schema of the databases.

     

    There are some messages in the log indicating an issue, such as "ambiguous column" and "field not found". However there is no additional information to give insight, such as the DB objects that triggered the errors.

     

    I hoped that "The trial version can operate inside Delphi / C++Builder IDE only, all other features are completely available without any restrictions" was accurate. There is no mention on the download page of other restrictions. The trial version appears to be limited to extracting only a few DB objects.


    The help file is unhelpful on most topics, for example:

    TCustomScriptExtract.ScriptFileNames
    property ScriptFileNames : TStrings;

    A vast majority of the help topics are like this, lacking any description aside from the Pascal definition. Without source, it is difficult to determine the capabilities without better documentation.

     

    One of the most important capabilities that I need to test is how well the components handle dependencies between database objects. What order are objects created? If there are stored procedures or functions that depend on other stored procedures or functions, are these created in the correct order so the creation scripts do not fail?

     

    How can I iterate through DBStructure.Metadata properties (such as Tables, Views, and Procedures) by name? I don't see a way to get a list of the names of the objects that were extracted, a count of the objects, or a way to iterate them by index. If I have a list of table names, how do I use .Tables['MyTable'] to get the CREATE script? I've tried .Body, .CurrentDefinition, .SourceDefinition, .Text, and .SQLExec (to get to .SQLExec.Statements, but .SQLExec is nil).

     

    I greatly appreciate any insight you or anyone else can provide. I need to make a recommendation on purchasing either Database Comparer VCL, Delphi HTML Component Library (primarily for the SQL Library components), or writing my own, which I started investigating today.


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

    I hope one day they will change their mind on this matter. IMHO even when migrating db related components this would be useful.

    Or provide the source (not open source, just source like the RTL/VCL source) to active subscribers so we can adapt it as we need. I added a comment to the RSP but I probably need to create a new QP request since mine was closed as resolved.


  11. 4 minutes ago, Anders Melander said:

    There's a more lightweight and faster alternative available in the RTL of recent Delphi versions

    TLightweightMREW

    5 minutes ago, Anders Melander said:

    because people didn't know what they were doing

    Our "issue" at the time was RTL code. Specially in a DCOM based MIDAS/DataSnap server that created a thread for each user connection. Once the DataSnap server had over 100 users connected, it would frequently lock up due to a deadlock with GlobalnameSpace.

     

    However, the contributing factor was we were doing "too much" in RemoteDataModuleCreate, which extended the amount of time that GlobalNameSpace was locked. We also had multiple TRemoteDataModules due to the size of our app.  While I don't disagree with your assertion of "people didn't know what they were doing", it wasn't because we were misusing TMREWS. We never used it directly in our code.

     

    My apologies for hijacking the thread. I'll stop now.


  12. 3 minutes ago, Anders Melander said:

    Search broken again?

    Nope. It returns results like

    On 1/27/2019 at 4:33 AM, dummzeuch said:

    It does use various instances of TMultiReadExclusiveWriteSynchronizer which, if I remember correctly, was broken in several RTL versions. So using that class might not have been the best decision.

    Quote

    it is terribly slow. So slow that it is actually almost never OK to use it. If a protected area of code is short, then it is almost always better to use a simple critical section for synchronization.

    Quote

    it appears there have been some major historical issues with TMultiReadExclusiveWriteSynchronizer but there’s no clear indication of whether this is still the case

    Numerous posts about TMREWS having issue and asking if those were fixed. A few posts suggesting an alternate solution.

     

    I trust members of Delphi-Praxis more than most other online resources. So I thought I'd ask. :classic_wink:


  13. 3 hours ago, Dalija Prasnikar said:

    If you share other data within that instance between threads then any such access should also be protected by a lock

    Does anyone use TMultiReadExclusiveWriteSynchronizer? The application team I was on 20 years ago had some problems with it in D6. But I see that the VCL still uses it for GlobalNameSpace (for Windows anyway).


  14. 4 hours ago, Willicious said:

    It's narrowed down to being a problem with TProjectile, which doesn't have a destructor. However, I implemented a destructor and this doesn't fix the memory leak

    Classes do not need a destructor to be destroyed/freed. A class needs a destructor if it allocated other objects or memory that it should free or there is other cleanup it needs to do before being destroyed.

     

    If an object is not being freed, adding a destructor would not fix the issue.

    4 hours ago, Willicious said:

    Does it open a dialog or spit out a text file? Or something else?

    Read the comments in FastMM4Options.inc. Specifically:

      {Set this option to log all errors to a text file in the same folder as the
       application. Memory errors (with the FullDebugMode option set) will be
       appended to the log file. Has no effect if "FullDebugMode" is not set.}
      {$define LogErrorsToFile}
    
      {Set this option to log all memory leaks to a text file in the same folder as
       the application. Memory leak reports (with the FullDebugMode option set)
       will be appended to the log file. Has no effect if "LogErrorsToFile" and
       "FullDebugMode" are not also set. Note that usually all leaks are always
       logged, even if they are "expected" leaks registered through
       AddExpectedMemoryLeaks. Expected leaks registered by pointer may be excluded
       through the HideExpectedLeaksRegisteredByPointer option.}
      {$define LogMemoryLeakDetailToFile}

    It has been a while since I did this. I found a post on SO for a reminder. Based on that post, copy FastMM4Options.inc to your project folder. Edit the .inc file and remove the period from {.$define FullDebugMode}

     

    Do a full build of the project and any leaks should be written to a text file with additional information about the object leaked and when it was allocated.

    • Like 1
×