This is a revised version of the locking scheme for SipRefreshManager.
Problem: We have a set of objects, and the objects contain timers.
When a timer fires, the event routine may modify the object containing
the timer. In addition, methods called by other threads may modify
the objects, and may create and delete objects. The nastiest part of
the problem is the race between an externally-called method that wants
to delete an object and the event routine of a timer within the
object.
Limitation: We assume that a timer event routine does not affect
objects other than the one that contains it, and does not delete its
object.
Proposed solution:
The set is given a lock. There is another lock for access to (all of)
the objects.
If a thread (but not a timer) possesses a pointer to an object, the
thread must hold the set lock. If a thread wishes to access an
object (with the exception of stopping one of the object timers), it
must also hold the object lock.
If a thread wishes to create or delete an object, it must hold the
set lock. Since this excludes other threads from possessing a
pointer to the object, the thread need not hold the object lock if
there are no running timers in the object.
A timer (that is started) is treated much like a thread. It need not
hold the set lock as the timer's existence implies that its containing
object exists. But in order to read or modify the object (especially
including restarting itself or other timers), it must hold the object
lock.
In order to prevent deadlocks, if a thread wants to hold both the set
lock and the object lock, it must seize the set lock first.
Each object contains a boolean member, SuppressTimerEventRoutine.
This value is normally false. If it is true, then the body of the
timer event routine for every timer in the object is skipped. A
consequence of this is that a synchronous stop() applied to a timer
when it's object SuppressTimerEventRoutine is true will not start any
timer, and will it hold the object lock only transiently. Reading and
writing SuppressTimerEventRoutine is protected by the object lock.
Pattern for timer event routine:
seize the object lock
if not SuppressTimerEventRoutine
then
perform the operations
end if
release the object lock
Pattern for an externally-called method that does not affect timers:
seize the set lock
look up the object in question
seize the object lock
perform the operations
release the object lock
release the set lock
Most externally-called methods will cause state changes in the object
for which timers should be stopped and started. In that case, a more
elaborate pattern is needed:
seize the set lock
look up the object in question
seize the object lock
set SuppressTimerFire
release the object lock
# After this point, any timer event routines for the object
# that are executed will not start any timers.
stop timer(s) synchronously
# At this point all the object's timers are stopped and
# there are no queued event routine firings.
seize the object lock
clear SuppressTimerFire
perform the operations (which may include starting timers)
release the object lock
release the set lock
Pattern for an externally-called method to delete an object:
seize the set lock
look up the object in question
seize the object lock
set SuppressTimerFire
release the object lock
# After this point, any timer event routines for the object
# that are executed will not start any timers.
stop timer(s) synchronously
# At this point all the object's timers are stopped and
# there are no queued event routine firings.
remove the object from the set
delete the object
release the set lock
Pattern for an externally-called method to create an object:
seize the set lock
create the object
seize the object lock
clear SuppressTimerFire
perform the operations (which may include starting timers)
release the object lock
release the set lock
In SipRefreshManager, timer event routines or externally-called
methods may want to send SIP messages. Since SipUserAgent::send() can
take a long time, the routine must pass responsibility for doing this
to the SipRefreshManager's thread, by queueing a message for
SipRefreshManager::handleMessage to act on. (It cannot be done by a
timer event routine, even after it has released the object lock,
because a method (that holds the set lock) can be waiting for the
timer event routine to finish.) This introduces new race conditions,
in that a message may be sent after the object state changes in a way
that renders the message obsolete, but SIP already allows for
duplicated or delayed messages, so no harm can come from this.
Similarly, any call-back routines to be called must be called from
SipRefreshManager::handleMessage. This also creates race conditions,
but callback routines are not expected to be completely synchronous.
Dale
_______________________________________________
sipx-dev mailing list [email protected]
List Archive: http://list.sipfoundry.org/archive/sipx-dev
Unsubscribe: http://list.sipfoundry.org/mailman/listinfo/sipx-dev
sipXecs IP PBX -- http://www.sipfoundry.org/