How we don't crash, by Last.fm
Max Howell
max at last.fm
Mon Feb 16 18:28:24 CET 2009
Hello Amarok people,
Regarding Markey's last mail, perhaps this mail will help. Some just
repeats Markey.
So, this is how we don't crash as much. And we can't crash, at all (if
possible). It's a "people-pay-us-subscriptions" quality thing.
1. Stack allocate everything and pass by value
Except widgets and qobjects. The performance is fine. Especially with
Qt container classes.
2. Parent all widgets and qobjects
Qt deletes them for you, everyone wins.
3. If you have to delete yourself, use a QPointer wrapper
This way you don't risk double frees
4. Singletons return references not pointers
This way you are forced to rethink singleton allocation. It simply has
to exist, always. (Although singletons are evil, you end up with
highly coupled code, but blah).
5. Initialise all data member pointers to zero
Yeah, you could be cool and only init what makes sense today. Like you
immediately assign to m_awesome, so why bother with m_awesome( 0 )?
Because today is today, and tomorrow is tomorrow and this is open
source people, anyone could come along and change everything in this
code and then suddenly m_awesome is the decidely less awesome
0x0000P00P and your delete m_awesome in the dtor crashes.
6. Our track object is never passed by pointer
We use a value-based Track class with a QExplicitlySharedDataPointer d
pointer. This way a copied Track object still is the same Track
object. Which if you think about it is exactly the same as a pointer.
But the good thing is you have to test for null with a function. And
thus there is no null pointer dereferencing And someone can't
accidently delete the track and then cause a crash in some other place
in your code. And users of the object can keep the objects around for
as long as they like.
An alternative is to never pass 0 from you Track injection logic.
Instead pass a new Track(). You have to delete it sometime though.
Which means memory management.
You may have to do the latter if your track object has slots. Although
that was prolly a bad idea if so. Data is data. Some kind of model
should manage the data.
7. Always use deleteLater() on qobjects and qwidgets, (there's a few
places that is daft though, you can figure it out)
calling delete on something from within a slot crashes often. Often
the crash happens way up the stack inside qt because a huge trail of
functions were called, then your slot. when you slot finishes these
functions finish up and prolly one of them tries to use the object you
deleted. Yay!
The best thing with this bug is different versions of Qt may have
different call stacks and deep-usage. So you get bugs that make no
sense.
8. Call the bounds checked versions of the Qt container functions
accessors
This is usually the value() call. In general we always try to use
functionality that returns a safe "null" or "zombie" object if there
is an error. This is so we one, don't crash, and two, have better odds
of handling the failure in a way that doesn't make the GUI look crazy.
Zombie objects return safe empty values.
9. Use throw-away patterns
For instance, lets download an image and put it in a view. Make a new
QObject derived class to do this. Call it ImageDownload0r. Add a
signal kFinishedBai( QPixmap ). Make a start() function. The
controller class does not keep a pointer to the object. The
ImageDownload0r deletes itself (deleteLater!) when its done. The
signal is emitted at the end always (but before the deleteLater!). To
feel more memory conscious parent the downloader the to view it is
populating.
You can feel safe with these classes. There is barely any coupling
with anything else. The more you use them, the less potential crash
bugs there are.
10. Encapsulate everything. Null pointers are more likely when you
don't really feel sure about who is doing what.
I hope this was useful. And I'm sure you all knew most/all of it. Just
trying to be helpful.
Max Howell
Lead Developer, Last.fm
PS liblastfm is out next weekend I hope. I'm doing all the last bits.
I need to get it out urgently in fact so this time I may actually live
up to my promise.
PPS you'd best cc me replies, thx
More information about the Amarok-devel
mailing list