Where to get Monitor implementation based on primitives?

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
47 messages Options
123
Reply | Threaded
Open this post in threaded view
|

Where to get Monitor implementation based on primitives?

Denis Kudriashov
 
Hello.

I hear about new Monitor implementation based on new primitives.
Where to get it?
Reply | Threaded
Open this post in threaded view
|

Re: Where to get Monitor implementation based on primitives?

Clément Béra
 
Hello,

Eliot, please, you told me you had the code and Denis is interested.

It uses 3 primitives for performance.

2016-01-07 13:24 GMT+01:00 Denis Kudriashov <[hidden email]>:
 
Hello.

I hear about new Monitor implementation based on new primitives.
Where to get it?


Reply | Threaded
Open this post in threaded view
|

Re: Where to get Monitor implementation based on primitives?

Ben Coman
In reply to this post by Denis Kudriashov
 
On Thu, Jan 7, 2016 at 8:24 PM, Denis Kudriashov <[hidden email]> wrote:
>
> Hello.
>
> I hear about new Monitor implementation based on new primitives.
> Where to get it?
>

A while ago Eliot mentioned some mutex primitives.  I didn't follow up
then, but maybe now is the right time to try these.  I can't find the
post, but looking just now I found some likely candidates at [1]:
- 185 primitiveExitCriticalSection
- 186 primitiveEnterCriticalSection
- 187 primitiveTestAndSetOwnershipOfCriticalSection

However I can't find any image side code in Squeak or Newspeak.

[1] https://github.com/pharo-project/pharo-vm/search?utf8=%E2%9C%93&q=criticalsection

cheers -ben
Reply | Threaded
Open this post in threaded view
|

Re: Where to get Monitor implementation based on primitives?

Eliot Miranda-2
In reply to this post by Clément Béra
 
Hi Denis, Hi Clément,  Hi Frank,

On Thu, Jan 7, 2016 at 5:34 AM, Clément Bera <[hidden email]> 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.')

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 <[hidden email]>:
 
Hello.

I hear about new Monitor implementation based on new primitives.
Where to get it?

_,,,^..^,,,_
best, Eliot

CriticalSection.st (4K) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: Where to get Monitor implementation based on primitives?

Eliot Miranda-2
 
and here's a version with a better class comment

On Thu, Jan 7, 2016 at 9:12 AM, Eliot Miranda <[hidden email]> wrote:
Hi Denis, Hi Clément,  Hi Frank,

On Thu, Jan 7, 2016 at 5:34 AM, Clément Bera <[hidden email]> 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.')

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 <[hidden email]>:
 
Hello.

I hear about new Monitor implementation based on new primitives.
Where to get it?

_,,,^..^,,,_
best, Eliot



--
_,,,^..^,,,_
best, Eliot

CriticalSection.st (4K) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: Where to get Monitor implementation based on primitives?

Ben Coman

On Fri, Jan 8, 2016 at 1:20 AM, Eliot Miranda <[hidden email]> wrote:

>
> and here's a version with a better class comment
>
> On Thu, Jan 7, 2016 at 9:12 AM, Eliot Miranda <[hidden email]> wrote:
>>
>> Hi Denis, Hi Clément,  Hi Frank,
>>
>> On Thu, Jan 7, 2016 at 5:34 AM, Clément Bera <[hidden email]> 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

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 <[hidden email]>:
>>>>
>>>>
>>>> Hello.
>>>>
>>>> I hear about new Monitor implementation based on new primitives.
>>>> Where to get it?
>>
>>
>> _,,,^..^,,,_
>> best, Eliot
>
>
>
>
> --
> _,,,^..^,,,_
> best, Eliot
>
Reply | Threaded
Open this post in threaded view
|

Re: Where to get Monitor implementation based on primitives?

Eliot Miranda-2
 
Hi Ben,

On Thu, Jan 7, 2016 at 10:39 AM, Ben Coman <[hidden email]> wrote:

On Fri, Jan 8, 2016 at 1:20 AM, Eliot Miranda <[hidden email]> wrote:
>
> and here's a version with a better class comment
>
> On Thu, Jan 7, 2016 at 9:12 AM, Eliot Miranda <[hidden email]> wrote:
>>
>> Hi Denis, Hi Clément,  Hi Frank,
>>
>> On Thu, Jan 7, 2016 at 5:34 AM, Clément Bera <[hidden email]> 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 <[hidden email]>:
>>>>
>>>>
>>>> Hello.
>>>>
>>>> I hear about new Monitor implementation based on new primitives.
>>>> Where to get it?
>>
>>
>> _,,,^..^,,,_
>> best, Eliot
>
>
>
>
> --
> _,,,^..^,,,_
> best, Eliot
>



--
_,,,^..^,,,_
best, Eliot
Reply | Threaded
Open this post in threaded view
|

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

Ben Coman

On Fri, Jan 8, 2016 at 2:51 AM, Eliot Miranda <[hidden email]> wrote:

> Hi Ben,
>
> On Thu, Jan 7, 2016 at 10:39 AM, Ben Coman <[hidden email]> wrote:
>>
>>
>> On Fri, Jan 8, 2016 at 1:20 AM, Eliot Miranda <[hidden email]>
>> wrote:
>> >
>> > and here's a version with a better class comment
>> >
>> > On Thu, Jan 7, 2016 at 9:12 AM, Eliot Miranda <[hidden email]>
>> > wrote:
>> >>
>> >> Hi Denis, Hi Clément,  Hi Frank,
>> >>
>> >> On Thu, Jan 7, 2016 at 5:34 AM, Clément Bera <[hidden email]>
>> >> 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.

Should Mutex and Monitor both directly subclass LinkedList and
duplicate the primitives in each?

Or should they both subclass CriticalSection which subclasses
LinkedList so the primitives are only defined once?

What effect would using the primitives from the superclass have on
performance? If any, I'd vote to optimise for duplication rather than
"nice" design, but our comments should document this.

cheers -ben
Reply | Threaded
Open this post in threaded view
|

Re: Where to get Monitor implementation based on primitives?

stephane ducasse-2
In reply to this post by Eliot Miranda-2
 
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.

Stef




Hi Ben,

On Thu, Jan 7, 2016 at 10:39 AM, Ben Coman <[hidden email]>wrote:

On Fri, Jan 8, 2016 at 1:20 AM, Eliot Miranda <[hidden email]> wrote:

>
> and here's a version with a better class comment
>
> On Thu, Jan 7, 2016 at 9:12 AM, Eliot Miranda <[hidden email]> wrote:
>>
>> Hi Denis, Hi Clément,  Hi Frank,
>>
>> On Thu, Jan 7, 2016 at 5:34 AM, Clément Bera <[hidden email]> 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 <[hidden email]>:
>>>>
>>>>
>>>> Hello.
>>>>
>>>> I hear about new Monitor implementation based on new primitives.
>>>> Where to get it?
>>
>>
>> _,,,^..^,,,_
>> best, Eliot
>
>
>
>
> --
> _,,,^..^,,,_
> best, Eliot
>



-- 
_,,,^..^,,,_
best, Eliot

Reply | Threaded
Open this post in threaded view
|

Re: Where to get Monitor implementation based on primitives?

stepharo
In reply to this post by Eliot Miranda-2
 
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.

Stef



Reply | Threaded
Open this post in threaded view
|

Re: Where to get Monitor implementation based on primitives?

Denis Kudriashov
 

2016-01-08 13:06 GMT+01:00 stepharo <[hidden email]>:
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.


Eliot code is used VM primitives for critical sections. 

Reply | Threaded
Open this post in threaded view
|

Re: Where to get Monitor implementation based on primitives?

Ben Coman
In reply to this post by stephane ducasse-2

On Fri, Jan 8, 2016 at 5:42 PM, stephane ducasse
<[hidden email]> 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].

4. Its faster (for me, the least argument given that.

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 <[hidden email]>wrote:
>>
>>
>> On Fri, Jan 8, 2016 at 1:20 AM, Eliot Miranda <[hidden email]> wrote:
>> >
>> > and here's a version with a better class comment
>> >
>> > On Thu, Jan 7, 2016 at 9:12 AM, Eliot Miranda <[hidden email]> wrote:
>> >>
>> >> Hi Denis, Hi Clément,  Hi Frank,
>> >>
>> >> On Thu, Jan 7, 2016 at 5:34 AM, Clément Bera <[hidden email]> 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 <[hidden email]>:
>> >>>>
>> >>>>
>> >>>> Hello.
>> >>>>
>> >>>> I hear about new Monitor implementation based on new primitives.
>> >>>> Where to get it?
Reply | Threaded
Open this post in threaded view
|

Re: Where to get Monitor implementation based on primitives?

Denis Kudriashov
In reply to this post by Denis Kudriashov
 

2016-01-08 13:36 GMT+01:00 Denis Kudriashov <[hidden email]>:
2016-01-08 13:06 GMT+01:00 stepharo <[hidden email]>:
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.


Eliot code is used VM primitives for critical sections. 

And it seams that without primitive it is impossible to implement #critical:ifLocked:. Current implementation is not guard against caller blocking
Reply | Threaded
Open this post in threaded view
|

Re: Where to get Monitor implementation based on primitives?

Denis Kudriashov
In reply to this post by Ben Coman
 

2016-01-08 14:39 GMT+01:00 Ben Coman <[hidden email]>:
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.)

I realize that my proposals was wrong because it is based on extra message send which can be interrupted. So I think about different solution.
Current solution which silently skip ensure block not suitable for case of ReadWriteLock where there are two semaphores inside same critical method
Reply | Threaded
Open this post in threaded view
|

Re: Where to get Monitor implementation based on primitives?

Ben Coman
In reply to this post by Ben Coman

On Fri, Jan 8, 2016 at 9:39 PM, Ben Coman <[hidden email]> wrote:

> On Fri, Jan 8, 2016 at 5:42 PM, stephane ducasse
> <[hidden email]> 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...

  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 ;)

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 <[hidden email]>wrote:
>>>
>>>
>>> On Fri, Jan 8, 2016 at 1:20 AM, Eliot Miranda <[hidden email]> wrote:
>>> >
>>> > and here's a version with a better class comment
>>> >
>>> > On Thu, Jan 7, 2016 at 9:12 AM, Eliot Miranda <[hidden email]> wrote:
>>> >>
>>> >> Hi Denis, Hi Clément,  Hi Frank,
>>> >>
>>> >> On Thu, Jan 7, 2016 at 5:34 AM, Clément Bera <[hidden email]> 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 <[hidden email]>:
>>> >>>>
>>> >>>>
>>> >>>> Hello.
>>> >>>>
>>> >>>> I hear about new Monitor implementation based on new primitives.
>>> >>>> Where to get it?
Reply | Threaded
Open this post in threaded view
|

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

Eliot Miranda-2
In reply to this post by Ben Coman
 
Hi Ben,

On Thu, Jan 7, 2016 at 4:40 PM, Ben Coman <[hidden email]> wrote:
On Fri, Jan 8, 2016 at 2:51 AM, Eliot Miranda <[hidden email]> wrote:
> Hi Ben,
>
> On Thu, Jan 7, 2016 at 10:39 AM, Ben Coman <[hidden email]> wrote:
>>
>>
>> On Fri, Jan 8, 2016 at 1:20 AM, Eliot Miranda <[hidden email]>
>> wrote:
>> >
>> > and here's a version with a better class comment
>> >
>> > On Thu, Jan 7, 2016 at 9:12 AM, Eliot Miranda <[hidden email]>
>> > wrote:
>> >>
>> >> Hi Denis, Hi Clément,  Hi Frank,
>> >>
>> >> On Thu, Jan 7, 2016 at 5:34 AM, Clément Bera <[hidden email]>
>> >> 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.

Should Mutex and Monitor both directly subclass LinkedList and
duplicate the primitives in each?

Or should they both subclass CriticalSection which subclasses
LinkedList so the primitives are only defined once?

That's a good idea.  Feel free to change the code, but test that the Monticello load handles this case properly first :-).  Actually, given that the default state of all the Mutex and Monitor instances in the image is unowned (owner process is nil) then it'll just work anyway.  If we do that, we must make sure to include the ICC copyright in CriticalSection's class comment, and can eliminate it from the primitives.

What effect would using the primitives from the superclass have on
performance? If any, I'd vote to optimise for duplication rather than
"nice" design, but our comments should document this.

Likely in the noise.  The inline cacheing machinery in the VM is far cheaper than the real overheads here which are in block creation, process switch, interpreter primitive invocation.
 

cheers -ben




--
_,,,^..^,,,_
best, Eliot
Reply | Threaded
Open this post in threaded view
|

Re: Where to get Monitor implementation based on primitives?

Eliot Miranda-2
In reply to this post by Ben Coman
 
Hi Ben,

On Fri, Jan 8, 2016 at 7:25 AM, Ben Coman <[hidden email]> wrote:

On Fri, Jan 8, 2016 at 9:39 PM, Ben Coman <[hidden email]> wrote:
> On Fri, Jan 8, 2016 at 5:42 PM, stephane ducasse
> <[hidden email]> 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 <[hidden email]>wrote:
>>>
>>>
>>> On Fri, Jan 8, 2016 at 1:20 AM, Eliot Miranda <[hidden email]> wrote:
>>> >
>>> > and here's a version with a better class comment
>>> >
>>> > On Thu, Jan 7, 2016 at 9:12 AM, Eliot Miranda <[hidden email]> wrote:
>>> >>
>>> >> Hi Denis, Hi Clément,  Hi Frank,
>>> >>
>>> >> On Thu, Jan 7, 2016 at 5:34 AM, Clément Bera <[hidden email]> 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 <[hidden email]>:
>>> >>>>
>>> >>>>
>>> >>>> Hello.
>>> >>>>
>>> >>>> I hear about new Monitor implementation based on new primitives.
>>> >>>> Where to get it?



--
_,,,^..^,,,_
best, Eliot
Reply | Threaded
Open this post in threaded view
|

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

Denis Kudriashov
In reply to this post by Eliot Miranda-2
 
Now I implement pragma approach to set up local variable during termination process. So I can write such methods:

critical: aBlock
"Evaluate aBlock protected by the receiver."
|  lockAcquired |
<lockAt: #lock trackStateAt: 1>
lockAcquired := false. 
^[
lockAcquired := true.
lockAcquired := lock wait.
aBlock value
] ensure: [lockAcquired ifTrue: [lock signal]].

And Process>>terminate detects waiting on such methods and push false to variable lockAcquired (which is 1 temp here).
This approach allow me to use multiple "locks" (semaphores or whatever) in single method which I need for ReadWriteLock.


2016-01-08 18:31 GMT+01:00 Eliot Miranda <[hidden email]>:
Hi Ben,

On Thu, Jan 7, 2016 at 4:40 PM, Ben Coman <[hidden email]> wrote:
On Fri, Jan 8, 2016 at 2:51 AM, Eliot Miranda <[hidden email]> wrote:
> Hi Ben,
>
> On Thu, Jan 7, 2016 at 10:39 AM, Ben Coman <[hidden email]> wrote:
>>
>>
>> On Fri, Jan 8, 2016 at 1:20 AM, Eliot Miranda <[hidden email]>
>> wrote:
>> >
>> > and here's a version with a better class comment
>> >
>> > On Thu, Jan 7, 2016 at 9:12 AM, Eliot Miranda <[hidden email]>
>> > wrote:
>> >>
>> >> Hi Denis, Hi Clément,  Hi Frank,
>> >>
>> >> On Thu, Jan 7, 2016 at 5:34 AM, Clément Bera <[hidden email]>
>> >> 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.

Should Mutex and Monitor both directly subclass LinkedList and
duplicate the primitives in each?

Or should they both subclass CriticalSection which subclasses
LinkedList so the primitives are only defined once?

That's a good idea.  Feel free to change the code, but test that the Monticello load handles this case properly first :-).  Actually, given that the default state of all the Mutex and Monitor instances in the image is unowned (owner process is nil) then it'll just work anyway.  If we do that, we must make sure to include the ICC copyright in CriticalSection's class comment, and can eliminate it from the primitives.

What effect would using the primitives from the superclass have on
performance? If any, I'd vote to optimise for duplication rather than
"nice" design, but our comments should document this.

Likely in the noise.  The inline cacheing machinery in the VM is far cheaper than the real overheads here which are in block creation, process switch, interpreter primitive invocation.
 

cheers -ben




--
_,,,^..^,,,_
best, Eliot




Reply | Threaded
Open this post in threaded view
|

Re: Where to get Monitor implementation based on primitives?

stephane ducasse-2
In reply to this post by Ben Coman

Hi ben

thanks for your comments.
I feel better.

Stef

On 08 Jan 2016, at 14:39, Ben Coman <[hidden email]> wrote:

>
> On Fri, Jan 8, 2016 at 5:42 PM, stephane ducasse
> <[hidden email]> 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].
>
> 4. Its faster (for me, the least argument given that.
>
> 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 <[hidden email]>wrote:
>>>
>>>
>>> On Fri, Jan 8, 2016 at 1:20 AM, Eliot Miranda <[hidden email]> wrote:
>>>>
>>>> and here's a version with a better class comment
>>>>
>>>> On Thu, Jan 7, 2016 at 9:12 AM, Eliot Miranda <[hidden email]> wrote:
>>>>>
>>>>> Hi Denis, Hi Clément,  Hi Frank,
>>>>>
>>>>> On Thu, Jan 7, 2016 at 5:34 AM, Clément Bera <[hidden email]> 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 <[hidden email]>:
>>>>>>>
>>>>>>>
>>>>>>> Hello.
>>>>>>>
>>>>>>> I hear about new Monitor implementation based on new primitives.
>>>>>>> Where to get it?

Reply | Threaded
Open this post in threaded view
|

Re: Where to get Monitor implementation based on primitives?

Henrik Sperre Johansen
In reply to this post by Ben Coman
 

On 08 Jan 2016, at 4:25 , Ben Coman <[hidden email]> wrote:


On Fri, Jan 8, 2016 at 9:39 PM, Ben Coman <[hidden email]> wrote:
On Fri, Jan 8, 2016 at 5:42 PM, stephane ducasse
<[hidden email]> 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...

 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 ;)

cheers -ben

Won't work, there's no guarantee thread has actually ran and signalConsumed been assigned the primitive result after Semaphore resumed the waiting thread, before  a higher priority thread runs and terminates it. (which is exactly the case handled by special code in #terminate)

Cheers,
Henry

signature.asc (859 bytes) Download Attachment
123