24 December, 2020

Even empty applications have bugs

We were contacted by a person who claimed to have found a bug in EurekaLog. He justified this statement as follows: if you create a new DataSnap application and add EurekaLog to it, the application will crash with Access Violation on exit.

The investigation revealed the following:
  • The ServerContainerUnit1 data module contains a DSServer1 server component;
  • The same data module contains two auxiliary components: DSTCPServerTransport1 and DSServerClass1;
  • Both DSTCPServerTransport1 and DSServerClass1 specify the DSServer1 as a "Server";
  • When the application exits, the ServerContainerUnit1 data module will be destroyed;
  • Destroying a data module means destroying all components inside - including DSTCPServerTransport1 and DSServerClass1.

    Here's where it happens:
    • TDSServerClass.Destroy
    • TComponent.DestroyComponents
    • TDataModule.Destroy

    • TDSTCPServerTransport.Destroy
    • TComponent.DestroyComponents
    • TDataModule.Destroy
    However, neither TDSServerClass.Destroy nor TDSTCPServerTransport.Destroy notify the server that they are being destroyed. As a result, DSServer1 continues to store reference to the (already deleted) DSTCPServerTransport1 and DSServerClass1;
  • The ServerContainerUnit1 data module continues to clean up;
  • It is now time to remove DSServer1. Specifically, the DSServer1 tries to stop all registered transports:
    • TDSCustomServer.StopTransports
    • TDSCustomServer.Stop
    • TDSServer.Stop
    • TDSServer.Destroy
    • TObject.Free
    • TComponent.DestroyComponents
    • TDataModule.Destroy
    which will cause Access Violation, since the objects for these transports have already been deleted.

As you can see, it is a bug in DataSnap, not in EurekaLog. EurekaLog only revealed this bug. Indeed, when EurekaLog is not enabled, DSServer1 can "successfully" call the StopTransports method, since memory of already deleted objects will not be changed, therefore calls of methods of already deleted objects inside StopTransports will be "successful".

Note that this error has been sitting in DataSnap for ages and no one fixes it - precisely because there is no means to detect it in a naked application (without additional debugging tools). There are similar problems in the empty Fire Monkey (FMX) application, as well as in many standard Delphi components.

Specifically, this error can be worked around if you force DSServer1 to be deleted first:
procedure TServerContainer1.DataModuleDestroy (Sender: TObject);
begin
  FreeAndNil (DSServer1);
end;
Other errors of a similar nature should be investigated separately.

If you cannot fix the error, you can always turn off memory checks in EurekaLog, although this is not recommended.