Multiple locks and missing wakeup.

John Baldwin jhb at freebsd.org
Tue Apr 8 18:12:47 UTC 2014


On Tuesday, April 08, 2014 12:45:49 pm Edward Tomasz Napierała wrote:
> Wiadomość napisana przez John Baldwin w dniu 8 kwi 2014, o godz. 16:01:
> 
> > On Tuesday, April 08, 2014 2:34:30 am Edward Tomasz Napierała wrote:
> >> Let's say I have a kernel thread processing elements from a queue,
> >> sleeping until there is work to do; something like this:
> >> 
> >> mtx_lock(&mtx1);
> >> for (;;) {
> >> 	while (!LIST_EMPTY(&list1)) {
> >> 		elt = LIST_FIRST(&list1);
> >> 		do_stuff(elt);
> >> 		LIST_REMOVE(&list1, elt);
> >> 	}
> >> 	sleep(&list1, &mtx1);
> >> }
> >> mtx_unlock(&mtx1);
> >> 
> >> Now, is there some way to make it work with two lists, protected
> >> by different mutexes?  The mutex part is crucial here; the whole
> >> point of this is to reduce lock contention on one of the lists.  The
> >> following code would result in a missing wakeup:
> > 
> > All our sleep primitives in the kernel only accept a single wait channel.
> > It sounds like you want something more like select() or poll() where you
> > can specify multiple wait channels.  There isn't a good way to do that
> > currently.  You could write one, but it would be a bit hard to do
> > correctly.
> 
> Perhaps I should have been more clear: I'm ok with a single wait
> channel.  The problem is that there is no way to pass more than one
> mutex to the sleep() function, so we can miss wakeup for the list
> protected by the second lock, if something gets enqueued between
> releasing mtx2 and calling sleep().
> 
> >  In practice you'd end up implementing something that boiled
> > down to having a single wait channel with a common lock that protected
> > it so you could do something like:
> 
> The whole purpose of this is to avoid locking mtx1 in the the enqueue
> routine for the second list, for contention reasons.

Ah, but note that I didn't lock mtx1 in the enqueue routine, I marked
the 'combo_mtx' which is only used for the sleep/wakeup.

> [..]
> 
> > Another way to do this would be to be a bit more poll-like (e.g. if
> > you wanted a generic mechanism for this) where you have some sort of
> > 'poller' structure and you set a flag before starting a scan of all
> > your backends.  Any wakeup that occurs while scanning clears the
> > flag, and you only sleep if the flag is still set at the end of the
> > scan, etc.
> 
> But the flag would have to be protected by the mutex we pass
> to sleep(), and would require grabbing that mutex in both enqueue
> routines, right?

Yep.

-- 
John Baldwin


More information about the freebsd-hackers mailing list