[Vm-dev] Where to get Monitor implementation based on primitives?

Eliot Miranda eliot.miranda at gmail.com
Fri Jan 8 17:37:10 UTC 2016


Hi Ben,

On Fri, Jan 8, 2016 at 7:25 AM, Ben Coman <btc at openinworld.com> wrote:

>
> On Fri, Jan 8, 2016 at 9:39 PM, Ben Coman <btc at openinworld.com> wrote:
> > On Fri, Jan 8, 2016 at 5:42 PM, stephane ducasse
> > <stephane.ducasse at gmail.com> wrote:
> >>
> >> I have a (stupid) question.
> >> Is the code running without the primitives?
> >> Are the code below the primitives correct?
> >> I asked that because we can have 100 eyes and brains on the smalltalk
> level and far less on the VM primitive level.
> >
> > Because:
> > 1. Concurrency bugs can be subtle and the *exact* conditions can be
> > hard to reproduce for debugging.  For example, the solution to a
> > couple of problems with Delay [1] [2] were solved by moving away from
> > Semaphore>>critical: to use signalling.
> >
> > 2. The in-image atomicity of determining whether a signal was actually
> > consumed or not during process suspension/termination is awkward.  Its
> > seems hard to *really* know for sure it right (but I haven't looked in
> > depth into Denis' latest proposals.)
> >
> > 3. The existing in-image implementation of Semaphore>>critical messes
> > around in  Process>>terminate in a *special* way that is not easy for
> > those 100 eyes to understand. For example, I personally am not
> > comfortable with understanding how the special Semaphore handling in
> > Process>>terminate works, but I can easily follow how
> > primitiveEnterCriticalSection just looking at the code [3].
>
> Points 2 & 3 might possibly be addressed by having new
> primitiveWaitReturned
> *always* return true, so if the process is terminated while waiting,
> the assignment to signalConsumed doesn't occur...
>
>   critical: mutuallyExcludedBlock
>     signalConsumed := false.
>     [
>         signalConsumed := self primitiveWaitReturned.
>         blockValue := mutuallyExcludedBlock value
>     ] ensure: [ signalConsumed ifTrue: [self signal] ].
>     ^blockValue
>
> where primitiveWait (https://git.io/vuDjd) is copied
> and (just guessing) the marked line added...
>

That looks like a good idea.  I'll try and take a look early next week.


>
>   primitiveWaitReturned
>       | sema excessSignals activeProc inInterpreter |
>       sema := self stackTop. "rcvr"
> "==>>" self pop: argumentCount + 1 thenPush: objectMemory trueObject.
> "<<=="
>      excessSignals := self fetchInteger: ExcessSignalsIndex ofObject: sema.
>       excessSignals > 0
>           ifTrue:
>             [self storeInteger: ExcessSignalsIndex
>                         ofObject: sema
>                         withValue: excessSignals - 1]
>           ifFalse:
>             inInterpreter := instructionPointer >= objectMemory
> startOfMemory.
>             activeProc := self activeProcess.
>             self addLastLink: activeProc toList: sema.
>             self transferTo: self wakeHighestPriority from: CSWait.
>             self
> forProcessPrimitiveReturnToExecutivePostContextSwitch: inInterpreter]
>
> which I guess could be added quickly if Esteban could compile the
> latest pharo-spur-vm ;)
>

If you built a Squeak VMMaker image via
http://www.squeakvm.org/svn/squeak/branches/Cog/image/BuildSqueakSpurTrunkVMMakerImage.st
and built using the svn source tree you could do this yourself now.


> cheers -ben
>
> > 4. Its faster
> >
> > btw, Looking at the following...
> >   CriticalSection>>critical: aBlock
> >       ^self primitiveEnterCriticalSection
> >           ifTrue: [aBlock value]
> >           ifFalse: [aBlock ensure: [self primitiveExitCriticalSection]]
> >
> > without intimate VM knowledge but seeing Eliot recently say
> > "invocations of methods with primitives [...] are not suspension
> > points, unless their primitives fail" -- I'm paranoid that since
> > #ensure: primitive 198 always fail, there might be some some small
> > window for a race where a process might be terminated before
> > #primitiveExitCriticalSection can be executed. I'll take a refutation
> > of this to improve the method comment.
> >
> > The following instils more confidence...
> >
> >   CriticalSection2>>critical: aBlock
> >       | reEntered |
> >       reEntered := false.
> >       [ reEntered := self primitiveEnterCriticalSection.
> >          aBlock value ] ensure:
> >          [ reEntered ifFalse: [ self primitiveExitCriticalSection] ]
> >
> > but performance is reduced...
> > "#('Mutex -> 282,511 per second'
> >   'Monitor -> 479,217 per second'
> > 'CriticalSection -> 729,049 per second'
> > 'CriticalSection2 -> 571,631 per second')"
> >
> > Then again, any termination from within a critical section is anyhow
> > problematic since potentially data structures are left in a
> > indeterminate state.
> >
> > [1] http://forum.world.st/Super-fast-delay-td4787257.html
> > [2] https://pharo.fogbugz.com/default.asp?13755
> > [3] https://git.io/vuDnA
> >
> > cheers -ben
> >
> >>
> >> Stef
> >>
> >>
> >>
> >>
> >> Hi Ben,
> >>
> >> On Thu, Jan 7, 2016 at 10:39 AM, Ben Coman <btc at openinworld.com>wrote:
> >>>
> >>>
> >>> On Fri, Jan 8, 2016 at 1:20 AM, Eliot Miranda <eliot.miranda at gmail.com>
> wrote:
> >>> >
> >>> > and here's a version with a better class comment
> >>> >
> >>> > On Thu, Jan 7, 2016 at 9:12 AM, Eliot Miranda <
> eliot.miranda at gmail.com> wrote:
> >>> >>
> >>> >> Hi Denis, Hi Clément,  Hi Frank,
> >>> >>
> >>> >> On Thu, Jan 7, 2016 at 5:34 AM, Clément Bera <
> bera.clement at gmail.com> wrote:
> >>> >>>
> >>> >>> Hello,
> >>> >>>
> >>> >>> Eliot, please, you told me you had the code and Denis is
> interested.
> >>> >>>
> >>> >>> It uses 3 primitives for performance.
> >>> >>
> >>> >>
> >>> >> Forgive the delay.  I thought it proper to ask permission since the
> code was written while I was at Qwaq. I'm attaching the code in a fairly
> raw state, see the attached.  The code is MIT, but copyright 3DICC.
> >>> >>
> >>> >> It is a plugin replacement for Squeak's Mutex, and with a little
> ingenuity could be a replacement for Squeak's Monitor.  It is quicker
> because it uses three new primitives to manage entering a critical section
> and setting the owner, exiting the critical section and releasing the
> owner, and testing if a critical section, entering if the section is
> unowned.  The use of the primitives means fewer block activations and
> ensure: blocks in entering and exiting the critical section, and that's the
> actual cause of the speed-up.
> >>> >>
> >>> >> You can benchmark the code as is.  Here are some results on 32-bit
> Spur, on my 2.2GHz Core i7
> >>> >>
> >>> >> {Mutex new. Monitor new. CriticalSection new} collect:
> >>> >> [:cs| | n |
> >>> >> n := 0.
> >>> >> [cs critical: [n := n + 1]. cs critical: [n := n + 1]. cs critical:
> [n := n + 1]. cs critical: [n := n + 1]. cs critical: [n := n + 1].
> >>> >> cs critical: [n := n - 1]. cs critical: [n := n - 1]. cs critical:
> [n := n - 1]. cs critical: [n := n - 1]. cs critical: [n := n - 1].
> >>> >> n ] bench]
> >>> >>
> >>> >> {Mutex new. Monitor new. CriticalSection new} collect:
> >>> >> [:cs| | n |
> >>> >> n := 0.
> >>> >> cs class name, ' -> ',
> >>> >> [cs critical: [n := n + 1]. cs critical: [n := n + 1]. cs critical:
> [n := n + 1]. cs critical: [n := n + 1]. cs critical: [n := n + 1].
> >>> >> cs critical: [n := n - 1]. cs critical: [n := n - 1]. cs critical:
> [n := n - 1]. cs critical: [n := n - 1]. cs critical: [n := n - 1].
> >>> >> n ] bench]
> >>> >>
> >>> >> #( 'Mutex -> 440,000 per second. 2.27 microseconds per run.'
> >>> >> 'Monitor -> 688,000 per second. 1.45 microseconds per run.'
> >>> >> 'CriticalSection -> 1,110,000 per second. 900 nanoseconds per run.')
> >>> >>
> >>>
> >>> This is great Eliot. Thank you and 3DICC.  After loading the changeset
> >>> into Pharo-50515 (32 bit Spur) I get the following results on my
> >>> laptop i5-2520M @ 2.50GHz
> >>>
> >>> #('Mutex -> 254,047 per second'
> >>>  'Monitor -> 450,442 per second'
> >>>  'CriticalSection -> 683,393 per second')
> >>>
> >>> In a fresh Image "Mutex allInstances basicInspect" lists just two
> mutexes...
> >>> 1. NetNameResolver-->ResolverMutex
> >>> 2. ThreadSafeTranscript-->accessSemaphore
> >>
> >>
> >> I hate myself for getting distracted but I'm finding this is un.  One
> can migrate to the new representation using normal Monticello loads by
> >>
> >> In the first version redefine Mutex and Monitor to subclass LinkedList
> and have their owner/ownerProcess inst var first (actually third after
> firstLink & lastLink), and add the primitives.
> >>
> >> In the next version check that all Mutex and Monitor instanes are
> unowned and then redefine to discard excess inst vars
> >>
> >> Let me test this before committing, and see that all tests are ok.
> >>
> >>>
> >>> cheers -ben
> >>>
> >>> >> Replacement is probably trivial; rename Mutex to OldMutex, rename
> CriticalSection to Mutex, recompile.  But there are lots of mutexes in the
> system and these are potentially owned.  Transforming unowned ones is
> trivial, but transforming owned ones is, I think, impossible.  But at least
> in my system there are no owned mutexes or monitors.
> >>> >>
> >>> >> Frank (or anyone else), would you be interested in creating a
> replacement for Squeak's Monitor based on CriticalSection?
> >>> >>
> >>> >>
> >>> >> Here are the two business methods:
> >>> >> CriticalSection methods for mutual exclusion
> >>> >> critical: aBlock
> >>> >> "Evaluate aBlock protected by the receiver."
> >>> >> ^self primitiveEnterCriticalSection
> >>> >> ifTrue: [aBlock value]
> >>> >> ifFalse: [aBlock ensure: [self primitiveExitCriticalSection]]
> >>> >>
> >>> >> critical: aBlock ifLocked: lockedBlock
> >>> >> "Answer the evaluation of aBlock protected by the receiver.  If it
> is already in a critical
> >>> >> section on behalf of some other process answer the evaluation of
> lockedBlock."
> >>> >> ^self primitiveTestAndSetOwnershipOfCriticalSection
> >>> >> ifNil: [lockedBlock value]
> >>> >> ifNotNil:[:alreadyOwner|
> >>> >> alreadyOwner
> >>> >> ifTrue: [aBlock value]
> >>> >> ifFalse: [aBlock ensure: [self primitiveExitCriticalSection]]]
> >>> >>
> >>> >> and the primitives:
> >>> >> CriticalSection methods for private-primitives
> >>> >> primitiveEnterCriticalSection
> >>> >> "Primitive. The receiver must be unowned or owned by the current
> process to proceed.
> >>> >> Answer if the process is owned by the current process."
> >>> >> <primitive: 186>
> >>> >> self primitiveFailed
> >>> >> "In the spirit of the following"
> >>> >> "[owner ifNil:
> >>> >> [owner := Processor activeProcess.
> >>> >> ^false].
> >>> >>  owner = Processor activeProcess ifTrue:
> >>> >> [^true].
> >>> >>  self addLast: Processor activeProcess.
> >>> >>  Processor activeProcess suspend] valueUnpreemptively"
> >>> >>
> >>> >> primitiveExitCriticalSection
> >>> >> "Primitive. Set te receiver to unowned and if any processes are
> waiting on
> >>> >> the receiver then proceed the first one, indicating that the
> receiver is unowned."
> >>> >> <primitive: 185>
> >>> >> self primitiveFailed
> >>> >> "In the spirit of the following"
> >>> >> "[owner := nil.
> >>> >>  self isEmpty ifFalse:
> >>> >> [process := self removeFirst.
> >>> >> process resume]] valueUnpreemptively"
> >>> >>
> >>> >> primitiveTestAndSetOwnershipOfCriticalSection
> >>> >> "Primitive. Attempt to set the ownership of the receiver.
> >>> >> If the receiver is unowned set its owningProcess to the
> >>> >> activeProcess and answer false.  If the receiver is owned
> >>> >> by the activeProcess answer true.  If the receiver is owned
> >>> >> by some other process answer nil."
> >>> >> <primitive: 187>
> >>> >> self primitiveFail
> >>> >> "In the spirit of the following"
> >>> >> "[owner ifNil:
> >>> >> [owningProcess := Processor activeProcess.
> >>> >> ^false].
> >>> >>  owner = Processor activeProcess ifTrue: [^true].
> >>> >>  ^nil] valueUnpreemptively"
> >>> >>
> >>> >>> 2016-01-07 13:24 GMT+01:00 Denis Kudriashov <dionisiydk at gmail.com
> >:
> >>> >>>>
> >>> >>>>
> >>> >>>> Hello.
> >>> >>>>
> >>> >>>> I hear about new Monitor implementation based on new primitives.
> >>> >>>> Where to get it?
>



-- 
_,,,^..^,,,_
best, Eliot
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.squeakfoundation.org/pipermail/vm-dev/attachments/20160108/3c27d853/attachment-0001.htm


More information about the Vm-dev mailing list