On Sat, Feb 6, 2016 at 8:28 AM, Ben Coman <btc@openinworld.com> wrote:
On Fri, Feb 5, 2016 at 1:16 AM, Ben Coman <btc@openinworld.com> wrote:
On Wed, Feb 3, 2016 at 2:35 AM, Eliot Miranda <eliot.miranda@gmail.com> wrote:
Hi Ben,
On Tue, Feb 2, 2016 at 9:54 AM, Ben Coman <btc@openinworld.com> wrote:
While we are in the process of revising the Mutex infrastructure [1] to use dedicated primitives rather than rely on counting via a semaphore, I want to raise the question of simple versus recursive mutexes.
This isn't something I know much about, except that some people strongly believe recursive mutexes are evil, including David Butenhof who apparently was the guy who added recursive mutexes to POSIX "on a dare [...] but nobody was supposed to use recursive mutexes." [1].
There are two interesting discussion on stockoverflow [2][3]. But btw I disagree with Tall Jeff [3] says "The difference between a recursive and non-recursive mutex has to do with ownership. " I think he confuses a simple-mutex and binary-semaphore, as do a some commenters.
"Prefer Simple Mutex Over Recursive Mutex" [4] demonstrates some performance considerations and advises "a recursive mutex is dangerous because you lose sense of locking scope. It costs more than a simple mutex."
"Recursive Locks Will Kill You!" [5] provides some thread safety guidelines.
Finally, there is a paper "Ad Hoc Synchronization Considered harmful" [6], which I haven't read yet since I wanted to get this post out before heading to bed, but I hope to tomorrow.
[1] http://www.zaval.org/resources/library/butenhof1.html [2] http://stackoverflow.com/questions/2415082/when-to-use-recursive-mutex [3] http://stackoverflow.com/questions/187761/recursive-lock-mutex-vs-non-recurs... [4] http://askldjd.com/2009/10/26/prefer-simple-mutex-over-recursive-mutex/ [5] http://www.fieryrobot.com/blog/2008/10/14/recursive-locks-will-kill-you/ [6] https://www.usenix.org/legacy/event/osdi10/tech/full_papers/Xiong.pdf
So our existing Mutex implementation happens to be recursive, and I'm not suggesting we change that. However if we should consider *not* baking the *assumption* of recursion into the primitives, so the same primitives could *also* be used for a SimpleMutex class. The logic to provide recursion for Mutex is only [ owner = activeProcess ] which is easily done in-image.
So I propose introducing aquire/release primitives based off the existing critical section primitives with the recursion removed.
We have two sets of primitives, the queueing semaphore ones, signal and wait, #85 & #86, which are not recursive, and new ones, #185, #186 & #187 that support ownership, and hence support recursion. It makes no sense to remove the recursion support. That's what the primitives are for, to provide ownership support as efficiently as possible. Please don't be led by the problems others may have in other systems, and instead be led by how we're doing. Squeak and Pharo (and Smalltalk) have had recursive owned critical sections for decades and no one (e.g. from demanding industrial control applications) has complained that these recursive critical sections are problematic.
I respect your deep experience, but there is an engineering principle that "just because it works, doesn't mean its correct. It may just be reinforcing some misconceptions" ;)
Several reasons why people may not complain... 1. the expectation its been there so long that its correct 2. the expectation expert people worked on it so its correct 3. subtle bugs remain latent or trigger infrequently, thus hard to isolate 4. when bugs arise, they are fixed pragmatically without deeper philosophical consideration 5. coincidently other good programming practices avoided problematic situations 6. Threads run concurrently and co-operatively, so maybe in practice there are less issues than a parallel and pre-emptive systems.
Okay class... your new word for the day is "hubris". It was hubris for me to lay it out like that. It was easy to think I could do better from a distant hand waving perspective, but having spent a few hours in the trenches playing concretely with the primitive code, I've realised the current primitiveEnterCriticalSection is optimum.
I would think a thread doing an in-image (owner = activeProcess) test would be unaffected by suspension points since it doesn't change any state and only the same thread would have set owner via the primitive.
The new primitives give us a streamlined and hack-free implementation which doesn't depend on the niceties of what are suspension points as do the pre-primitive implementations. So I see no rationale for gelding these primitives.
You are right. Actually, as Dennis indicated, the existing primitive already provides everything needed...
RecursiveMutex>>critical: aBlock ^self primitiveEnterCriticalSection ifTrue: [aBlock value] ifFalse: [aBlock ensure: [self primitiveExitCriticalSection]]
SimpleMutex>>critical: aBlock ^self primitiveEnterCriticalSection ifTrue: [self error: 'recursive primitives will kill you'] ifFalse: [aBlock ensure: [self primitiveExitCriticalSection]]
Dennis, I no longer think we need a new primitive for #acquire. We should just use 186. Regarding the inverted logic, we either live with it, or perhaps name it something like #acquiredAgain. For example we might have... Mutex>>criticalNew: aBlock lock acquiredAgain ifTrue: [aBlock value] "recursive" ifFalse: [ aBlock ensure: [ lock release] ]. "first time" OwnedLockTest>>testAcquireLockTwiceFromSameProcess | acquiredAgain | self fork: [ lock acquiredAgain. acquiredAgain := lock acquiredAgain ]. self waitLastProcessFinished. self assert: acquiredAgain description: 'acquiring lock twice should return true' but I'm not sure about my #acquiredAgain suggestion and hope to hear what others think. cheers -ben