Advertisement

Possible garbager problem

Started by October 25, 2010 11:53 AM
3 comments, last by WitchLord 14 years, 1 month ago
Either I don't quite understand what I'm doing, or here's a bug.

AS rev 684 (release 2.19.2)

class Big{  Big()  {    Log("Big instance created");  }  ~Big()  {    Log("Big instance being destroyed");  }  void exec()  {    Log("executed");  }}Big big;class SomeClass{  Big@ local_big;  SomeClass() { @local_big = @big; local_big.exec(); }  ~SomeClass()  {    Log("SomeClass instance being destroyed");    big.exec();  }}SomeClass@[] some;void test_main(){  some.resize(1);  SomeClass something;  @some[0]=something;}


When I load the module, I see:
Big instance created

And that's fine, I have one global instance of Big in there.

Then I execute test_main(), the log displays
executed

which again is fine and asserts that local_big handle actually points somewhere.

Then I unload all scripts and the log says
Big instance createdBig instance being destroyedSomeClass instance being destroyed

then it crashes, trying to execute big.exec().

So, I see the constructor for Big called twice. I could understand that if @local_big = @big would actually create a copy of big (this would also explain why original big were garbaged before my SomeClass instance, thus leading to crash in it's destructor - since the instance weren't holding a reference to it, but to it's copy). But I see the second constructor call occuring right after I unload the scripts, when everything gets to be garbaged. This ain't no log buffer flushing problem on my part, I am quite sure. And I suspect @local_big = @big shouldn't actually copy anything?
It does indeed look like there might be a problem in that revision. I'll have to verify this. Does this happen in the latest SVN revision too?

Only one instance of the Big class should be created with this script. There is definitely something wrong with the second Big instance being created.

Here's what should have happened:

- During Build()  - The global Big in variable big is created. (not gc, rc = 1)  - The global SomeClass@[] in variable some is created. (gc, rc = 2)- When executing test_main()  - Local SomeClass created (gc, rc = 2)    - SomeClass keeps reference to global Big (rc = 2)  - Reference to local SomeClass stored in global some (rc = 3)  - local reference to SomeClass is released (rc = 2)- When unloading scripts  - The handle in global big is released (rc = 1)  - The handle in global some is released (rc = 1)- When running GC (first pass)  - SomeClass@[] with only one reference will be destroyed (rc = 0)    - Array destructor will release the reference to local SomeClass (rc = 1)- When running GC (second pass)  - local SomeClass with only one reference will be destroyed (rc = 0)    - SomeClass destructor will release reference to Big, and thus destroy it (rc = 0)


gc means the object is garbage collected. rc is the reference count.


AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game

Advertisement
I ran this test with the latest revision, and got the expected result:

Big instance createdexecutedSomeClass instance being destroyedexecutedBig instance being destroyed


AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game

[edit] The above reply showed up before I finished posting. I guess the below can be disregarded.

That pasted code was trimmed down version of my production code. At first it lacked (the equivalent of) local_big handle, which led to crashes. That was understandable, since the GC cannot be expected to know that now-zero-referenced object is going to be called by another object's destructor. So I've added (the equivalent of) local_big to make sure the object still existed, but the magic things happened.

If it's done in a little different way (big turned into handle and initialized in test_main()):
class Big{  Big()  {    Log("Big instance created");  }  ~Big()  {    Log("Big instance being destroyed");  }  void exec()  {    Log("executed");  }}Big@ big;class SomeClass{  Big@ local_big;  SomeClass() { @local_big = @big; local_big.exec(); }  ~SomeClass()  {    Log("SomeClass instance being destroyed");    big.exec();  }}SomeClass@[] some;void test_main(){  @big=Big();  some.resize(1);  SomeClass something;  @some[0]=something;}


then it works as expected (as seen in the log).

Will try this on newer AS revision soon.

[Edited by - TheAtom on October 25, 2010 5:56:29 PM]
Quote: Original post by TheAtom
At first it lacked (the equivalent of) local_big handle, which led to crashes. That was understandable, since the GC cannot be expected to know that now-zero-referenced object is going to be called by another object's destructor.


This was actually a bug (which I fixed now in rev 730). The application should not crash under this circumstances.

With the correction the destructor of SomeClass will now throw a script exception when trying to access the global variable big that has already been destroyed by the engine. But the script exception will simply make the function exit early, the object and it's members will still be destroyed properly without the application crashing.

Regards,
Andreas

AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game

This topic is closed to new replies.

Advertisement