Allegro.cc - Online Community

Allegro.cc Forums » Allegro Development » User event source destructors

This thread is locked; no one can reply to it. rss feed Print
User event source destructors
Karadoc ~~
Member #2,749
September 2002
avatar

It just took me ages to work out that my game was crashing because I hadn't called al_destroy_user_event_source before exiting. I found this kind of surprising because my rule of thumb is that destroy only needs to be called on things that allegro actually creates (such as al_create_event_queue). I figured that since allegro only initialised the event source, it didn't need to be destroyed.

Anyway I was apparently wrong about that. Neglecting to call al_destroy_user_event_source before the end of the program resulted in a potential crash. (It doesn't crash all programs.) So I was wondering, is there anything else I need to know about this? For example, if I register a user event source with an event queue, do I need to unregister it before destroying the queue? or before destroying the event_source? Does it matter which order I destroy the event sources and the event queue? I don't want any more nasty surprises... :(

-----------

Thomas Fjellstrom
Member #476
June 2000
avatar

Does it still crash if you unregister the event source before exiting?

--
Thomas Fjellstrom - [website] - [email] - [Allegro Wiki] - [Allegro TODO]
"If you can't think of a better solution, don't try to make a better solution." -- weapon_S
"The less evidence we have for what we believe is certain, the more violently we defend beliefs against those who don't agree" -- https://twitter.com/neiltyson/status/592870205409353730

Karadoc ~~
Member #2,749
September 2002
avatar

In my tests I was making it crash without registering it in the first place.

Here's the simplest program I came up with which produced a crash:

#SelectExpand
1#include <allegro5/allegro5.h> 2#include <stdio.h> 3 4class Test 5{ 6public: 7 ALLEGRO_EVENT_SOURCE source; 8 Test() 9 { 10 al_init_user_event_source(&source); 11 } 12 ~Test() 13 { 14 al_destroy_user_event_source(&source); 15 } 16}; 17 18int main(void) 19{ 20 if (!al_init()) 21 { 22 printf("error\n"); 23 } 24 Test t; // if this line is commented out, the program will not crash 25 26 ALLEGRO_EVENT_SOURCE source2; 27 al_init_user_event_source(&source2); 28 //al_destroy_user_event_source(&source2); // if this line is uncommented, the program will not crash 29 30 printf("ok then..\n"); 31 return 0; // crashes on exit 32}

To compile, I used "g++ -Wall crash.cpp -lallegro" (g++ is mingw in this case)
As you can see from the comments, the program needs some level of complexity for it to crash (it doesn't crash without that class being created). And when it does crash, it crashes on exit.

-----------

Thomas Fjellstrom
Member #476
June 2000
avatar

You probably shouldn't use allegro types in statically allocated class variables like that.

But I'm not sure it isn't a bug in allegro.

--
Thomas Fjellstrom - [website] - [email] - [Allegro Wiki] - [Allegro TODO]
"If you can't think of a better solution, don't try to make a better solution." -- weapon_S
"The less evidence we have for what we believe is certain, the more violently we defend beliefs against those who don't agree" -- https://twitter.com/neiltyson/status/592870205409353730

Matthew Leverton
Supreme Loser
January 1999
avatar

This is all you need to trigger the crash:

int main(void)
{
  al_init();
  ALLEGRO_EVENT_SOURCE source2;
  al_init_user_event_source(&source2);
  return 0;
}

_al_event_source_free (es=0x7fffffffe1e0) at 4.9/src/evtsrc.c:62
62	      al_unregister_event_source(*slot, es);

Seems like a bug if it's trying to unregister an event source when none were ever registered.

Edit: Actually, on second thought, it's not really a bug. It's more of a limitation imposed by the global destructors. The source2 is going out of scope before the global destructor is being called, which is likely leading to garbage data.

But I'm not sure that Allegro should register a destructor when al_init_user_event_source is called. It seems that it would be very hard for that behavior to actually ever work properly.

For example, if I register a user event source with an event queue, do I need to unregister it before destroying the queue? or before destroying the event_source? Does it matter which order I destroy the event sources and the event queue?

It doesn't matter.

Thomas Fjellstrom
Member #476
June 2000
avatar

But I'm not sure that Allegro should register a destructor when al_init_user_event_source is called.

I'm not sure it would help. I assume the existing global destructor is whats causing the crash, so another global destructor wont help at all.

Yeah, I'm not sure theres a proper way around that. Other than to say "always destroy that object before main exits if your user event source struct is allocated statically on the stack".

--
Thomas Fjellstrom - [website] - [email] - [Allegro Wiki] - [Allegro TODO]
"If you can't think of a better solution, don't try to make a better solution." -- weapon_S
"The less evidence we have for what we believe is certain, the more violently we defend beliefs against those who don't agree" -- https://twitter.com/neiltyson/status/592870205409353730

Matthew Leverton
Supreme Loser
January 1999
avatar

I'm not sure it would help. I assume the existing global destructor is whats causing the crash, so another global destructor wont help at all.

I'm saying Allegro probably shouldn't even register the one it currently is registering.

Even if it's not on the stack, it will probably look like:

struct MY_OBJECT
{
  ALLEGRO_EVENT_SOURCE foo;
  int bar;
}

MY_OBJECT *obj = malloc(sizeof *obj);
al_init_user_event_source(&obj->foo);
free(obj);
return 0;

So in that case, you'd have to manually destroy the event source as well.

It seems simpler to remove the destructor and just make the programmer explicitly call it in every case.

Thomas Fjellstrom
Member #476
June 2000
avatar

So in that case,

I would suggest not doing that. Obviously if your "object" has some special init needs, you also need to clean up after it. That example there is more user bug than anything.

Quote:

It seems simpler to remove the destructor and just make the programmer explicitly call it in every case.

I don't really have anything against making al_exit() manual. Its not really up to me though.

--
Thomas Fjellstrom - [website] - [email] - [Allegro Wiki] - [Allegro TODO]
"If you can't think of a better solution, don't try to make a better solution." -- weapon_S
"The less evidence we have for what we believe is certain, the more violently we defend beliefs against those who don't agree" -- https://twitter.com/neiltyson/status/592870205409353730

torhu
Member #2,727
September 2002
avatar

Yeah, I'm not sure theres a proper way around that. Other than to say "always destroy that object before main exits if your user event source struct is allocated statically on the stack".

"statically on the stack"? You can't have your cake and eat it too :P

Matthew Leverton
Supreme Loser
January 1999
avatar

Obviously if your "object" has some special init needs, you also need to clean up after it.

Of course. My point is simply that the "at exit" destructor doesn't really ever work for the user event sources, and therefore, probably shouldn't ever be registered for the user event sources.

Quote:

I don't really have anything against making al_exit() manual.

I'm only referring to this particular (user event) destructor, not the concept in general. (Although I wouldn't be opposed to removing the implicit shutdown call.)

ImLeftFooted
Member #3,935
October 2003
avatar

I don't like manually releasing events -- that's not Allegro style.

What's the argument against the old style?

ALLEGRO_EVENT_SOURCE *source2 = al_init_user_event_source();

Matthew Leverton
Supreme Loser
January 1999
avatar

See the documentation:

struct THING {
    ALLEGRO_EVENT_SOURCE event_source;
    int field1;
    int field2;
    /* etc. */
};

The idea behind the init style is that you can "extend" the user event structure.

Allegro could have both al_init_user_event_source() and al_create_user_event_source(). (I believe the create function is how it used to be.) It would make sense for something initialized with the create style to have a global destructor, but I think it leads to problems with the init version.

Thomas Fjellstrom
Member #476
June 2000
avatar

I'm only referring to this particular (user event) destructor, not the concept in general. (Although I wouldn't be opposed to removing the implicit shutdown call.)

Well technically there is only one atexit destructor registered. But I suppose you mean the code in that destructor that goes and cleans up user event sources.

--
Thomas Fjellstrom - [website] - [email] - [Allegro Wiki] - [Allegro TODO]
"If you can't think of a better solution, don't try to make a better solution." -- weapon_S
"The less evidence we have for what we believe is certain, the more violently we defend beliefs against those who don't agree" -- https://twitter.com/neiltyson/status/592870205409353730

Karadoc ~~
Member #2,749
September 2002
avatar

This is all you need to trigger the crash:

int main(void)
{
  al_init();
  ALLEGRO_EVENT_SOURCE source2;
  al_init_user_event_source(&source2);
  return 0;
}

That's might well trigger the problem that causes the crash, but on my computer that code does not crash (it's the same as my original example with the Test t; line commented out, which I noted does not crash.) I suppose the actual behaviour is a bit unpredictable, as is often the way with these memory bugs.

I don't really know what al_destroy_user_event actually does, but from my naïve point of view it seems wrong that I have to call "al_destroy" on something that allegro didn't actually create – and then the thing doesn't even get destroyed anyway, it only get 'deinitialized'. Also, I can't guess what important atexit stuff actually needs to be done for these event sources. Does al_init_user_event_source really do something that can't be undone by simply terminating the program? That seems unlikely to me.

I suppose I should look at the allegro code to get a better understanding of what's actually going on; but I get the impression that people more knowledgeable than me are already thinking about it...

-----------

Matthew Leverton
Supreme Loser
January 1999
avatar

al_init_user_event_source() dynamically allocates some memory, so you'd get a memory leak if it's not destroyed.

The changes I would make to Allegro are:

  • Don't automatically destroy user event sources that were created via init(). Make it the programmer's responsibility to free it up, since he most likely is responsible for the memory there.


  • Document the init() function accordingly that the memory must explicitly be destroyed when you are done with it.

Karadoc ~~
Member #2,749
September 2002
avatar

If al_destroy_user_event_source is basically just freeing some memory, then I completely agree with your suggested changes. As you pointed out earlier, the automatic destruction of these event sources cannot work anyway because they will always be out of scope by the time they get automatically destroyed (unless they are a global variable on the stack, or a variable on the heap that the user didn't free).

This problem of trying to free memory when the pointer has already been lost reminds me of why I made my 'salloc' library ages ago... (a 'safe' alternative to malloc)

-----------

Peter Wang
Member #23
April 2000

It's not only a memory leak. If you free the memory for a user event source without unregistering it from all event queues then you are left with dangling pointers.

I agree with Matthew's changes.

Go to: