Advanced Programming in the UNIX® Environment

(lily) #1
ptg10805159

Section 12.4 Synchronization Attributes 435


main

func1(x)

func2(x)

func1

pthread_mutex_lock(x->lock)

func2(x)

pthread_mutex_unlock(x->lock)

func2

pthread_mutex_lock(x->lock)

pthread_mutex_unlock(x->lock)

Figure 12.6 Recursive locking opportunity

the mutex beforecallingfunc2and reacquire it afterfunc2returns, but this approach
opens a window whereanother thread can possibly grab control of the mutex and
change the data structure in the middle offunc1.This may not be acceptable,
depending on what protection the mutex is intended to provide.
Figure12.7 shows an alternative to using a recursive mutex in this case. We can
leave the interfaces tofunc1andfunc2unchanged and avoid a recursive mutex by
providing a private version offunc2,calledfunc2_locked.Tocallfunc2_locked,
we must hold the mutex embedded in the data structurewhose address we pass as the
argument. The body offunc2_lockedcontains a copy offunc2,andfunc2now
simply acquires the mutex, callsfunc2_locked,and then releases the mutex.
If we didn’t have to leave the interfaces to the library functions unchanged, we
could have added a second parameter to each function to indicate whether the structure
is locked by the caller.It is usually better to leave the interfaces unchanged if we can,
however,instead of polluting them with implementation artifacts.
The strategy of providing locked and unlocked versions of functions is usually
applicable in simple situations. In morecomplex situations, such as when the library
needs to call a function outside the library,which then might call back into the library,
we need to rely on recursive locks.
Free download pdf