Timers can produce their own special problems with races. Consider a collection of objects (list, hash, etc) where each object has a timer which is due to destroy it.
If you want to destroy the entire collection (say on module removal), you might do the following:
/* THIS CODE BAD BAD BAD BAD: IF IT WAS ANY WORSE IT WOULD USE HUNGARIAN NOTATION */ spin_lock_bh(&list_lock); while (list) { struct foo *next = list->next; del_timer(&list->timer); kfree(list); list = next; } spin_unlock_bh(&list_lock);
Sooner or later, this will crash on SMP, because a timer can
have just gone off before the spin_lock_bh()
,
and it will only get the lock after we
spin_unlock_bh()
, and then try to free
the element (which has already been freed!).
This can be avoided by checking the result of
del_timer()
: if it returns
1, the timer has been deleted.
If 0, it means (in this
case) that it is currently running, so we can do:
retry: spin_lock_bh(&list_lock); while (list) { struct foo *next = list->next; if (!del_timer(&list->timer)) { /* Give timer a chance to delete this */ spin_unlock_bh(&list_lock); goto retry; } kfree(list); list = next; } spin_unlock_bh(&list_lock);
Another common problem is deleting timers which restart
themselves (by calling add_timer()
at the end
of their timer function). Because this is a fairly common case
which is prone to races, you should use del_timer_sync()
(include/linux/timer.h
)
to handle this case. It returns the number of times the timer
had to be deleted before we finally stopped it from adding itself back
in.