How to kill a type?

BlitzMax Forums/BlitzMax Beginners Area/How to kill a type?

fredborg(Posted 2004) [#1]
Hi,

I'm having a bit of a problem with getting properly rid of a type.

The thing I'm doing is I have a controller, which keeps track of everything going on. When killing this controller (using a custom function) it releases all the other types used in the game/program....Or...It should release everything, but cannot if you have made an external reference to something. So the big question is:

How do you kill a type properly, when there are still references to it somewhere?

Here's some code to demonstrate what I am on about:



FlameDuck(Posted 2004) [#2]
The easy solution? Make sure the 'controller' is the only place you have references. Then just go "con = null", this should eliminate everything stored in the controller type, provided you don't have cyclic references.

Incidently, what does your controller do that BlitzMAX doesn't do for you already?


Beaker(Posted 2004) [#3]
Having spoken to Fredborg on IRC, I think it is safe to say he will have external references outside of the controller type.


fredborg(Posted 2004) [#4]
The controller in the above example is obviously just to show the problem.

I am using controllers for different things, like gui, 3d engine etc. It makes it a lot easier to handle complex stuff when you have a 'super structure' that keeps track of 'sub parts' in my opinion.

External references are needed in order to make it easy and fast to operate.

A small example of real use:
'
' main menu
gui.init
local mybutton:button = button.Create("Begin",10,10,20,10)
Repeat
   cls
   gui.update
   flip
Until mybutton.clicked
'
' Now we want to get rid of everything associated to the gui
' and not have to trawl through every single button we made manually.
gui.kill
'
' Begin game...
game.init
' etc...
I think it's the question about how to make sure a type instance is properly released that's important, and not how you can avoid having multiple references to the same type instance.


fredborg(Posted 2004) [#5]
Please, someone tell me there is a way to 'brutally slay' a type even when references to it exists elsewhere.


Beaker(Posted 2004) [#6]
This really needs some official response. It is a fairly big issue.


fredborg(Posted 2004) [#7]
Ok, here's the deal:

Imagine a 3D engine.

The engine is controlled by an 'engine' type, which acts as a controller that keeps track of all 'model' types, rendering, etc.

The engine is initialized by a call to engine.init() and shut down by calling engine.kill(). Models are accessed and controlled using methods (and type functions).

The typical flow of using the engine would be:
' Lets start up the 3D engine
engine.init(640,480,0)

' We make a sphere
local mysphere:mesh = mesh.CreateSphere()

' We make a camera
local mycamera:camera = camera.Create()

' Now we do some stuff with these
mysphere.position(10,10,10)
mycamera.rotate(90,40,0)

' A small render loop
While Not Keyhit(KEY_ESCAPE)
  mysphere.turn(1,1,1)
  engine.render()
  Flip
Wend

' We now want to clean up everything, by shutting down the engine
engine.kill()

' Now everything should be cleaned up, and we could load a new level or whatever.
' BUT everything is not cleaned up. The sphere and the camera still exists, because the user made a reference to them
' outside of the engine itself.
'
' We really need a way of forcing the destruction of a type instance.
' 
'
It seems like a gross limitation if it isn't possible to force the destruction of a type instance, even if references to it still exist.

This is equivalent to having to remove all references to an entity in Blitz3D before freeing it, which is kind of ludicrous.

I realize you could work around it by making an 'index number' based system, but then you would lose methods and type functions which are truly what makes BlitzMax shine.

To repeat myself again: There MUST be a way to force the deletion of a type instance. I want InstantKill(TM)!

Edit: And another example that's a little simpler:
Type Test
	Field t:String = "This is something funky"
End Type

a:Test = New Test
b:Test = a
Release a

FlushMem

' This still works (only 'a' was nullified, but the actual instance wasn't released)
Print b.t
' This will make an error ('a' is really gone...maybe)
Print a.t



Bot Builder(Posted 2004) [#8]
While monkeying around with Blitz internals, I've noticed the refs property of the object class. BRL could just allow us access to that, make it a keyword so people dont complain about not being able to use it, and then put a FreeType() function in the blitz module that accepts an object as a parameter, and sets the objects refs to 0. An alternate version, maybe DeleteType() could also call flushmem.


Dreamora(Posted 2004) [#9]
Release + Flushmem is a very good way if you don't forget to overload
method delete() to free internal stuff.

haven't had a problem with free-ing this way so far ...


Beaker(Posted 2004) [#10]
Bully for you. :) Unfortunately this is a problem for any libraries written in Bmax. What do you mean "overload method delete()" ? Can you explain how this works?


Bot Builder(Posted 2004) [#11]
[SNIP] Link to solution that doesnt work. Appears to be a problem deeper than the modules.


Dreamora(Posted 2004) [#12]
With overloading delete() you can execute your own code when a type instance is released by the garbage collector.
overloading the method works the same way as with "new()"

this way you can even add "uncontrolled memory" to type instances.

One of the most important uses of delete() for me so far was the removal of a type instance from the types own global handling list.

Type test
 global all_items:TList
 
 method delete()
  ' this method will be called when this type instance is freed
  all_items.remove ( self )
 endmethod

endtest




If you have now

temp:test = new test

and

release temp
flushmem

the delete method will be calle and remove the "temp" from "all_items" which would not happen otherwise for example.


Warren(Posted 2004) [#13]
However, keep in mind that delete is not guaranteed to be called on your type instance so you can not count on it to do critical things. The docs say as much...


Dreamora(Posted 2004) [#14]
yepp ... although I really hope that until win release this WILL be guaranteed, as a garbage collector that can not guarantee the execution the class deinstance function is of quite less use.


Bot Builder(Posted 2004) [#15]
Dreamora, that works, but what if the user of say, your library has references to your type?


Warren(Posted 2004) [#16]
Dreamora

It's really the nature of a garbage collector. You have no control, or guarantee, of when your object will actually be destroyed. Your destructor might get called right away, it might never get called ... it's up to the collector.


Bot Builder(Posted 2004) [#17]
Unless you edit the module code which IS the garbage collector :) I think something funky is going on when you call a C method though. Purhaps the type is duplicated, and the reference to that is passed. I dont know. Whatever it is, setting the number of references to 0 and calling flushmem doesn't work.


marksibly(Posted 2004) [#18]
I think you may be panicing about nothing...

So what if there's still a reference to camera or whatever? As long as it's not in an 'active' list anywhere, it is effectively deleted, and the reference is really just hanging on to a chunk of memory with no connection to your 3D engine.

Eventually, the variable holding the reference will go out of scope or be overwritten, at which point this memory will be reclaimed by the garbage collector.

If you *really* need to know if a variable is pointing to an unused object, give it a state field. However, in the example you give above there doesn't seem to be a need for this.


Bot Builder(Posted 2004) [#19]
But what if the references do not go out of scope or get overwritten? And then after say, playing all the levels of your game, memory leaks have accumulated and it slows to a halt because the program has to use the page file.


marksibly(Posted 2004) [#20]

But what if the references do not go out of scope or get overwritten?



But they do. Think about it...

There are only a finite number of globals in any program, so these will all eventually be overwritten. Sure, you may have a 'dangling' global that keeps an object live longer than is strictly necessary. But this is not a 'leak' - the memory hasn't been lost forever, it will be reclaimed if/when the global is eventually overwritten. If you're paranoid about this then you can always set such globals to 'Null', but personally I wouldn't bother.

As for locals, these eventually go out of scope when the block they are declared in exits, so again, there is no possiblity of a 'leak' - each creation of a local is matched by a deletion.

What it all really boils down to is that any point in your program's execution, there can only be a finite number of active locals and globals. For a 'leak' to occur, a program would have to accumulate new variables somehow, which the language does not allow.


fredborg(Posted 2004) [#21]
So what if there's still a reference to camera or whatever? As long as it's not in an 'active' list anywhere, it is effectively deleted, and the reference is really just hanging on to a chunk of memory with no connection to your 3D engine.

Eventually, the variable holding the reference will go out of scope or be overwritten, at which point this memory will be reclaimed by the garbage collector.

Well, I have to admit that you do have a point. It just seems like science fiction to a simple man like myself. It somehow worries me that BlitzMax will take care of the lose ends eventually, but I can't force it to do it immediately. Well, live and learn :)

(Damn, this means I actually have to get the 3D engine working...)


Beaker(Posted 2004) [#22]
Question:
is this:
a:blah = New blah
a:blah = New blah
FlushMem

the same as this:
a:blah = New blah
a:blah = Null
FlushMem
a:blah = New blah

???

Also, when you say "an active list" do you mean a bmax TList thingy?


Bot Builder(Posted 2004) [#23]
beaker - in effect it should be the same. Bmax will notice you are changing a reference, and will decrement the refs property of the object you changed from, and increment the refs of the object you changed to (unless its null). So, when it hits flushmem in both cases it deletes the left over memory.

mark - True there are finite globals/locals, but that doesnt mean that you dont have more than there is memory for :)

  a-t
 /
c-i-r-c-l-e
 \
  o-d-e
   \
    r-d
     \
      e

    t k
   / /
  a-c-e
 /
r-i-n-k
 \
  u-l-e-r
   \
    t


So, basically, you have types with lists of child types, and a short of actual data. This will be a very fast dictionary but thats besides the point :). Lets say you wanted to free the dictionary, and you have an array of 26 pointers to initial letters. I delete the array. Ok, so now references are gone to teh first level. Flushmem takes them out. But what about the rest? Will i have to call flushmem as many times as there are charachters in the largest word?


marksibly(Posted 2004) [#24]
Hi,


Ok, so now references are gone to teh first level. Flushmem takes them out. But what about the rest?



When an object is reclaimed by the system, its fields go out of scope, which may cause other objects to be reclaimed etc. etc. Ditto with arrays and array elements. In other words, FlushMem is 'greedy', it will reclaim all it can. The only 'gotcha' is with cyclic data structures - see the 'Advanced topics' section in the language reference.