Please see below ...
Joe.
-----Original Message----- From: Frank Heckenbach [SMTP:frank@g-n-u.de] Sent: Tuesday, February 12, 2002 7:16 PM To: gpc@gnu.de Subject: Re: gcc-3+
Russell Whitaker wrote:
found this at http://gcc.gnu.org/projects/
Miscellaneous ideas:
Chill should use garbage collection.
Convert the Chill front end to use garbage collection instead of obstacks, so that it can work again with current GCC.
Suspect this should apply to gpc as well.
Indeed. That's one of the main issues when moving GPC to gcc-3. Other issues are a numer of changed interfaces, though this will probably mostly be "mechanical" work.
[Joe da Silva]
Well, the pragmatist in me says GCC-3 compatibility is more important than ideological misgivings about garbage collection. <G>
Any guess how much time and effort it would take to make the change?
According to a talk with Peter, it will be on the order of several days to a few weeks. If several people will work on it, it will be easier on everyone, of course. Are you interested in helping?
da Silva, Joe wrote:
IMHO, garbage collection is a kludge to fix memory leakage problems, which are a particularly common affliction with C, with it's particularly heavy reliance on pointers ... <BG>
Excuse me, but I think that's more of a prejudice here. What the compiler does internally relies heavily on tree structures. These require pointers in *any* language -- though some languages hide the fact. Pascal doesn't hide it, so the tree handling code and the resulting memory management problems would be exactly the same if GPC was written in Pascal.
Other typical usages of pointers in C, such as for strings, are relatively few in GCC/GPC, and are not the problem.
[Joe da Silva]
My comments were general and relate to the excessive reliance of C on pointers, whereas Pascal often avoids them with safer alternatives, such as "var" parameters, etc. Of course, pointers are important and necessary in many situations (but I still consider garbage collection is a kludge for bad housekeeping - note also the <BG> ;-)
My knowledge of the internal workings of GPC (compilers in general, actually) is next to zero, although I hope that one day this will improve. BTW, I do realize GPC is itself written in C, thereby taking advantage of the wide platform support of GCC (every cloud has a silver lining!;-). If this inter-dependence with GCC also means that garbage collection is a requirement, then "so be it" ...
The "obstacks" were something like a "half-hearted" garbage collector and cause(d) many problems, so it's good to get rid of them, anyway, even if gcc-3 would not require it.
Of course, there are other alternatives to GC, such as reference counting. But that's not without cost either: it requires traversing all structures to get rid of, which costs runtime and is even a little tricky in the presence of recursive structures. And it costs memory to store the reference counters while GC has no significant memory costs AFAIK (only runtime costs).
So, from the programmer's perspective, GC is probably the easiest to live with, and also at runtime, it might not be worse than the alternatives.
Or am I missing the perfect solution?
Frank
-- Frank Heckenbach, frank@g-n-u.de, http://fjf.gnu.de/, 7977168E GPC To-Do list, latest features, fixed bugs: http://agnes.dida.physik.uni-essen.de/~gnu-pascal/todo.html
da Silva, Joe wrote:
Excuse me, but I think that's more of a prejudice here. What the compiler does internally relies heavily on tree structures. These require pointers in *any* language -- though some languages hide the fact. Pascal doesn't hide it, so the tree handling code and the resulting memory management problems would be exactly the same if GPC was written in Pascal.
Other typical usages of pointers in C, such as for strings, are relatively few in GCC/GPC, and are not the problem.
[Joe da Silva]
My comments were general and relate to the excessive reliance of C on pointers, whereas Pascal often avoids them with safer alternatives, such as "var" parameters, etc.
As much as I share your dislike of C in general, let's try to keep to the point. The use of explicit pointers instead of `var' parameters does not affect the memory management situation (if used properly, but I think GCC does so). Memory management is used with malloc() in C or New in Pascal, and those routines yield explicit pointers, anyway.
Of course, pointers are important and necessary in many situations (but I still consider garbage collection is a kludge for bad housekeeping - note also the <BG> ;-)
My knowledge of the internal workings of GPC (compilers in general, actually) is next to zero, although I hope that one day this will improve.
Just imagine lots of tree structures, i.e., a certain kind of "objects" (say, records, or variant records or even Pascal objects if you like) that refer to other objects very often. Often, these references are cyclic (e.g., an object that describes an ordinal type, pointing to its minimum and maximum value, ordinal constants, pointing to the type they belong to which is again the first object; or imagine a Pascal type which contains recursive references, such as a record type containing as a field a pointer to this record type -- the description of this type contains indirect references to itself). (Well, according to normal terminology, it's not a tree then anymore, but a directed graph, but it's called tree nodes internally, so let's stick with the wrong term for now. ;-)
Obviously, you need pointers to represent such structures. Creating them is not a problem, but how to clean up properly? You can traverse all pointers in the object and clean up the objects they point to recursively. But that's wrong because those objects might be referred to from elsewhere. Also, with cyclic references, you'd run into an endless loop.
This is not to say it's impossible (I mentioned reference counting, and there are ways to avoid the endless loop), but is this much simpler than GC? In fact, if you consider the whole memory as a large tree(*), the resulting algorithm wouldn't look much differently from a GC algorithm ...
(*) It's not really so, but in fact most of it is consumed by tree nodes, and whether you consider cleaning up one of several trees or cutting a branch from the only tree doesn't make a big difference.
If this inter-dependence with GCC also means that garbage collection is a requirement, then "so be it" ...
Practically, it does. But if you know a better solution, even if only in theory, I'd be interested, just for curiosity.
Frank
Hi folks,
Frank Heckenbach wrote (in reply to da Silva, Joe):
Obviously, you need pointers to represent such structures. Creating them is not a problem, but how to clean up properly? You can traverse all pointers in the object and clean up the objects they point to recursively. But that's wrong because those objects might be referred to from elsewhere. Also, with cyclic references, you'd run into an endless loop.
This is not to say it's impossible (I mentioned reference counting, and there are ways to avoid the endless loop), but is this much simpler than GC? In fact, if you consider the whole memory as a large tree(*), the resulting algorithm wouldn't look much differently from a GC algorithm ...
If this inter-dependence with GCC also means that garbage collection is a requirement, then "so be it" ...
Practically, it does. But if you know a better solution, even if only in theory, I'd be interested, just for curiosity.
I'm currently working on a framework including an object type named "tReferenceManager". It is intended to exactly solve this problem, at least for synchronization of collections (lists, hashtables etc.): For every pointer, you can subscribe _one_ collection it is stored in, plus an arbitrary number of reference collections. When the pointer is removed from the store collection, it is automatically disposed, and all of the references are removed from the reference collections.
I hope to complete the respective unit of this framework within a few weeks. If anyone thinks he/she can use it, I'll announce it after this unit is ready. (Frank: The framework contains also the exception handling unit we already talked about.)
Personally, I _really_ dislike garbage collection since I had to deal with it in an optimization problem in Java. ;-/ IMO, providing a language with GC is just a way to encourage programmers to give up control. An alternative feature I'd like to see in a language would be to split pointers into two separate data types: "store" pointers and "reference" pointers. Allocating and deallocating may only be done on a store pointer; setting a pointer to an already existing address may only be done with a reference pointer. When disposing of a store pointer, itself and all of its references are automatically set to nil, so you can always check whether the pointer is pointing to valid data. (It's the same principle I'm using in my tReferenceManager, and up to now I didn't find any disadvantages in it. Except that you are forced to think before you code.;-)
Bye
Markus
On Tue, 12 Feb 2002, Markus Gerwinski wrote:
Hi folks,
Frank Heckenbach wrote (in reply to da Silva, Joe):
Obviously, you need pointers to represent such structures. Creating them is not a problem, but how to clean up properly? You can traverse all pointers in the object and clean up the objects they point to recursively. But that's wrong because those objects might be referred to from elsewhere. Also, with cyclic references, you'd run into an endless loop.
This is not to say it's impossible (I mentioned reference counting, and there are ways to avoid the endless loop), but is this much simpler than GC? In fact, if you consider the whole memory as a large tree(*), the resulting algorithm wouldn't look much differently from a GC algorithm ...
If this inter-dependence with GCC also means that garbage collection is a requirement, then "so be it" ...
Practically, it does. But if you know a better solution, even if only in theory, I'd be interested, just for curiosity.
I'm currently working on a framework including an object type named "tReferenceManager". It is intended to exactly solve this problem, at least for synchronization of collections (lists, hashtables etc.): For every pointer, you can subscribe _one_ collection it is stored in, plus an arbitrary number of reference collections. When the pointer is removed from the store collection, it is automatically disposed, and all of the references are removed from the reference collections.
I hope to complete the respective unit of this framework within a few weeks. If anyone thinks he/she can use it, I'll announce it after this unit is ready. (Frank: The framework contains also the exception handling unit we already talked about.)
Personally, I _really_ dislike garbage collection since I had to deal with it in an optimization problem in Java. ;-/ IMO, providing a language with GC is just a way to encourage programmers to give up control. An alternative feature I'd like to see in a language would be to split pointers into two separate data types: "store" pointers and "reference" pointers. Allocating and deallocating may only be done on a store pointer; setting a pointer to an already existing address may only be done with a reference pointer. When disposing of a store pointer, itself and all of its references are automatically set to nil, so you can always check whether the pointer is pointing to valid data. (It's the same principle I'm using in my tReferenceManager, and up to now I didn't find any disadvantages in it. Except that you are forced to think before you code.;-)
Perhaps I'm missing something. I thought the purpose of GC was to maintain a list of free memory. Thus GC doesn't care how you access a block, it cares only if it is free or not. A simple GC would recognize adjacent free blocks and combine them into a larger single block. A more complex scheme could move already allocated blocks to make a large free block when one is needed. This last might be implemented by adding a transparent level of indirection. Programmer thinks his pointer p is pointing to something when in reality it is pointing to an internal pointer pointing to the something. Thus when the block gets moved only one pointer needs to be changed.
Along with GC you have the problem of do you allocate first fit or best fit? Each has its pros and cons.
And just how is gcc doing GC and friends? (was lazy and didn't do much digging thru the code.)
Just some thoughts, Russ
Markus Gerwinski wrote:
Frank Heckenbach wrote (in reply to da Silva, Joe):
Obviously, you need pointers to represent such structures. Creating them is not a problem, but how to clean up properly? You can traverse all pointers in the object and clean up the objects they point to recursively. But that's wrong because those objects might be referred to from elsewhere. Also, with cyclic references, you'd run into an endless loop.
This is not to say it's impossible (I mentioned reference counting, and there are ways to avoid the endless loop), but is this much simpler than GC? In fact, if you consider the whole memory as a large tree(*), the resulting algorithm wouldn't look much differently from a GC algorithm ...
If this inter-dependence with GCC also means that garbage collection is a requirement, then "so be it" ...
Practically, it does. But if you know a better solution, even if only in theory, I'd be interested, just for curiosity.
I'm currently working on a framework including an object type named "tReferenceManager". It is intended to exactly solve this problem, at least for synchronization of collections (lists, hashtables etc.): For every pointer, you can subscribe _one_ collection it is stored in, plus an arbitrary number of reference collections. When the pointer is removed from the store collection, it is automatically disposed, and all of the references are removed from the reference collections.
I hope to complete the respective unit of this framework within a few weeks. If anyone thinks he/she can use it, I'll announce it after this unit is ready. (Frank: The framework contains also the exception handling unit we already talked about.)
Personally, I _really_ dislike garbage collection since I had to deal with it in an optimization problem in Java. ;-/ IMO, providing a language with GC is just a way to encourage programmers to give up control. An alternative feature I'd like to see in a language would be to split pointers into two separate data types: "store" pointers and "reference" pointers. Allocating and deallocating may only be done on a store pointer; setting a pointer to an already existing address may only be done with a reference pointer. When disposing of a store pointer, itself and all of its references are automatically set to nil, so you can always check whether the pointer is pointing to valid data. (It's the same principle I'm using in my tReferenceManager, and up to now I didn't find any disadvantages in it.
What about the following situation:
- Create an object Foo (store pointer) and an object Bar that points to Foo (i.e., contains the store pointer).
- Create an object Baz that also points to Foo (reference pointer).
- Destroy Bar. IIUIC, Foo will be disposed of and the reference pointer in Baz invalidated. That's nice so I get an error message rather than a crash when I try to access Baz.Foo, but I'd prefer to actually access it. ;-)
I guess I must be missing something, since that's only a very simple example of what happens all the time in GCC/GPC.
Besides, when you really want to evaluate things, you have to describe all the hidden costs first. AFAICS, for every object you need an implicit list containing all the reference pointers, so they can be invalidated later.
When copying a pointer, you have to add the destination to the list, and remove its previous value (if any) from its list. If not done properly, this can easily be O(n) which is unacceptable. So you need at least a doubly-linked list or something like this.
These are memory and runtime issues, and given that copying pointers is quite common in GPC, these effects must be analyzed precisely.
Except that you are forced to think before you code.;-)
As I said in another mail, polemics probably won't get us any further. (I, e.g., have some scepticism about over-use of OOP (though I use a limited amount of OOP in my own programs) and could go on about people talking about "collections" and "subscribing" which I have to translate into the terminology I'm familiar with to understand what's really going on, and who sometimes simply hide their inefficiencies behind a cloud of classes (I've seen such code in C++ and Java), but I wouldn't talk about this in such a discussion. ;-)
Not "giving up control" can also mean not delegating work. There are certainly situations where you want exact control about when and how some block of memory is disposed of, and I also don't view GC as a panacea.
But the question is whether it's appropriate for this particular program that GCC is. GCC is rather complex (I don't think anyone will disagree ;-), and having one more thing to worry about can be really annoying (speaking from experience). So *if* the GC works silently in the background (I haven't seen it in actual use yet), it would be a great relief for the programmer in the first place.
As I said, the memory overhead of GC seems to be rather small. I haven't analyzed the runtime overhead, but given that GC is done rather seldom (as compared to something that needs extra effort for each pointer copy, such as your suggestion IIUIC), I'm rather optimistic there, too. Some other conditions that GC is more or less hostile to (like real-time issues) don't apply either.
Frank
Frank Heckenbach wrote:
... snip ...
What about the following situation:
Create an object Foo (store pointer) and an object Bar that points to Foo (i.e., contains the store pointer).
Create an object Baz that also points to Foo (reference pointer).
Destroy Bar. IIUIC, Foo will be disposed of and the reference pointer in Baz invalidated. That's nice so I get an error message rather than a crash when I try to access Baz.Foo, but I'd prefer to actually access it. ;-)
I guess I must be missing something, since that's only a very simple example of what happens all the time in GCC/GPC.
Besides, when you really want to evaluate things, you have to describe all the hidden costs first. AFAICS, for every object you need an implicit list containing all the reference pointers, so they can be invalidated later.
When copying a pointer, you have to add the destination to the list, and remove its previous value (if any) from its list. If not done properly, this can easily be O(n) which is unacceptable. So you need at least a doubly-linked list or something like this.
These are memory and runtime issues, and given that copying pointers is quite common in GPC, these effects must be analyzed precisely.
In Pascal, but not in C, you can force all pointer copying to be done via a subroutine, because there is an essential difference between pointers and VAR parameters. That subroutine will increase the reference count in the actual heap storage area by 1. At the same time you have to make all destruction, including destruction via procedure return and loss of local variables, decrement that reference count. If dispose finds a count greater than 1 it decrements and flags the storage as invalid, so that use by an orphan pointer will bomb. This means that access has also got to be via a routine to check that invalid flag. When anything decrements the ref to 0, the storage is released.
This complicates pointer copying and passing, but leaves the actual derefencing untouched. Note that a WITH statement effectively creates a pointer copy.
Another technique ties in with run-time checks. Any pointer use is validated by a routine, which effectively walks the list of allocated storage. If not found, the use is invalid and the system crashes. This again adds considerable overhead. It was kept disabled in PascalP because of that.
Whatever you do, from checking to ignoring, Pascal should not have garbage collection. It does not have the wild pointer usage of C (and C++), and so can be controlled. GC brings in things like unexpected pauses which are fatal to any real-time or quasi-real-time use. If you are going to create the overhead for GC then you can better create the overhead for proper run-time checking.
On Wed, 13 Feb 2002, CBFalconer wrote:
... Pascal should not have garbage collection.
1. Does the program fragment memory? 2. If yes, what are the acceptable limits of that fragmentation? 3. Will the program terminate before those limits are reached?
One size doesn't fit all.
Russ
CBFalconer wrote:
Frank Heckenbach wrote:
These are memory and runtime issues, and given that copying pointers is quite common in GPC, these effects must be analyzed precisely.
In Pascal, but not in C, you can force all pointer copying to be done via a subroutine, because there is an essential difference between pointers and VAR parameters. That subroutine will increase the reference count in the actual heap storage area by 1. At the same time you have to make all destruction, including destruction via procedure return and loss of local variables, decrement that reference count. If dispose finds a count greater than 1 it decrements and flags the storage as invalid, so that use by an orphan pointer will bomb. This means that access has also got to be via a routine to check that invalid flag. When anything decrements the ref to 0, the storage is released.
This complicates pointer copying and passing, but leaves the actual derefencing untouched. Note that a WITH statement effectively creates a pointer copy.
Indeed, in Pascal one can make it transparent to the programmer, not so in C. But the memory and runtime overhead is still there. And for GCC/GPC (which is not exactly known as the fastest compiler on earth, anyway), any change that would cause significant slowdown will have to be thought about very carefully ...
Another technique ties in with run-time checks. Any pointer use is validated by a routine, which effectively walks the list of allocated storage. If not found, the use is invalid and the system crashes. This again adds considerable overhead. It was kept disabled in PascalP because of that.
For this purpose, I prefer something like libefence. It's not 100% perfect (i.e., can only detect overruns or underruns at once), but has already found me some pointer bugs in my code.
Whatever you do, from checking to ignoring, Pascal should not have garbage collection. It does not have the wild pointer usage of C (and C++), and so can be controlled. GC brings in things like unexpected pauses which are fatal to any real-time or quasi-real-time use. If you are going to create the overhead for GC then you can better create the overhead for proper run-time checking.
Again, as I noted in another mail, we're talking about the MM used by the compiler itself. We have no intentions to change the runtime MM for GPC compiled programs, and I am aware that GC is not the right thing for every kind of program (in particular real-time programs as you said).
Frank
Hi folks!
Frank Heckenbach wrote:
What about the following situation:
Create an object Foo (store pointer) and an object Bar that points to Foo (i.e., contains the store pointer).
Create an object Baz that also points to Foo (reference pointer).
Destroy Bar. IIUIC, Foo will be disposed of and the reference pointer in Baz invalidated. That's nice so I get an error message rather than a crash when I try to access Baz.Foo, but I'd prefer to actually access it. ;-)
The point is: If in the current state you have n pointers p1 ... pn pointing to the same data, and p1 is disposed, p2 ... pn still point to the disposed address, and you don't have any possibility to check whether the data is valid anymore. "store" and "reference" pointers would just yield this additional possibility to check. Currently, it's what I'm implementing by hand in all of my Pascal programs; delegating it to the compiler would help to avoid crashes and memory leaks.
I guess I must be missing something, since that's only a very simple example of what happens all the time in GCC/GPC.
It's a simple process happening all the time, yes, that's the point; that's why I think a standard solution could pay.
Besides, when you really want to evaluate things, you have to describe all the hidden costs first. AFAICS, for every object you need an implicit list containing all the reference pointers, so they can be invalidated later.
Depends on the way how to implement it...
These are memory and runtime issues, and given that copying pointers is quite common in GPC, these effects must be analyzed precisely.
Agreed.
Except that you are forced to think before you code.;-)
As I said in another mail, polemics probably won't get us any further.
Didn't intend to be polemic, Frank... My statement was just an example of self-observation. ;-)
Not "giving up control" can also mean not delegating work. There are certainly situations where you want exact control about when and how some block of memory is disposed of, and I also don't view GC as a panacea.
If we're not going to make GC obligatory and drop any other way of pointer deallocation (The @#*!-"don't let the programmer ever touch memory"- philosophy of Java)-:, I'm okay with it.
Markus
Markus Gerwinski wrote:
The point is: If in the current state you have n pointers p1 ... pn pointing to the same data, and p1 is disposed, p2 ... pn still point to the disposed address, and you don't have any possibility to check whether the data is valid anymore. "store" and "reference" pointers would just yield this additional possibility to check. Currently, it's what I'm implementing by hand in all of my Pascal programs; delegating it to the compiler would help to avoid crashes and memory leaks.
OK, this is more a debugging aid (i.e., the program will probably run slower, but you find bug faster). It doesn't solve the original problem (with "tree" structures), but is certainly useful for other purposes.
Though I'm not sure why the distinction between store and reference pointers is necessary. Why not make all pointers the same, and when one pointer to an object is disposed of, all the other ones are invalidated?
Besides, when you really want to evaluate things, you have to describe all the hidden costs first. AFAICS, for every object you need an implicit list containing all the reference pointers, so they can be invalidated later.
Depends on the way how to implement it...
Now I'm interested how else you'd implement it. An alternative I could think of are double-pointers, but these are slower to dereference and cause another kind of memory leak: Each second-level pointer allocated once can never be freed if you don't know if there's somewhere a first-level pointer pointing to it. I suppose that's not what you have it mind.
If we're not going to make GC obligatory and drop any other way of pointer deallocation (The @#*!-"don't let the programmer ever touch memory"- philosophy of Java)-:, I'm okay with it.
OK, apparently you also had the misunderstanding that this was about the MM for Pascal programs. As you've probably read in my other mails by now, Russ's original mail was about GCC/GPC's internal MM.
Frank
Hi folks,
Frank Heckenbach wrote:
Though I'm not sure why the distinction between store and reference pointers is necessary. Why not make all pointers the same, and when one pointer to an object is disposed of, all the other ones are invalidated?
This is usually not, what you do... is it? If the pointer p1 is the one used to allocate p1^, and it is also referenced by p2..pn, you normally take care to use p1 again to deallocate, don't you?
If so, you _are_ implicitly working with "store" and "reference" pointers. Making this an explicit naming within the programming language would IMO make the language more "speaking". It would enforce "clear responsibilities" within the program: Only the pointer that created a piece of memory may destroy it.
If the restrictions are like this: - store pointers may _only_ "new" or "dispose", but never be set to the value of another pointer. Trying to "new" an already set store pointer causes a runtime error. - reference pointers may _only_ be set to the value of another pointer, but never be "new"ed or "dispose"d. ... then memory leaks are made impossible (you just _can't_ allocate a pointer and then set somewhere else), and accidentally disposing of some data still needed by a bunch of other pointers becomes at least less likely (reference p42 just isn't able to dispose of p1).
Besides, when you really want to evaluate things, you have to describe all the hidden costs first. AFAICS, for every object you need an implicit list containing all the reference pointers, so they can be invalidated later.
Depends on the way how to implement it...
Now I'm interested how else you'd implement it. An alternative I could think of are double-pointers, but these are slower to dereference and cause another kind of memory leak: Each second-level pointer allocated once can never be freed if you don't know if there's somewhere a first-level pointer pointing to it. I suppose that's not what you have it mind.
To be honest, I don't quite get the idea nor the problem... What exactly do you mean by double-pointers?
Bye
Markus
Hi folks,
Frank Heckenbach wrote:
Though I'm not sure why the distinction between store and reference pointers is necessary. Why not make all pointers the same, and when one pointer to an object is disposed of, all the other ones are invalidated?
This is usually not, what you do... is it? If the pointer p1 is the one used to allocate p1^, and it is also referenced by p2..pn, you normally take care to use p1 again to deallocate, don't you?
If so, you _are_ implicitly working with "store" and "reference" pointers. Making this an explicit naming within the programming language would IMO make the language more "speaking". It would enforce "clear responsibilities" within the program: Only the pointer that created a piece of memory may destroy it.
If the restrictions are like this:
- store pointers may _only_ "new" or "dispose", but never be set to the value of another pointer. Trying to "new" an already set store pointer causes a runtime error.
- reference pointers may _only_ be set to the value of another pointer, but never be "new"ed or "dispose"d.
... then memory leaks are made impossible (you just _can't_ allocate a pointer and then set somewhere else), and accidentally disposing of some data still needed by a bunch of other pointers becomes at least less likely (reference p42 just isn't able to dispose of p1).
What I don't understand about your story is
(1) how do you check if you can dereference the reference pointer. (e.g. what if the store pointer has been killed) (2) If you do that by invalidating the reference pointers, you'd need to have to be able to find the refernece pointers for a given store pointer. The situation then already approximates equals a primitive GC system . This all depends on if the store and reference pointers are all invalidated all at once, and how you deallocate.
Markus Gerwinski wrote:
Frank Heckenbach wrote:
Though I'm not sure why the distinction between store and reference pointers is necessary. Why not make all pointers the same, and when one pointer to an object is disposed of, all the other ones are invalidated?
This is usually not, what you do... is it? If the pointer p1 is the one used to allocate p1^, and it is also referenced by p2..pn, you normally take care to use p1 again to deallocate, don't you?
I don't think so. It's not uncommon, e.g., for a function to allocate and return (copy) something in a pointer, or to pass (copy) a pointer to a procedure to clean it up and dispose it. Or to allocate a pointer in a local variable, do something with it, then put it in a global list from where it's later disposed, etc.
Or do you have any arguments why these are less common situations than what you describe?
Besides, when you really want to evaluate things, you have to describe all the hidden costs first. AFAICS, for every object you need an implicit list containing all the reference pointers, so they can be invalidated later.
Depends on the way how to implement it...
Now I'm interested how else you'd implement it. An alternative I could think of are double-pointers, but these are slower to dereference and cause another kind of memory leak: Each second-level pointer allocated once can never be freed if you don't know if there's somewhere a first-level pointer pointing to it. I suppose that's not what you have it mind.
To be honest, I don't quite get the idea nor the problem... What exactly do you mean by double-pointers?
The problem is, as Marco said, how do you find all the existing reference pointers to invalidate when disposing of one store pointer.
With double pointers I mean: When you allocate some memory, you allocate another pointer to it, and let the real pointers point to that. So if you dispose of the memory, you set the other pointer to nil (or something), and you don't have to iterate over all reference pointers. The problem then would be that you can never dispose of the other pointer if you don't keep track of all existing reference pointers, so it's not a good solution.
Or how do you implement it?
Frank
Hi folks,
Frank Heckenbach wrote:
This is usually not, what you do... is it? If the pointer p1 is the one used to allocate p1^, and it is also referenced by p2..pn, you normally take care to use p1 again to deallocate, don't you?
I don't think so. It's not uncommon, e.g., for a function to allocate and return (copy) something in a pointer, or to pass (copy) a pointer to a procedure to clean it up and dispose it. Or to allocate a pointer in a local variable, do something with it, then put it in a global list from where it's later disposed, etc.
For these purposes, you still could pass a store pointer as a parameter. (Nobody prevents you from defining "Procedure foo ( bar: store )", since you never explicitly write "bar:= baz" or something.) Anyway, Maurice already kept me on the case of the linked list (see parallel mail), where you really can't avoid using a pointer as both, store and reference; so I don't insist any longer. Replacing the pointer by store and reference would without doubt be a bad idea. But I still think it could be a useful supplement.
With double pointers I mean: When you allocate some memory, you allocate another pointer to it, and let the real pointers point to that. So if you dispose of the memory, you set the other pointer to nil (or something), and you don't have to iterate over all reference pointers. The problem then would be that you can never dispose of the other pointer if you don't keep track of all existing reference pointers, so it's not a good solution.
I'm not quite sure, if I understand your "other" pointer right. Do you mean something like this?
var s: store; r: reference;
... new ( s ); r:= s; (* This one implicitly creates a pointer pr *) ... (* connected to s, pointing to r *) ... dispose ( s ); (* must find pr, too, set r to nil and destroy pr *)
Is that the structure you mean?
Bye
Markus