When you are in a TFrame and you do TThread.ForceQueue(nil, MyFrame.OneProc, 200) how can you check in the MyFrame.OneProc procedure that MyFrame was not destroyed in the mean time?
In other words, what mechanism can be used in such common scenario?
You can use guardian interface that will be fully functioning instance you can use to check whether guarded object is released in the meantime.
type
IGuardian = interface
function GetIsDismantled: Boolean;
procedure Dismantle;
property IsDismantled: Boolean read GetIsDismantled;
end;
TGuardian = class(TInterfacedObject, IGuardian)
private
FIsDismantled: Boolean;
function GetIsDismantled: Boolean;
public
procedure Dismantle;
property IsDismantled: Boolean read GetIsDismantled;
end;
procedure TGuardian.Dismantle;
begin
FIsDismantled := True;
end;
function TGuardian.GetIsDismantled: Boolean;
begin
Result := FIsDismantled;
end;
And then you need to add guardian field in your frame
type
TMyFrame = class(TFrame)
private
FGuardian: IGuardian;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
property Guardian: IGuardian read FGuardian;
end;
constructor TMyFrame.Create(AOwner: TComponent);
begin
inherited;
FGuardian := TGuardian.Create;
end;
destructor TMyFrame.Destroy;
begin
// prevent AV when destroying partially
// constructed instance
if Assigned(FGuardian) then
FGuardian.Dismantle;
inherited;
end;
But you cannot directly queue frame's MyProc, you need to use anonymous methods and capture that guardian variable so its life will be extended beyond the lifetime of the frame.
Reference counting will keep the guardian object instance alive even after MyFrame is released and its memory will be automatically managed.
It is important to use locally declared Guardian interface variable and capture that variable instead of directly capturing MyFrame.Guardian field because that field address will no longer be valid after MyFrame is released.
procedure CallMyProc;
var
Guardian: IGuardian;
begin
Guardian := MyFrame.Guardian;
TThread.ForceQueue(nil,
procedure
begin
if Guardian.IsDismantled then
Exit;
MyFrame.OneProc;
end, 200);
end;
Note: Even if you use TThread.Queue without a delay, it is possible that frame will be released before queued procedure runs. So you need to protect your frame is such scenarios, too.
You can't call a method on an object that has been destroyed. The preferred solution is to simply remove the method from the queue if it hasn't been called yet, before destroying the object. TThread has a RemoveQueuedEvents() method for exactly that purpose.
For example:
TThread.ForceQueue(nil, MyFrame.OneProc, 200);
...
TThread.RemoveQueuedEvents(MyFrame.OneProc);
MyFrame.Free;
Alternatively, use the frame's destructor instead:
TThread.ForceQueue(nil, MyFrame.OneProc, 200);
...
destructor TMyFrame.Destroy;
begin
TThread.RemoveQueuedEvents(OneProc);
inherited;
end;
Related
I have an app which uses class, a base class called TBaseDB, and there will be many descendants of TBaseDB, all DIRECT siblings, only one of which has been started now, TOraDB, but will later add TSQLDB and others.
My app uses one instance of the class, and it is a global instance, i.e. a global variable called PROJ. There is an issue in my understanding of constructors, destructors, and global variables which is causing EStackOverflow somewhere else in the app. If I comment out my PROJ.CREATE, the EStackOverflow goes away.
My constructors ONLY set variables, they do not dynamically create linked lists, arays, or other memory intensive objects.
Here are some code snippets.
// global var definition
// Specifically of BASE class, so I can call any child class without knowing which child class it is...
PROJ : TBaseDB;
My routine which causes my error...
procedure TForm1.ShowBug;
begin
// We have clicked on 'Create New Oracle Project
// Now create our PROJ object.
// This is defined globally, but MAY have been used before
// so 'zero' it out
FreeAndNil(PROJ);
// Note that the create is on the CHILD class, not the BASE class
// If I DON'T create the class, no error....
PROJ := TOraDB.Create;
// ... other code
end;
Here are my class definitions.
Type
TBaseDB = class
published
public
DAC_ID: Integer;
DAC_ShortName : String;
DAC_LongName: String;
Constructor Create;
Destructor Destroy; override;
... other stuff
end;
implementation
// TBaseDB /////////////////////////////////////////////////////////////////
constructor TBaseDB.Create;
begin
inherited;
end;
destructor TBaseDB.Destroy;
begin
// If I comment out next two lines, my issue goes away
// but shouldn't I have them....?
Self.Free;
Self := nil;
// Always call the parent destructor after running your own code
inherited;
end;
Here is my definition for the TOraDB class
Type
TOraDB = Class(TBaseDB)
public
Constructor Create;
Destructor Destroy; override;
... other stuff
End;
implementation
// ------------------------------------------------------------------------------
constructor TOraDB.Create;
begin
inherited;
// Now set up the information about the source database. We know it is Oracle
// even though we DONT know if it is connected
DAC_ID := 4;
DAC_ShortName := 'Ora';
DAC_LongName := 'Oracle';
end;
// -----------------------------------------------------------------------------
destructor TOraDB.Destroy;
begin
// Always call the parent destructor after running your own code
inherited;
end;
I am not understanding something about 'resetting' a global class variable. Where should I be 'resetting it, so I can still use the GLOBAL variable PROJ?
Thanks,
GS
You must not call Self.Free in the destructor of a class.
Free calls Destroy and Destroy calls Free ...... until Stack Overflow
destructor TBaseDB.Destroy;
begin
// Don't do that at all in a destructor
// Self.Free;
// Self := nil;
// Always call the parent destructor after running your own code
inherited;
end;
TObject.Free is just a safe call of the destructor, because it will test if the Instance is not nil.
procedure TObject.Free;
begin
if Self <> nil then
Destroy;
end;
EDIT
Regarding the global variable PROJ there is a simple (but not very wise) solution
destructor TBaseDB.Destroy;
begin
if Self = PROJ then
PROJ := nil;
// Always call the parent destructor after running your own code
inherited;
end;
You should have a look at Singleton implementation instead.
Don't use:
Self.Free;
Self := nil;
in your destructor.
I am having issue with declaring a unique global variable for each connection of IdTCPServer. What i am trying to do here is.
TMyContext = class(TIdServerContext)
public
Tag: Integer;
Queue: TIdThreadSafeList;
FPacketBuffer: Pointer;
PacketBufferPtr: Integer;
constructor Create(AConnection: TIdTCPConnection; AYarn: TIdYarn; AList: TThreadList = nil); override;
destructor Destroy; override;
end;
and then accessing the variable using TMyContext(AContext).FPacketBuffer, but i get an access violation error when there is a connection active and a new connection tries to connect. here is what is in my idTcpConnect and idTcpDisconnect
procedure TMainFrm.MainSckConnect(AContext: TIdContext);
begin
TMyContext(AContext).Queue.Clear;
TMyContext(AContext).Tag := -1;
GetMem(TMyContext(AContext).FPacketBuffer,65536);
end;
procedure TMainFrm.MainSckDisconnect(AContext: TIdContext);
Var Client: TClientInfo;
begin
//If TMyContext(AContext).Queue.Count > 0 Then TMyContext(AContext).Queue.Clear;
TMyContext(AContext).Queue.Clear;
FreeMem(TMyContext(AContext).FPacketBuffer);
If AContext.Data <> nil Then Begin
Client := Pointer(AContext.Data);
Clients.Remove(Client);
Client.Free;
AContext.Data := nil;
End;
end;
The error occures when getmem is called in idtcpconnect, i think i am doing it all wrong, i am not sure how i can have a unique global variable for each context.
Make sure you are assigning your class type to the TIdTCPServer.ContextClass property before activating the server at runtime, eg:
procedure TMainFrm.FormCreate(Sender: TObject);
begin
MainSck.ContextClass := TMyContext;
end;
You can't change the class of a [already created] object instance to a different type. The object is of the class it was instantiated at creation time.
You can safely cast any object to it's own class or any class it inherits of, because the object IS of that class. In a hard cast (like you're doing), you're telling the compiler you know what you're doing, for example:
type
TMyButton: TButton
public
FMyField: array[1..50] of byte;
end;
var
Button: TButton;
begin
//next line is valid, a variable of type TButton can reference any object
//inheriting from TButton or a TButton instance directly
Button := TMyButton.Create(nil);
//next line contains a valid cast, because Button contains a reference to
//a instance of TMyButton
TMyButton(Button).FMyField[10] := 5;
//valid, a TButton variable referencing a TButton instance
Button := TButton.Create(nil);
//next line is invalid and may cause an AV or in the worst case
//you may corrupt memory by doing that
TMyButton(AButton).FMyField[20] := 5;
end;
The fact is, in your OnConnect event, you get an already created instance of TIdContext (or a descendant type).
If you want this object to belong to your class, you have to first ask the server to create objects of that class, via the ContextClass property. You have to do this before the Active property of the server is set to true.
procedure TMyForm.Init;
begin
MyServer.ContextClass := TMyContext;
MyServer.Active := True;
end;
And finally, if you have object references, you have to create the objects on the context constructor, or add a Late create mechanism if you don't want to waste memory and you don't use it too often:
TMyContext = class(TIdServerContext)
private
FQueue: TIdThreadSafeList;
public
constructor Create(AConnection: TIdTCPConnection; AYarn: TIdYarn; AList: TThreadList = nil); override;
destructor Destroy; override;
property Queue: TIdThreadSafeList read FQueue;
end;
constructor TMyContext.Create(AConnection: TIdTCPConnection; AYarn: TIdYarn; AList: TThreadList = nil);
begin
inherited;
FQueue := TIdThreadSafeList.Create(Parameters);
end;
destructor TMyContext.Destroy;
begin
FQueue.Free;
inherited;
end;
PHEWWWW! i was killing myself about what is wrong, i was thinking that the FPacetBuffer variable isnt unique to each connection but after alot of debugging and commenting out code sections i saw the problem and i was like WHATTT!!!!
In processing a login packet data i declared a PChar variable and copied data using StrLCopy to it and retrieved the size of the data and then assigned a null character myself to it (and that was the problem line).
Size := (Packet.BufferSize-SizeOf(TLoginPacket));
GetMem(UserName,Size);
StrLCopy(UserName, PChar(Cardinal(Packet)+SizeOf(TLoginPacket)),Size);
UserName[Size] := #0; <--- This Line here
The size variable was holding real size + 2 in it.
Thanks for all the help guys :)
When using Interfaces in Delphi and overriding reference counting, it is possible to bypass the_Release calls Delphi makes when an interface reaches a reference count of zero.
But - when mixing classes and interfaces (which is very useful) the _Release method is ALWAYS called no matter what. The problem is that in the sample code below, the local object is nill-ed, but _Release is still called - except on invalid memory. Depending on memory operations in the application, an exception can result when _Release is called on the nilled localObject's old location or no exception if the memory was not re-used.
So, can the compiler generated call to _Release be "removed/blocked/avoided/killed/redirected/vmt hijacked/terminated/smacked/etc etc etc"? If this can be achieved you have proper pure interfaces in Delphi.
unit TestInterfaces;
interface
uses
Classes,
SysUtils;
type
ITestInterface = interface
['{92D4D6E4-A67F-4DB4-96A9-9E1C40825F9C}']
procedure Run;
end;
TTestClass = class(TInterfacedObject, ITestInterface)
protected
function _AddRef: Integer; stdcall;
function _Release: Integer; stdcall;
public
procedure Run;
end;
TRunTestClass = class(TObject)
protected
FlocalInterface : ITestInterface;
FlocalObject : TTestClass;
public
constructor Create;
destructor Destroy; override;
procedure Test;
end;
procedure RunTest;
procedure RunTestOnClass;
var
globalInterface : ITestInterface;
implementation
procedure RunTest;
var
localInterface : ITestInterface;
localObject : TTestClass;
begin
try
//create an object
localObject := TTestClass.Create;
//local scope
// causes _Release call when object is nilled
localInterface := localObject;
localInterface.Run;
//or global scope
// causes _Release call when exe shuts down - possibly on invalid memory location
globalInterface := localObject;
globalInterface.Run;
finally
//localInterface := nil; //--> forces _Release to be called
FreeAndNil( localObject );
end;
end;
procedure RunTestOnClass;
var
FRunTestClass : TRunTestClass;
begin
FRunTestClass := TRunTestClass.Create;
FRunTestClass.Test;
FRunTestClass.Free;
end;
{ TTheClass }
procedure TTestClass.Run;
begin
beep;
end;
function TTestClass._AddRef: Integer;
begin
result := -1;
end;
function TTestClass._Release: integer;
begin
result := -1;
end;
{ TRunTestClass }
constructor TRunTestClass.Create;
begin
FlocalObject := TTestClass.Create;
FlocalInterface := FlocalObject;
end;
destructor TRunTestClass.Destroy;
begin
//..
FlocalObject.Free;
//FlocalObject := nil;
inherited;
end;
procedure TRunTestClass.Test;
begin
FlocalInterface.Run;
end;
end.
There's no practical way to achieve what you are looking for. The compiler is going to emit the calls to _Release and in order to whack them you would need to find all the call sites. That's not practical.
I'm afraid the only viable approach when reference counted lifetime management is disabled is to ensure that you finalize (i.e. set to nil) all your interface references before calling Free.
When you use Interfaces you do not need to free your objects any more. interfaced objects will released automatically when there is no any references to same object.
In your sample you must delete _Release and _Addref functions in TTestClass they are defined in TInterfacedObject class.
In RunTest procedure you not need to Free the localObject only in finally section set globalInterface to nil. after end of procedure localInterface will destroy the local object automatically.
try
... use your code
...
finnaly
globalInnterface := nil;
end;
And about TTestRun.Destroy just left this destructor blank. you must not Free the FlocalObject.
TTestRun.Destroy;
begin
inherited;
end;
I have the following problem: I've got an entity class 'TEntity' and a mesh class 'TMesh' and TEntity needs to know when its element, (TMesh) is removed. Is there a possible working way I can call the TEntity method 'OnMeshRemove' from the TMesh destructor?
//uTEntity
interface
uses
uTMesh;
type
TEntity = class
Mesh : TMesh;
constructor Create(); overload;
procedure OnMeshRemove();
end;
implementation
constructor TEntity.Create();
begin
Mesh := TMesh.Create();
Mesh.EntityContainer := #self;
end;
procedure TEntity.OnMeshRemove();
begin
//Do stuff
end;
end.
//uTMesh
interface
type
TMesh = class
EntityContainer : Pointer;
destructor Remove();
end;
implementation
uses
uTEntity;
destructor TMesh.Remove();
var
PEntity : ^TEntity;
begin
PEntity := EntityContainer;
if Assigned( PEntity^ ) then
begin
PEntity^.OnMeshRemove();
end;
inherited Destroy();
end;
Example:
var
Ent : TEntity;
begin
Ent := TEntity.Create();
Ent.Mesh.Remove();
//I want Ent.OnMeshRemove to be called. In my example code, there is a pointer problem. I need to solve that. Thanks!
end;
PS: I don't want to have a TEntity procedure like TEntity.RemoveMesh();
All objects in Delphi are pointer type so no need to deference it. Here is a bit more simpler
type
TEntity = class
public
Mesh: TMesh;
constructor Create;
destructor Destroy; override;
end;
implementation
constructor TEntity.Create;
begin
inherited Create;
Mesh := TMesh.Create;
Mesh.EntityContainer := Self;
end;
procedure TEntity.Destroy;
begin
if Mesh <> nil then
begin
Mesh.EntityContainer := nil;
FreeAndNil(Mesh);
end;
inherited Destroy;
end;
//***************************************************
type
TMesh = class
EntityContainer: TObject;
destructor Destroy; override;
end;
implementation
uses
uTEntity;
destructor TMesh.Destroy;
begin
if (EntityContainer <> nil) and (TEntity(EntityContainer).Mesh = Self) then
TEntity(EntityContainer).Mesh := nil;
EntityContainer := nil;
inherited Destroy;
end;
Your TEntity instance should register itself with TMesh instance so that when TMesh instance is being freed, it will modify the TEntity instance about it.
If your classes are derived from TComponent class, then this mechanism is already implemented for you; each TComponent instance has a method called FreeNotification and a method called Notification. Any TComponent instance can register itself with the other component calling its FreeNotification method and passing itself as the parameter. Whenever a component is being destroyed, it will check the list of components registered for its free notification, and will invoke Notification method of each registered component. This way, the register component will be notified whenever the other component is about to be destroyed.
If one TComponent instance is the owner of the other (In your case, TEntity can be the owner of TMesh instance), then it will be notified automatically whenever TMesh instance is removed. All you need to do is to override its Notification method and do whatever you want to do inside that method.
If you do not want to derive your classes from TComponent class or for any reason do not want to use Delphi's implementation, you can implement the same behavior in your own classes. You need an internal list in your TMesh class which holds a list of classes which should be notified. You also need a method to register a class with your TMesh class, and eventually you need a method in your TEntity class which should be called by TMesh whenever it is being freed.
Here is a simple source code just for demonstrating the general idea. Take note that this sample code is not thread-safe, and might lack some other checks. I just wrote it fast to show you how to implement such an idea:
unit Unit1;
interface
uses
Classes, Generics.Collections;
type
TBaseClass = class
private
FNotificationList : TList<TBaseClass>;
protected
procedure Notify(AClass: TBaseClass); virtual;
public
procedure RegisterForNotification(AClass: TBaseClass);
procedure UnregisterNotification(AClass: TBaseClass);
constructor Create;
destructor Destroy; override;
end;
TMesh = class(TBaseClass)
end;
TEntity = class(TBaseClass)
private
FMesh : TMesh;
FOnMeshRemoved : TNotifyEvent;
procedure SetMesh(Value: TMesh);
protected
procedure Notify(AClass: TBaseClass); override;
procedure DoMeshRemoved; virtual;
public
constructor Create;
destructor Destroy; override;
property Mesh : TMesh read FMesh write SetMesh;
property OnMeshRemoved : TNotifyEvent read FOnMeshRemoved write FOnMeshRemoved;
end;
implementation
{ TBaseClass }
constructor TBaseClass.Create;
begin
inherited;
FNotificationList := TList<TBaseClass>.Create;
end;
destructor TBaseClass.Destroy;
var
AClass: TBaseClass;
begin
if Assigned(FNotificationList) then
begin
if (FNotificationList.Count > 0) then
for AClass in FNotificationList do
AClass.Notify(Self);
FNotificationList.Free;
FNotificationList := nil;
end;
inherited;
end;
procedure TBaseClass.Notify(AClass: TBaseClass);
begin
end;
procedure TBaseClass.RegisterForNotification(AClass: TBaseClass);
begin
if not Assigned(AClass) then
Exit;
if FNotificationList.IndexOf(AClass) < 0 then
FNotificationList.Add(AClass);
end;
procedure TBaseClass.UnregisterNotification(AClass: TBaseClass);
begin
if not Assigned(AClass) then
Exit;
if FNotificationList.IndexOf(AClass) >= 0 then
FNotificationList.Remove(AClass);
end;
{ TEntity }
constructor TEntity.Create;
begin
inherited;
end;
destructor TEntity.Destroy;
begin
if Assigned(FMesh) then
FMesh.UnregisterNotification(Self);
inherited;
end;
procedure TEntity.DoMeshRemoved;
begin
if Assigned(FOnMeshRemoved) then
FOnMeshRemoved(Self);
end;
procedure TEntity.Notify(AClass: TBaseClass);
begin
inherited;
FMesh := nil;
DoMeshRemoved;
end;
procedure TEntity.SetMesh(Value: TMesh);
begin
if Assigned(FMesh) then
begin
FMesh.UnregisterNotification(Self);
FMesh := nil;
end;
if Assigned(Value) then
begin
FMesh := Value;
FMesh.RegisterForNotification(Self);
end;
end;
end.
In this code, both TEntity and TMesh are derived from TBaseClass which provides notification mechanism. TEntity does not create any instance of TMesh initially, but you can assign a created TMesh instance to its Mesh property. Doing so will make TEntity to assign that value to its FMesh field, and call its RegisterForNotification class so that it can be notified if the mesh is being destroyed.
When the mesh is being destroyed, it iterates over all the objects registered themselves with it, and invokes their Notify method. Here it would be Notify method of TEntity class. Inside Notify method of TEntity, it first makes its FMesh field nil, because that object is being destroyed and there is no need to keep a reference of a dead object. It then calls DoMeshRemove method which is an event-invoker for OnMeshRemove event.
Edit: back behind a PC.
The classic way of maintaining lists of predetermined objects of a certain class is using TCollection/TCollectionItem.
TCollection/TCollectionItem are heavily used in Delphi (see this list).
They are lighter weight than TComponent (that automatically maintains Owner/Components/ComponentCount and has FreeNotification), as TCollectionItem and TCollection both descend from the TPersistent branch in stead of TComponent branch.
TCollection has a nice virtual Notify method:
procedure TCollection.Notify(Item: TCollectionItem; Action: TCollectionNotification);
begin
case Action of
cnAdded: Added(Item);
cnDeleting: Deleting(Item);
end;
end;
From Delphi 2009, you have generics, so then it can pay off big time to use TList (in your cast TList<TEntity>, as it contains this very nice Notify method and OnNotify event:
procedure TList<T>.Notify(const Item: T; Action: TCollectionNotification);
begin
if Assigned(FOnNotify) then
FOnNotify(Self, Item, Action);
end;
These two solutions work well if your TMesh is indeed a collection/list of TEntity.
If it TMesh is a non-list graph of TEntity, then it is better to descend both from TComponent like vcldeveloperlink text explained in his answer.
Andy Bulka has a nice blog post on the various ways of using lists and collections, including a well balanced view of TCollection/TCollectionItem usage.
--jeroen
Old answer (great iPad auto-complete bugs fixed):
Sorry for the short answer, as I am on the road only carrying a mobile device.
It looks like your mesh is a container for entities.
If so, then you should look into TCollection and TCollectionItem.
Derive your mesh from the former and your entity from the latter.
The delphi vcl source code contain many examples of these: fields/field or actions/action are good starters.
Here is my code example:
type
TMyBaseClass = class
public
procedure SomeProc; virtual;
end;
TMyChildClass = class(TMyBaseClass)
public
procedure SomeProc; override;
end;
var
SomeDelegate: procedure of object;
procedure TMyBaseClass.SomeProc;
begin
ShowMessage('Base proc');
end;
procedure TMyChildClass.SomeProc;
begin
ShowMessage('Child proc');
// here i want to get a pointer to TMyBaseClass.SomeProc (NOT IN THIS CLASS!):
SomeDelegate := SomeProc;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
with TMyChildClass.Create do
try
// there will be "Child proc" message:
SomeProc;
finally
Free;
end;
// there i want to get "Base proc" message, but i get "Child proc" again
// (but it is destroyed anyway, how coud it be?):
SomeDelegate;
end;
The one way I know is:
procedure TMyChildClass.BaseSomeProc;
begin
inherited SomeProc;
end;
procedure TMyChildClass.SomeProc;
begin
ShowMessage('Child proc');
SomeDelegate := BaseSomeProc;
end;
The 2nd is to change SomeProc declaration from override to reintroduce:
TMyChildClass = class(TMyBaseClass)
public
procedure SomeProc; reintroduce;
end;
and then cast self as TMyBaseClass (do not use as cast):
SomeDelegate := TMyBaseClass(self).SomeProc;
Also note that your code will give Access Violation because you call SomeDelegate on already freed object.
Adding a type declaration and some typecasting works but comes with some notes of warning.
As you've mentioned it yourself, the call to somedelegate after the instance has been freed doesn't AV. This is because your SomeProc method doesn't use any instance variables, all it does is calling ShowMessage.
Should you add any instance variables to the call, you even might still get away with it if the memory has not been reassigned. It would be an AV waiting to happen.
Bottom line:
don't call methods off destroyed objects.
setting a global variable from within an instance of a class that survives the lifetime of the class is not considered good design.
in a ideal design, there should be no need for a child class to revert a call anyhow to the ancestor's method, other than by calling inherited.
Code changes
...
type
TSomeDelegate = procedure of object;
var
SomeDelegate: TSomeDelegate;
...
procedure TMyChildClass.SomeProc;
var
method: TMethod;
begin
ShowMessage('Child proc');
// here i want to get a pointer to TMyBaseClass.SomeProc (NOT IN THIS CLASS!):
method.Code := #TMyBaseClass.SomeProc;
method.Data := Self;
SomeDelegate := TSomeDelegate(method);
end;