Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 03 Sep 2004 15:04:23 -0700
From:      Maksim Yevmenkin <maksim.yevmenkin@savvis.net>
To:        Brooks Davis <brooks@one-eyed-alien.net>
Cc:        freebsd-current@freebsd.org
Subject:   Re: fine grained locking and traversing linked lists
Message-ID:  <4138EA67.8090605@savvis.net>
In-Reply-To: <20040903195528.GA9245@odin.ac.hmc.edu>
References:  <4138BE8D.7000102@savvis.net> <20040903191128.GA649@odin.ac.hmc.edu> <4138C82A.5020304@savvis.net> <20040903195528.GA9245@odin.ac.hmc.edu>

next in thread | previous in thread | raw e-mail | index | archive | help
Brooks Davis wrote:
> On Fri, Sep 03, 2004 at 12:38:18PM -0700, Maksim Yevmenkin wrote:
>>>>recent Robert Watson's locking changes made me to revisit locking in
>>>>the bluetooth code. bluetooth code uses linked lists for various
>>>>objects. quite often it is required to locate a certain object in the
>>>>list. during this operation i have to hold list mutex and individual
>>>>object mutex. this is very inconvenient.
>>>
>>>Why do you have to hold the object mutex?  I can think of scenerios
>>>where that is required, but usually it isn't since they key is fixed at
>>>the time the item is inserted in to the list, or is at least protected
>>>by the list mutex.  For an example of a key protected by the list
>>>mutex, consider struct ifnet's if_xname member relative to ifunit() and
>>>renaming.
>>
>>well, again i'm using bluetooth sockets code as an example. there is a 
>>linked list of all PCB. each PCB has its own lock. so, when i need to 
>>locate PCB by any field i do
>>
>>lock(list);
>>list_foreach(pcb, ...) {
>>  lock(pcb);
>>  if (compare(key)) {
>>    unlock(pcb);
>>    unlock(list);
>>    return (pcb);
>>  }
>>  unlock(pcb);
>>}
>>unlock(list);
>>return (NULL);
>>
>>in sockets layer some functions (i.e. bind, connect, control etc.) can 
>>change PCB fields without holding sockets list lock.
>>
>>so, in some cases i want: lock(list), lock(pcb) and in other cases i 
>>want lock(pcb), lock(list).
> 
> I guess my question was, do you really need to hold the pcb lock to
> do the compare.  It doesn't matter if other fields change if the key
> can not.  I don't know the code in question well enough do answer that
> question.

the key field can change :(

> The key thing is that just because a object has a lock, it does not
> follow that the lock must be held to touch the object.  You just need to
> be sure the object can't be deleted.  If you have refcounted objects,
> the list holds a refrence so if you hold the list lock, you are safe.

yes, i understand this.

>>>>so, i've written a "spherical cow" that shows fine grained locking
>>>>when traversing linked lists (see below). basically, for double linked
>>>>list, in order to safely manipulate by object "y" one must hold three
>>>>locks: object "y" lock, object "x = y->previous" lock and object "z =
>>>>y->next" lock.
>>>>
>>>>so, the $1 million question is: am i missing something? or this will work?
>>>
>>>How do you protect the head in this case?  The list mutex would normally
>>>do so, but if the head can change, you'll need a mutex to protect it
>>>(using an array hid this issue).  Also, doubly linked lists won't work
>>>without a lot of effort (read pain :-) because scanning backwards and
>>>forwards at the same time will lead to deadlock.
>>
>>yes, the head is still the issue. but i think it can be avoided. i think 
>>it only has to be protected when head changes from NULL -> non NULL and 
>>vice versa. otherwise its just lock(head).
>>
>>i do not think that scanning backwards and forwards at the same time is 
>>an issue. it is a (serious?) limitation i agree. but it is possible to 
>>scan list forward staring from any point.

well, after thinking a bit more about the list head protection, i do not 
really need the list head lock. all i need is to ensure the list head 
never changes. this can be easily done by addind the static dummy entry 
and use it as the list head. this way if i want to insert element into 
the list all i need is lock dummy entry (head) and head->next (if any). 
then i can safely insert element after the head.

> If you implement this, I strongly recommend making the lists singally
> linked to avoid the possiablity of this deadlock.

yes, i was thinking the same too. but double-linked list gives o(1) 
deletion (which is very nice :)

so i guess the restrictions are:

1) lock order: item "x", then item "x->previous" (if any), then item 
"x->next" (if any)

2) always scan list forward

3) never use "x->previuos" field (except for "x" locking)

it can even be done with standard sys/queue.h macros :) i just ran 
wintess test and it only complained about "acquiring duplicate lock of 
same type".

thanks,
max



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?4138EA67.8090605>