-
When manual reference counting and you must replace an object with a new one
always ref the new object before unrefing the old one, otherwise you risk
deleteing the object prematurely if the object is replaced by itself. This bug
can be very hard to find since the memory is reused in the same context
immediatly so memory cooruption errors will probably not occur for quite a
while. If you explicitly disallow replacement by the same object for some reason
ALWAYS include an 'assert(old_object != new_object)'.
-
Use asserts VERY liberally, when writing a function the first thing you should
think of is, 'what assumptions is this function making about its arguments and
the envirnment?' and then 'assert' that these are true. but be careful, asserts
used properly should only induce at most a constant slowdown of code, only
assert local information and envirnment. ie, dont have code that works on binary
trees do an 'assert(traverse whole binary tree and make sure is sane)' on entry
and exit to every function call or your nice logarithmic algorithms will turn
into exponential ones, which does more than just slow down your program it
actually radically modifies the relative speed of components of your system
which can only serve to mislead you as to where one must optimize and useability
analysis. a program running under normal debugging should run in a similar
fashion to the optimized version just uniformly slower. Just remember if every
part of a data structure that a function touches is sane then the whole
structure is sane. Of course this rule can be broken temporarily when tracking
down a particulary nasty bug.
-
multithreaded code is almost always more difficult but not impossibly so as some
might lead you to believe. Just remember that multithreaded interaction is by
nature non-deterministic, trial-and-error coding does not cut it even for
languages with advanced type systems. since every run of the code can be
different and many possible runs of the code will not even happen on your
particular machine you can never exaustivly test all input cases to your code.
One just needs to be able to logically reason about thread interactions less
memory cooruption and/or deadlock will result. if more than one thread ever use
the same data structure it must somehow be protected by a mutex. if you ever
find yourself needing to lock more than one mutex at once ask if there is ever
any other thread that will lock them in the opposite order, if so then you have
a possible deadlock. As long as you keep things like this in mind whenever you
write multithreaded code it should be safe, of course there are many other
things to take into account but as long as you have a model in your mind of how
any two threads will interact and how they will not step all over each other
then you are on the right track.
-
learn to use the basics of a debugger, just enough to get a stack backtrace from
a core file. a debugger will tell you exactly what line of code your program
core dumped on and show you all calls leading up to that dump. using gdb once
you have a dump (say from a segfault) type 'gdb <programname> core' and
then at the prompt 'bt' (for backtrace).