Allegro.cc - Online Community

Allegro.cc Forums » Programming Questions » Deleting moved memory crashes

Credits go to anonymous, Tomoso, and weapon_S for helping out!
This thread is locked; no one can reply to it. rss feed Print
Deleting moved memory crashes
OnlineCop
Member #7,919
October 2006
avatar

Original post updated -- 2009/08/22

Thanks for the help so far all. This is the current working version... sorry if only a few of you can compile it. It compiles and runs on my Mac, at least.

I've uploaded a more recent source code if anyone wants to check it out. It now supports mouse control.

It doesn't take all of the parameters from "config.xml" yet like I want, and there are still several things hard-coded, but those things are being removed as I continue.

Please post feedback as you have them; I always welcome comments.

============================================================

I've been trying to get that bubble popping game done for a while.

The version I have uploaded to this post isn't interactive yet, but it's not going to be very hard to actually implement a mouse (it may take less than a day to add support for that).

I tried compiling it on Windows, since I know the majority of you are on Windows, and I'll probably want my game to be Windows-based more than Mac-based, but for some reason, the src/TimeKeeper.cpp complains about DWORD not being defined when compiling with gcc unless I #include "winalleg.h" right above line 34:

#SelectExpand
30# ifdef HAVE_SCHED_H 31# include <sched.h> 32# endif 33#else // !defined(_WIN32) 34# include <mmsystem.h> 35static unsigned long int lastTime = 0; 36static LARGE_INTEGER qpcLastTime;

like this:

#SelectExpand
30# ifdef HAVE_SCHED_H 31# include <sched.h> 32# endif 33#else // !defined(_WIN32) 34# include <winalleg.h> 35# include <mmsystem.h> 36static unsigned long int lastTime = 0; 37static LARGE_INTEGER qpcLastTime;

and even then, I'm still getting a few other compiling errors (but not as many).

Anyway, the problem I'm facing is in Arena::~Arena().

Arena contains three std::list<Ball*> lists.

  • pBalls have infinite lifespans, unless they get popped. They bonce around the screen freely.

  • pPoppedBalls have about a 3-second lifespan, and pBalls can collide with them. They are immobile (pBalls become pPoppedBalls themselves when they collide, and themselves become immobile)

  • pRemovedBalls are removed from the arena completely, and no testing at all are done with them. It would be the same thing if I were to free their memory as soon as they enter this group, but for efficiency reasons, deleting the memory is not done till the Arena is destroyed at the end.

To move the balls from one list to another, I locate the ball in question, then use the std::splice() command to transfer from one list to another. I am STILL able to "see" all the balls in pBalls and pPoppedBalls through the lifetime of the game (I know, because they continue to pop each other and bounce around), so I know the pointers aren't invalidated.

However, when I go in and try to delete the memory within Arena::~Arena() on destruction, I am only able to do so on pBalls without causing a SEGFAULT. If I try to do it on pPoppedBalls or pRemovedBalls, I get errors (I had to #if 0...#endif those sections out). This is bad, though, because this is a HUGE memory leak, and I intend on having multiple levels and rounds of gameplay (like if you hit "R", you get to start a new round...

This compiles on my Mac without problem using the makefile, but if anyone else can take a look and give me some help, I'd appreciate it. Cookies!

P.S. If this looks like a fun project, and someone wants to help with any part of it (sounds, graphics, ... anything ...), I'd love it!

Tomoso
Member #3,128
January 2003
avatar

I'm guessing that's because you only have one version of each Ball* obj created. When you move them from list to list you are only moving a pointer not the actual object. So when you try to delete the same obj more than once, that's where your segfault comes in.

Lazy Noob - Blog

OnlineCop
Member #7,919
October 2006
avatar

Tomoso said:

When you move them from list to list you are only moving a pointer not the actual object.

That's actually what I was intending to do. Instead of doing an expensive move, I just want to reassign who "owns" the pointer so it can do all the updates.

pBalls.size() shows that there are the correct number of balls in it: 50, 25, 10, etc. And pPoppedBalls.size() increases correctly. When I'm calling update(), the balls within pBalls are moving and the balls within pPoppedBalls are growing, shrinking, and being removed correctly, so I at least know that the pointers are going to the actual Ball* objects...

So am I actually trying to double-delete the same memory?

Also, when I try to delete the memory, I usually do this:

#SelectExpand
1std::list<Ball*>::iterator itr; 2for (itr = pBalls.begin(); itr != pBalls.end(); ++itr) 3{ 4 Ball* ball = *itr; 5 if (ball) 6 { 7 delete ball; 8 ball = NULL; 9 } 10} 11 12for (itr = pPoppedBalls.begin(); itr != pPoppedBalls.end(); ++itr) 13{ 14 Ball* ball = *itr; 15 if (ball) 16 { 17 delete ball; 18 ball = NULL; 19 } 20}

So the memory should be freed and then it should be set to NULL, shouldn't it? Then if the next loop (from within pPoppedBalls) gets to the same address, wouldn't it point to NULL instead of to an address? Or has my logic failed me?

Should, instead of passing a Ball* around, I be passing the actual Ball object around?

Tomoso
Member #3,128
January 2003
avatar

If I'm honest I've never used std::list I prefer vectors, since most of what I want to do doesn't matter if I can move back and forth or not at will. I also don't use iterators, again because I haven't found a use for them where I desperatley required one.

I use vectors like so below:

#SelectExpand
1#include <cstdlib> 2#include <iostream> 3#include <vector> 4 5using namespace std; 6 7int main(int argc, char *argv[]) 8{ 9 vector<int*> somevector; 10 11 int *a = new int(10); 12 int *b = new int(20); 13 int *c = new int(30); 14 int *d = new int(40); 15 16 int *o = a; 17 18 somevector.push_back(a); 19 somevector.push_back(b); 20 somevector.push_back(c); 21 somevector.push_back(d); 22 23 /* Remove *a from vector */ 24 for (int i = 0; i < somevector.size(); i++) { 25 if (somevector.at(i) == o) { 26 somevector.erase(somevector.begin() + i); /* Pointer only removed from vector */ 27 } 28 } 29 30 /* Print vector */ 31 cout << "B, C, D" << endl; 32 for (int i = 0; i < somevector.size(); i++) { 33 cout << *somevector.at(i) << endl; 34 } 35 36 /* Printe *a */ 37 cout << "A: " << *a << endl; 38 39 /* Delete everything within vector */ 40 for (int i = 0; i < somevector.size(); i++) { 41 delete somevector.at(i); /* Deletes the actual object but not the pointer within */ 42 somevector.at(i) = NULL; /* the vector */ 43 } 44 somevector.clear(); /* Clears all pointers within the vector */ 45 46 cout << "Vector Size: " << somevector.size() << endl; /* Should be 0 */ 47 48 delete a; 49 50 system("PAUSE"); 51 return EXIT_SUCCESS; 52}

ps, In my defense I might have made some errors in my haste, not garunteed to work!

Lazy Noob - Blog

weapon_S
Member #7,859
October 2006
avatar

The fallback case for Timekeeper::sleep seems to be on all the time.
Nothing is happening with it, but each ball contains an Arena pointer (that is invalidated after the first delete).
BTW what happened to other bubble popping game?

anonymous
Member #8025
November 2006

OnlineCop said:

So the memory should be freed and then it should be set to NULL, shouldn't it? Then if the next loop (from within pPoppedBalls) gets to the same address, wouldn't it point to NULL instead of to an address? Or has my logic failed me?

From that description, yes, your logic has failed you. If you have a pointer to the same object in more than one list, then you are pretty much screwed. (Each list contains a copy of the pointer, setting one to NULL couldn't possible affect others.)

You'd need something smarter, like reference-counted pointers (std::tr1::shared_ptr/boost::shared_ptr) which delete the shared object when the last shared_ptr to it goes out of scope.

Or you might store all the pointers in one list that you don't mess around with, and finally delete everything in that and not elsewhere.

OnlineCop
Member #7,919
October 2006
avatar

weapon_S:

  1. TimeKeeper::sleep is a poor substitute for allegro's rest(). I had actually started this project with "correct" allegro timing, using the examples off of the allegro wiki as a basis, but for some reason, didn't see any sort of difference in CPU usage. Maybe the Mac's Activity Monitor just lies and it was working fine and I didn't know it.

    The way that I implemented TimeKeeper::sleep() is to "sleep" based on how much time the game wants to give back to the CPU, depending on the framerate. So if you play with the + and - keys on the keypad and push the framerate down to near zero, you'll notice that the amount of "sleep" time increases to almost a full second when FPS drops to 1 frame per second, and down to 1/200th of a second when FPS is 200 (give or take a little for some other processing). It's probably a horrible way to do an allegro rest() workaround, but I didn't really know what else to do for it.

  2. I actually had the other bubble popping game compile well on my Mac, and it looked pretty decent for an early beta. Then I uploaded it to the Linux lab for our professors to grade and it bombed when we tried to compile it and get it to show off my all-night last-minute feature additions. So I felt badly and haven't looked at it again. *sigh* Maybe it needs a good revival. We'll see.

EDIT 1:
anonymous: That approach might actually be the best way to go. I can assign everything and stick it in a std::vector<Ball*>. Then, I'll point to everything with the various std::list<Ball*> lists (I use lists because they get removed randomly and don't want to shuffle the memory around, which is what using std::vector<Ball*> would be doing), and just free memory from the main vector in the Arena::~Arena(). Is that correct? That's actually a pretty good idea...

EDIT 2:
anonymous: Your solution works. I tested it with no balls popped, a few balls popped, all balls popped, all balls popped and removed from the field, and restarting the game multiple times. All balls' destructors were called in order, and it shows that everything is being destroyed correctly. Thanks for the simple solution. I should update my OP with the new code so others can play with my code and make feature improvement suggestions!

EDIT 3:
I've updated the attached zip file for the original post, if anyone is able to compile and run the program. anonymous, were you able to compile the program, or were you just looking through the code?

It now supports mouse! Score isn't implemented, and there's no way I'm plunging into sound support at this point (I've never been able to do sound, so if there are volunteers, you'll get 100% of the viewable space on any credits screen for your own logo under the "sound" section ;D:-*:P), but take a look (for those who can).

And if anyone can help me get this so it will compile on Windows (and other platforms), I'd love::) you too.

anonymous
Member #8025
November 2006

OnlineCop said:

anonymous, were you able to compile the program, or were you just looking through the code?

I wasn't able to compile it (MinGW 4.4.1) and just read the description of your problem. (When I get a makefile, I'm supposed to run make in that directory? - The damned thing didn't find <allegro.h> in the compiler's main include folder with all related errors.) When I tried with the IDE I got the DWORD error with older code, and errors concerning various missing string functions with the newer version (xml code) regardless how the control macros were set (missing str(n)casecmp with _XMLWINDOWS commented and missing _strnicmp etc otherwise).

But looking at the source, you seem to have a lot of faith in setting pointers to NULL :) E.g in destructors - this may only help you catch errors where you access an already deleted object (it's members will be NULL), as long as that memory doesn't happen to be occupied by some new object.

---

Ok, added the compiler's include path to the makefile. Now I get an error with:

//ball.h line 15:
BallConstraints::BallConstraints();

//should be: BallConstraints();

Beyond that it is back to the errors with stricmp and such.

OnlineCop
Member #7,919
October 2006
avatar

anonymous, when I tried on Windows, it said that I was missing winmm.lib, so I uploaded (to the original post) a version where the makefile defaults to Windows and links to winmm, if you have it.

Yes, you need to be in the same directory as the makefile to build. From the command prompt, I usually do the following:

#SelectExpand
1cd prince_popper 2C:\some_dir\prince_popper>make depend 3g++ -MM -MG -I./include ./src/*.cpp > _depend.tmp 4sed -e "s/^\([a-zA-Z0-9_]*\)\.o:/./obj/\1\.o:/" _depend.tmp > ./obj/makefile.dep 5rm -rf _depend.tmp 6 7C:\some_dir\prince_popper>make 8g++ -o "obj/Arena.o" -c -W -Wall -Wno-unused -pg -g3 -ggdb3 -O0 -DDEBUG -I./include ./src/Arena.cpp 9g++ -o "obj/Ball.o" -c -W -Wall -Wno-unused -pg -g3 -ggdb3 -O0 -DDEBUG -I./include ./src/Ball.cpp 10g++ -o "obj/BallKeyframe.o" -c -W -Wall -Wno-unused -pg -g3 -ggdb3 -O0 -DDEBUG -I./include ./src/BallKeyframe.cpp 11g++ -o "obj/Game.o" -c -W -Wall -Wno-unused -pg -g3 -ggdb3 -O0 -DDEBUG -I./include ./src/Game.cpp 12g++ -o "obj/Main.o" -c -W -Wall -Wno-unused -pg -g3 -ggdb3 -O0 -DDEBUG -I./include ./src/Main.cpp 13g++ -o "obj/TimeKeeper.o" -c -W -Wall -Wno-unused -pg -g3 -ggdb3 -O0 -DDEBUG -I./include ./src/TimeKeeper.cpp 14g++ -o "obj/Timer.o" -c -W -Wall -Wno-unused -pg -g3 -ggdb3 -O0 -DDEBUG -I./include ./src/Timer.cpp 15g++ -o "obj/xmlParser.o" -c -W -Wall -Wno-unused -pg -g3 -ggdb3 -O0 -DDEBUG -I./include ./src/xmlParser.cpp 16g++ -o "./bin/test" -W -Wall -g3 -ggdb3 -O0 -pg ./obj/Arena.o ./obj/Ball.o ./obj/BallKeyframe.o ./obj/Game.o ./obj/Main.o ./obj/TimeKeeper.o ./obj/Timer.o ./obj/xmlParser.o -lalld -lwinmm 17 18C:\some_dir\prince_popper>

You'll need sed for the "make depend" part, liballd.lib and libwinmm.lib (or liballd.a and libwinmm.a, depending on how your system names library files) in your library path, and whatever dependencies stricmp was complaining about earlier.

Thanks about noting the problem with Ball.h's "BallConstraints::BallConstraints();"... why the compiler didn't yell at me, I don't know. Thanks.

This now compiles and runs fine on my WinXP with no modifications. Can anyone else test and try this version? Again, attachment is on the OP.

Feedback welcome and appreciated!

TeamTerradactyl
Member #7,733
September 2006
avatar

Nice.

OnlineCop
Member #7,919
October 2006
avatar

Updated original attachment again.

I really would like to learn Events one of these days. But for everyone (anyone?) interested in this project or thread, here are a few changes:

  • Balls no longer constrained to just 30, 45, and 60-degree movement; now any angle between 30-60 degrees in any of the four quadrants is used.

  • Scores should now update correctly.

  • Everything is moving toward being more data-driven than hard-coded (even if it's not fully implemented, the framework is "getting there" to handle it).

  • FPS starts at a default of 30 instead of 200 now (can be upped to 200 and lowered to 1 with the keypad +/- keys).

  • All floats replaced with doubles to reduce rounding errors.

  • Other changes. I've been working on this a while... Should have kept a running log...

Comments on the current version always welcome, and if this DOES compile for others out there, let me know.

Controls: Left mouse click (you can only "drop" a ball onto a playing field once per round... the whole point of the game: maximize your score).

key[KEY_R]: Reset field, play another round/level
key[KEY_P]: Debugging key for now: Pause gameplay (won't pause "popped" bubbles: can be a disadvantage)
key[KEY_U]: Debugging key for now: Unpause gameplay, unless a game is over
key[KEY_F]: Toggle between fullscreen and windowed mode
key[KEY_Q], key[KEY_ESC]: quit the program

You can also use some command-line arguments to change a few of the settings. Like the dimensions of the arena (a long narrow strip would make it very easy to win, hint-hint). You can find those by reading through the source code yourself. ;)

Go to: