 |
BorlandTalk.com Borland discussion newsgroups
|
| View previous topic :: View next topic |
| Author |
Message |
Akede Guest
|
Posted: Sun Nov 16, 2003 11:24 am Post subject: Delphi Classes/Pointers] Nil references causing problems. |
|
|
I've recently run into quite an annoying problem with my server. Any
code I post in here is not actual, merely just an example to simplify
everything.
Best way to give the example, is code:
type
GPlayer = class;
GPlayer = class
ref: pointer; //
refX: GPlayer;
end;
Ok, at this point, you can see the two variables. Let's say that I
have 2 GPlayer's created at this point; We'll say Sean and Mike, both
are GPlayer types.
Mike.ref := @sean;
Mike.refX := sean;
Sean.ref := @mike;
Sean.refX := mike;
As you can see here, Sean and Mike know about each other now.
Let's say Sean decides to quit..
FreeAndNil(Sean);
Now, my problem here, is Mike doesn't know that Sean is gone. Is there
a way I can find out Sean is no longer there? Before you start flaming
about how I could do Sean.refX.ref := 0; Sean.refX.refX := nil; let me
remind you these are examples, and the server is on a much bigger
scale than this.
For example, there could be 400 GPlayer classes created; all of which
could point to the same GPlayer, or none at all. An example of this
would be creating 10 more GPlayers, all of their references point to
Mike. Now, if Mike quits, there's no way to reset their references
because Mike only has one reference. I know that's rather confusing,
and I'm sure the suggestions of "Create a linked list or dynamic array
to hold them" will more than likely be made--these are methods I'm
trying to avoid. Looping 400 GPlayer classes to check for a command
that is used often is out of the question as well.
I've tried the following methods to work around this:
if (not (Assigned(ch.reply)))
if (GPlayer(ch.reply)=nil)
if (SizeOf(ch.reply)<>SizeOf(ch))
At this point, I gave GPlayer a function "DEAD_FUNC"; I have tried it
with a TRUE and FALSE result.
if (GPlayer(ch.reply).DEAD_FUNC)
ch.reply was originally a type of GPlayer; I changed it to a pointer
and retried these methods.
Most of which cause errors and crash the server, despite their nesting
within a try..except statement. The second and fourth methods do not
crash the server, however, they don't work, and follow through in the
code. The try..except statements do not catch them.
Any suggestions as to how I could go about finding out if a reference
is no longer valid? Well, other than looping a list of 400+ classes
and checking/removing references for a command that is used as often
as 200 times in any given minute.
Thanks in advance,
Mike [Akede]
|
|
| Back to top |
|
 |
J French Guest
|
Posted: Sun Nov 16, 2003 12:15 pm Post subject: Re: Delphi Classes/Pointers] Nil references causing problems |
|
|
On 16 Nov 2003 03:24:24 -0800, [email]akede2001 (AT) yahoo (DOT) com[/email] (Akede) wrote:
| Quote: | I've recently run into quite an annoying problem with my server. Any
code I post in here is not actual, merely just an example to simplify
everything.
Best way to give the example, is code:
type
GPlayer = class;
GPlayer = class
ref: pointer; //
refX: GPlayer;
end;
Ok, at this point, you can see the two variables. Let's say that I
have 2 GPlayer's created at this point; We'll say Sean and Mike, both
are GPlayer types.
Mike.ref := @sean;
Mike.refX := sean;
Sean.ref := @mike;
Sean.refX := mike;
As you can see here, Sean and Mike know about each other now.
Let's say Sean decides to quit..
FreeAndNil(Sean);
Now, my problem here, is Mike doesn't know that Sean is gone. Is there
a way I can find out Sean is no longer there? Before you start flaming
about how I could do Sean.refX.ref := 0; Sean.refX.refX := nil; let me
remind you these are examples, and the server is on a much bigger
scale than this.
For example, there could be 400 GPlayer classes created; all of which
could point to the same GPlayer, or none at all. An example of this
would be creating 10 more GPlayers, all of their references point to
Mike. Now, if Mike quits, there's no way to reset their references
because Mike only has one reference. I know that's rather confusing,
and I'm sure the suggestions of "Create a linked list or dynamic array
to hold them" will more than likely be made--these are methods I'm
trying to avoid. Looping 400 GPlayer classes to check for a command
that is used often is out of the question as well.
I've tried the following methods to work around this:
if (not (Assigned(ch.reply)))
if (GPlayer(ch.reply)=nil)
if (SizeOf(ch.reply)<>SizeOf(ch))
At this point, I gave GPlayer a function "DEAD_FUNC"; I have tried it
with a TRUE and FALSE result.
if (GPlayer(ch.reply).DEAD_FUNC)
ch.reply was originally a type of GPlayer; I changed it to a pointer
and retried these methods.
Most of which cause errors and crash the server, despite their nesting
within a try..except statement. The second and fourth methods do not
crash the server, however, they don't work, and follow through in the
code. The try..except statements do not catch them.
Any suggestions as to how I could go about finding out if a reference
is no longer valid? Well, other than looping a list of 400+ classes
and checking/removing references for a command that is used as often
as 200 times in any given minute.
|
I could be barking up the wrong tree, however ...
Rather than pointing directly at the other Object, could you not point
at an element in an Array that then points to the Object
That way when you Free an Object, it could clear the pointer in the
Array
Eg: when Mike is Created, he is allocated a slot in an array, and that
points to him
When anyone 'latches on' to Mike, instead of holding a direct
reference to Mike, they get his Slot number as a reference
When Mike dies, he simply clears the reference in his slot number
The 'Slots' could also hold a reference count, which would be useful
for re-allocating the old slot to a new player.
Actually I would be inclined to have all the players as elements of a
Dynamic Array of TPlayer or a Record that contains a TPlayer and a
Reference Count
It is really only an Array of 4 byte pointers to the real TPlayer
Objects - with another 4 bytes for the Ref Count
PlayerRec[ 49 ].Player
PlayerRec[ 49 ].RefCount
FreeAndNil( PlayerRec[ 49 ].Player )
It would save using things like @Mike
|
|
| Back to top |
|
 |
David Reeve Guest
|
Posted: Sun Nov 16, 2003 1:13 pm Post subject: Re: Delphi Classes/Pointers] Nil references causing problems |
|
|
"Akede" <akede2001 (AT) yahoo (DOT) com> wrote
[snip]
| Quote: | For example, there could be 400 GPlayer classes created; all of which
could point to the same GPlayer, or none at all. An example of this
would be creating 10 more GPlayers, all of their references point to
Mike. Now, if Mike quits, there's no way to reset their references
because Mike only has one reference. I know that's rather confusing,
|
Isn't this the sort of thing the notification mechanism in TComponent is
made to handle? When a new GPlayer is created it calls
NewPlayer.FreeNotification(Mike) which adds Mike to its FFreeNotifies list,
and Mike likewise adds NewPlayer to its FFreeNotifies list. When you free
Mike, before it is destroyed, Notification will be called for every GPlayer
in its FFreeNotifies list, giving each of these the chance to nil its
references to Mike.
The notification mechanism is poorly described in help, but it is fairly
straightforward and can be readily grasped from the source in classes.pas.
Dave
|
|
| Back to top |
|
 |
J French Guest
|
Posted: Sun Nov 16, 2003 2:12 pm Post subject: Re: Delphi Classes/Pointers] Nil references causing problems |
|
|
On Sun, 16 Nov 2003 13:13:12 GMT, "David Reeve"
<dree4456 (AT) big-pond (DOT) net.au> wrote:
<snip>
| Quote: | Isn't this the sort of thing the notification mechanism in TComponent is
made to handle? When a new GPlayer is created it calls
NewPlayer.FreeNotification(Mike) which adds Mike to its FFreeNotifies list,
and Mike likewise adds NewPlayer to its FFreeNotifies list. When you free
Mike, before it is destroyed, Notification will be called for every GPlayer
in its FFreeNotifies list, giving each of these the chance to nil its
references to Mike.
The notification mechanism is poorly described in help, but it is fairly
straightforward and can be readily grasped from the source in classes.pas.
snip |
Ouch !
That means that he'll need to base his Class on TComponent
- which is probably pretty heavy for something that is little more
than a jumped up Record ... well TObject
The more I think about it, the more certain I am that he needs a
simple way of maintaining a 'pool of entities', and it should not
matter much whether an 'entity' is alive or dead
It will make housekeeping and debugging much easier if the system is
'supervised'.
|
|
| Back to top |
|
 |
Maarten Wiltink Guest
|
Posted: Sun Nov 16, 2003 7:37 pm Post subject: Re: Delphi Classes/Pointers] Nil references causing problems |
|
|
"Akede" <akede2001 (AT) yahoo (DOT) com> wrote
<objects form a directed graph>
| Quote: | [...] I'm sure the suggestions of "Create a linked list or dynamic
array to hold them" will more than likely be made--these are methods
I'm trying to avoid.
|
And yet, that's your best bet.
| Quote: | Looping 400 GPlayer classes to check for a command that is used often
is out of the question as well.
|
Correct. That's totally the wrong way to keep that administration.
Your objects have references to another object, and what it boils down
to is that they also need to know who points to them. You simply will
have to keep track of that. It's not something you can get out of.
Jerry proposes a central administration; David using the one already
present in TComponent; I would build my own inside the objects using a
TList or TObjectList and mutually updating properties. I'm aware that
you've said you're trying to avoid that.
I don't think you _can_ avoid it.
Groetjes,
Maarten Wiltink
|
|
| Back to top |
|
 |
Martin Harvey (Demon Acco Guest
|
Posted: Mon Nov 17, 2003 2:49 am Post subject: Re: Delphi Classes/Pointers] Nil references causing problems |
|
|
On 16 Nov 2003 03:24:24 -0800, [email]akede2001 (AT) yahoo (DOT) com[/email] (Akede) wrote:
| Quote: | Now, my problem here, is Mike doesn't know that Sean is gone. Is there
a way I can find out Sean is no longer there? Before you start flaming
about how I could do Sean.refX.ref := 0; Sean.refX.refX := nil; let me
remind you these are examples, and the server is on a much bigger
scale than this.
|
Yes. You need to get really smart, and uses classes, like this. This
is an "inverse reference counting" system - instead of maintaining
counts, classes inform other classes of their destruction.
The bit of software I'm working on at the moment uses something very
similar to this. This scheme is veyr general, and you can use it for
derived classes, lists, queues, all sorts of stuff, and make arbitrary
datastructures that can deal with any part of them being destroyed.
The actual scheme I'm using is more complex than this, and has various
safeguards against unintended recusion, and trying to access objects
further up the call stack from a "free" call, but this is the essence
of it (actually I do all my notification before any destructors get
called, but that just complicates the issue for the purposes of this
post).
:-)
interface
type
TNotifiable = class
private
FDying:boolean; //Just a speed optimization.
protected
procedure SendDestroyNotifications;virtual;
procedure HandleDestroyNotification(Sender:TNotifiable);virtual;
procedure SendSingleNotification(Destination: TNotifiable);
public
destructor Destroy;override;
end;
GPlayer = class(TNotifiable)
private
refX: GPlayer; //refX does not own us, so when it dies, we stay
protected
procedure SendDestroyNotifications;virtual;
procedure HandleDestroyNotification(Sender:TNotifiable);virtual;
end;
GFancyPlayer = class(GPlayer)
private
RefY: GPlayer; //refY does own us.
protected
procedure SendDestroyNotifications;virtual;
procedure HandleDestroyNotification(Sender:TNotifiable);virtual;
end;
implementation
procedure TNotifiable.SendDestroyNotifications;
begin
FDying := true;
end;
procedure TNotifiable.HandleDestroyNotification(Sender:TNotifiable);
begin
Assert(false, 'Notification unhandled in class' + ClassName);
end;
procedure TNotifiable.SendSingleNotification(Destination:
TNotifiable);
begin
Destination.HandleDestroyNotification(Self);
end;
procedure TNotifiable.HandleDestroyNotification(Sender:TNotifiable);
begin
Assert(false, 'Notification unhandled in class' + ClassName);
end;
procedure GPlayer.SendDestroyNotifications;
begin
inherited; //Call inherited first to set flag.
SendSingleNotification(refX);
end;
procedure GPlayer.HandleDestroyNotification(Sender:TNotifiable);
begin
//In some cases, may notify connected classes that I'm being
//destroyed, and as a result of notifying that class,
//it destroys itself and notifies me back.
//All OK, but may waste time searching if I'm a big queue or list
//Hence, the FDying check, which is a speed optimisation.
if not FDying then
begin
if Sender = refX then
refX := nil
else
inherited HandleDestroyNotification(Sender);
//I can't handle the notification - perhaps
// my base class can.
end;
end;
procedure GFancyPlayer.SendDestroyNotifications;
begin
inherited;
SendSingleNotification(refY);
end;
procedure GFancyPlayer.HandleDestroyNotification(Sender:TNotifiable);
begin
if not FDying then
begin
if Sender = refY then
Free //Be sure to call nothing else after this.
//refY will then get our destroy notification
//as a result of having sent its destroy
//notification to us.
else
inherited HandleDestroyNotification(Sender);
end
end;
MH.
|
|
| Back to top |
|
 |
Martin Harvey (Demon Acco Guest
|
Posted: Mon Nov 17, 2003 2:51 am Post subject: Re: Delphi Classes/Pointers] Nil references causing problems |
|
|
On Sun, 16 Nov 2003 14:12:24 +0000 (UTC), [email]erewhon (AT) nowhere (DOT) com[/email] (J
French) wrote:
| Quote: | That means that he'll need to base his Class on TComponent
- which is probably pretty heavy for something that is little more
than a jumped up Record ... well TObject
|
That's okay - for that, there's my "slimmed down" version, which works
in a very similar way.
The pros are, that there's a lot less overhead.
The cons are that that you have to write the functions to send and
handle notifications according to a set of rules with respect to when
and where you call the inherited functions.
MH.
|
|
| Back to top |
|
 |
Martin Harvey (Demon Acco Guest
|
Posted: Mon Nov 17, 2003 2:54 am Post subject: Re: Delphi Classes/Pointers] Nil references causing problems |
|
|
Oops. small addition to my previous post - there's an error in my
comments. The "FDying" flag also prevents infinite recursion, and is a
necessary part of the algorithm.
My method's not as neat or as general as the others, but if you want a
quick and only slightly dirty way of doing it, then it's quite useful.
MH.
|
|
| Back to top |
|
 |
David Reeve Guest
|
Posted: Mon Nov 17, 2003 6:05 am Post subject: Re: Delphi Classes/Pointers] Nil references causing problems |
|
|
"J French" <erewhon (AT) nowhere (DOT) com> wrote
[snip]
| Quote: |
That means that he'll need to base his Class on TComponent
- which is probably pretty heavy for something that is little more
than a jumped up Record ... well TObject
[snip] |
I have tended to avoid TComponent as a base class for simple objects for the
same reason. However, I very recently ran into a notification problem of
this sort and decided that this time I was going to have a good long look at
TComponent. Unless I am reading the source quite wrongly, I reckon that a
TComponent created with a 'nil' owner is only marginally larger than a
TObject, since creation with a nil owner skips all the overhead of
maintaining a component list, updating the owners component list and so
forth. In fact the only difference I can see between creating a TComponent
and a TObject is the line, FComponentStyle := [csInheritable], which hardly
rates as an overhead. Because you don't have a chain of ownership in place
(and don't want one), then the only burden is to remember to call
FreeNotification for every external reference you add to the object, and
provide an override for Notifiction that will nil this reference when
required.
TComponent handles fully cascaded bi-direction notification with other
TComponents. If you want to see how to tie a non-TComponent descendant into
the mechanism, have a look at TComponentList in the source code.
Dave
|
|
| Back to top |
|
 |
Nicolai Hansen Guest
|
Posted: Mon Nov 17, 2003 9:24 am Post subject: Re: Delphi Classes/Pointers] Nil references causing problems |
|
|
A completely different approach to this problem:
Instead of using GPlayer.Free when someone logs off (?), write a
GPlayer.Logoff method which doesn't free the GPlayer object, but set a
flag to make it an invalid target (repliers to this object can then
check on this flag), then make a garbage collector that runs through
all the players now and then, looking for "invalid" players -and
references-, freeing/nil'ing these.
|
|
| Back to top |
|
 |
Maarten Wiltink Guest
|
Posted: Mon Nov 17, 2003 1:08 pm Post subject: Re: Delphi Classes/Pointers] Nil references causing problems |
|
|
"Martin Harvey (Demon Account)" <martin (AT) pergolesi (DOT) demon.co.uk> wrote in
message news:cncgrvgukufmcd4p9d96tlh8r5rijlbvpm (AT) 4ax (DOT) com...
[...]
| Quote: | Yes. You need to get really smart, and uses classes, like this. This
is an "inverse reference counting" system - instead of maintaining
counts, classes inform other classes of their destruction.
|
Somebody, I forget who, mentioned "the Observer/Observable
[Design] Pattern" last time this subject came up.
Groetjes,
Maarten Wiltink
|
|
| Back to top |
|
 |
Bruce Roberts Guest
|
Posted: Mon Nov 17, 2003 9:37 pm Post subject: Re: Delphi Classes/Pointers] Nil references causing problems |
|
|
"Akede" <akede2001 (AT) yahoo (DOT) com> wrote
| Quote: | As you can see here, Sean and Mike know about each other now.
Let's say Sean decides to quit..
FreeAndNil(Sean);
Now, my problem here, is Mike doesn't know that Sean is gone. Is there
a way I can find out Sean is no longer there? Before you start flaming
about how I could do Sean.refX.ref := 0; Sean.refX.refX := nil; let me
remind you these are examples, and the server is on a much bigger
scale than this.
|
Another approach is to use messaging (not necessarily Windows messaging) and
let each instance resolve its response to a given situation. So each
instance would have a message handler. The constructor would broadcast a
creation message, the destructor would broadcast a destruction message. It
means keeping a list of all instances (if Windows messaging was not
useable), but there is no need to maintain a list of interested instances in
each object. Since each instance can manage its inclusion and removal from
the central list there is no need to use FreeAndNil or depend on some
reference being set to nil (see some fairly long threads on this particular
topic).
One might argue that sending a message to 400 instances when only a few of
them might be interested is wasteful. This may be the case, but often the
overhead is less than more complex management schemes - consider that each
uninterested instance need only perform a test or two to establish the fact.
In situations where a significant portion of the group is interested in each
instance, the scheme may actually perform better than a tComponent type
registration scheme.
|
|
| Back to top |
|
 |
David Reeve Guest
|
Posted: Tue Nov 18, 2003 6:01 am Post subject: Re: Delphi Classes/Pointers] Nil references causing problems |
|
|
"Bruce Roberts" <ber (AT) bounceitattcanada (DOT) xnet> wrote
| Quote: |
[snip]
Another approach is to use messaging (not necessarily Windows messaging)
and
let each instance resolve its response to a given situation. So each
instance would have a message handler. The constructor would broadcast a
creation message, the destructor would broadcast a destruction message. It
means keeping a list of all instances (if Windows messaging was not
useable), but there is no need to maintain a list of interested instances
in
each object. Since each instance can manage its inclusion and removal from
the central list there is no need to use FreeAndNil or depend on some
reference being set to nil (see some fairly long threads on this
particular
topic).
[snip] |
A messaging system like this is good when notification has to extend beyond
merely keeping references valid. Engineering CAD applications come to mind,
where you have a 'sea' of abstract objects, subsets of which develop mutual
interdependencies as the drawing progress. Just an extreme example of
objects all containing references each to the other, I guess.
Dave
|
|
| Back to top |
|
 |
Bruce Roberts Guest
|
Posted: Tue Nov 18, 2003 7:42 pm Post subject: Re: Delphi Classes/Pointers] Nil references causing problems |
|
|
"Bjørge Sæther" <bjorge (AT) hahaha_itte (DOT) no> wrote
| Quote: | Messaging is a good choice if the notifications is about all cooperation
needed. If one is to perform operations on the attached objects, more
direct
references would be handy. You need to maintain references no matter how,
else you can't know whether the message sender has any connection with
you.
Using lists on both sides makes it all "symmetrical". Symmetry is a nice
thing.
|
I don't think that a messaging model excludes the keeping of direct
references. In fact in the situation cited I would envisage this. Messaging
would simply be used to notify instances of instance creation and
destruction. It would, IOW, operate much like the notification method of
tComponent, without the registration overhead.
Symmetry is nice, but I think it often adds a needles layer of complexity.
ISTM that messaging makes a lot of sense if a significant percentage of the
population is interested in any given individual's actions. OTH,
registration may make more sense if any given individual is of interest to
only one or two others, i.e. an insignificant percentage.
What I really like about messaging is its simplicity and extensibility.
|
|
| Back to top |
|
 |
VBDis Guest
|
Posted: Wed Nov 19, 2003 12:03 am Post subject: Re: Delphi Classes/Pointers] Nil references causing problems |
|
|
Im Artikel <3fb7d1e1$0$58705$e4fe514c (AT) news (DOT) xs4all.nl>, "Maarten Wiltink"
<maarten (AT) kittensandcats (DOT) net> schreibt:
| Quote: | Jerry proposes a central administration; David using the one already
present in TComponent; I would build my own inside the objects using a
TList or TObjectList and mutually updating properties. I'm aware that
you've said you're trying to avoid that.
|
IMO decentralized lists are inappropriate, just consider the size and amount of
management overhead to maintain 400 lists, with up to 400 entries each, for 400
objects.
| Quote: | I don't think you _can_ avoid it.
|
Of course some management is inevitable, but IMO it should be centralized. A
common unique list of all objects allows to substitute "gone" players by a
PlayerGone object, which implements appropriate reactions for all possible
invocations of player object methods. [The use of NULL or possibly otherwise
invalid pointers is C style, not Pascal style.] Referencing objects then can
check whether a player is still present, or they can rely on appropriate
handling and return values, when referencing an gone player without checking
whether he's still present. Such a list also allows (and is required) for
broadcasting messages, like notifications about leaving players.
I'd implement a Game object, that manages the whole game and all players. Then
every other player can be referenced by Game.Player[i], in a safe manner
(provided the index is in range). Every Player object contains an MyIndex
field, which holds his own index in the global player list, so that another
player can store that index as a local persistent reference to any other
player. Furthermore the Game.Player array can contain the objects themselves,
instead of only references to the players - see TObject.NewInstance for
details. This will reduce the overhead for both memory management and object
(de)references at the same time.
DoDi
|
|
| Back to top |
|
 |
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
|