[squeak-dev] Interaction between objects which can be waited on, and their clients(waiters)

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

[squeak-dev] Interaction between objects which can be waited on, and their clients(waiters)

Igor Stasenko
Hello list,

I stared @ DelayWaitTimeout class, and thinking, how to implement it
in new scheduler..
First i thought , just make it quickly and forget about it, but one
thing which don't makes me feel good about it, that
DelayWaitTimeout is a perfect example, when you need to stop a Process
until one of the multiple events occurs - in case of DelayWaitTimeout
its either semaphore signaled or delay expired.

So, i thinking, what if instead of implementing a DelayWaitTimeout ,
which works only for 2 objects - delay & semaphore, implement a more
generic class , which would allow us to wait for any number of objects
and blocks process until one of them is signaled. Or, blocks process
until all of them signaled.
There's nothing new, in fact, in Windoze there an API function, called
WaitForMultipleObjects, which does exactly what i describing - it
could wait on multiple objects, releasing a waiter once a single
object signaled, or release waiter only after all objects signaled.

Here is my thoughts, how to implement that.
First, we need to allow Delay & Semaphore to work with abstract waiter
object (not just Process).
A proposed protocol between object which acts as one who can be waited
on, and object who acts as waiter is following:

To start waiting, we sending a #waitForSignalBy: waiter
semaphore waitForSignalBy: waiter
or
delay waitForSignalBy: waiter

then, default #wait could be implemented, in both cases, simply as:

^ self waitForSignalBy: Processor activeProcess.

now, after issuing #waitForSignalBy: we have the following possible cases:
- given object is already signaled (as a semaphore with excess
signals), in this case we immediately sending #waitComplete: to waiter

- given object is scheduled for being signaled later , in this case we
sending #startWaitingOn: to waiter.

- now after #startWaitingOn: issued and once a semaphore/delay
signaled we sending #waitComplete: to waiter.

- in case if semaphore/delay abandoned, we sending #waitAbandoned: to
waiter. This happens, for instance, when we terminating a process who
currently waits on semaphore.

(If you have a better idea how to name these messages, please make a guess)

Now, by having such protocol implemented for Delay, Semaphore &
Process, we now can easily introduce another class which could allow
us to wait for multiple events.

Again, i'm not sure how properly call it..
Just want to illustrate the usage:

semaphore := Semaphore new.
delay := Delay forMilliseconds: 100.

signaler := (MultipleWait on: { semaphore. delay }) waitForOne.  "wait
until one of the listed objects get signaled"
or:
result := (MultipleWait on: { semaphore. delay }) waitForAll. "wait
until all of the listed objects get signaled"

by default, MultipleWait>>waitForSignalBy: is releasing waiter once
one of the listed objects signaled (as in waitForOne case).
Not sure about return result of #waitForAll, IMO , it could be just a
boolean value - return true, if all objects signaled, or false in case
if waiting abandoned.

Now, of course, since  MultipleWait acts as a waiter and as an object
who can be waited on, we can allow nesting:

mwait1 := MultipleWait on: { sema1. sema2 }.
(MultipleWait on: { mwait1. delay }) waitForOne.

and finally, as you may guess , a Semaphore>>waitTimeoutMSecs: now can
be implemented as:

waitTimeoutMSecs: anInteger
        "Wait on this semaphore for up to the given number of milliseconds,
then timeout.
        Return true if the deadline expired, false otherwise."
        | delay |

       delay := Delay forMilliseconds: (anInteger max: 0).
       ^ delay == (MultipleWait on: { self. delay }) waitForOne.


--
Best regards,
Igor Stasenko AKA sig.

Reply | Threaded
Open this post in threaded view
|

[squeak-dev] Re: Interaction between objects which can be waited on, and their clients(waiters)

Andreas.Raab
Hi Igor -

How is what you are proposing fundamentally different from a solution
that simply creates a watcher process for each event source and then
combines those properly? I.e.,

Object subclass: #MultiWait
   instanceVariableNames: 'eventSemas'
   ...

waitForAll
   "waits for signals from all event sources"
   eventSemas do:[:sema| sema wait].

waitForOne
   "Waits for signals from at least one event source.
    Returns all the semaphores that have been signaled."
   waitSema := Semaphore new.
   processes := eventSemas do:[:sema| [sema wait. waitSema signal] fork].
   waitSema wait. "or waitTimeoutMSecs:"
   signaled := Set new.
   eventSemas with: processes do:[:list :proc|
     (proc suspend == list) ifTrue:[signaled add: list].
     proc terminate.
   ].
   ^signaled

DelayWaitTimeout is an optimization that avoids the need for additional
processes - when you use #waitTimeoutMSecs: a *lot* (say, because you're
running a server with heavy network activity) the additional overhead
incurred by the additional processes (and process switches) gets
relevant in a hurry. But other than that, the above is a straightforward
solution that will work with anything that supports the #wait protocol.

Cheers,
   - Andreas

Igor Stasenko wrote:

> Hello list,
>
> I stared @ DelayWaitTimeout class, and thinking, how to implement it
> in new scheduler..
> First i thought , just make it quickly and forget about it, but one
> thing which don't makes me feel good about it, that
> DelayWaitTimeout is a perfect example, when you need to stop a Process
> until one of the multiple events occurs - in case of DelayWaitTimeout
> its either semaphore signaled or delay expired.
>
> So, i thinking, what if instead of implementing a DelayWaitTimeout ,
> which works only for 2 objects - delay & semaphore, implement a more
> generic class , which would allow us to wait for any number of objects
> and blocks process until one of them is signaled. Or, blocks process
> until all of them signaled.
> There's nothing new, in fact, in Windoze there an API function, called
> WaitForMultipleObjects, which does exactly what i describing - it
> could wait on multiple objects, releasing a waiter once a single
> object signaled, or release waiter only after all objects signaled.
>
> Here is my thoughts, how to implement that.
> First, we need to allow Delay & Semaphore to work with abstract waiter
> object (not just Process).
> A proposed protocol between object which acts as one who can be waited
> on, and object who acts as waiter is following:
>
> To start waiting, we sending a #waitForSignalBy: waiter
> semaphore waitForSignalBy: waiter
> or
> delay waitForSignalBy: waiter
>
> then, default #wait could be implemented, in both cases, simply as:
>
> ^ self waitForSignalBy: Processor activeProcess.
>
> now, after issuing #waitForSignalBy: we have the following possible cases:
> - given object is already signaled (as a semaphore with excess
> signals), in this case we immediately sending #waitComplete: to waiter
>
> - given object is scheduled for being signaled later , in this case we
> sending #startWaitingOn: to waiter.
>
> - now after #startWaitingOn: issued and once a semaphore/delay
> signaled we sending #waitComplete: to waiter.
>
> - in case if semaphore/delay abandoned, we sending #waitAbandoned: to
> waiter. This happens, for instance, when we terminating a process who
> currently waits on semaphore.
>
> (If you have a better idea how to name these messages, please make a guess)
>
> Now, by having such protocol implemented for Delay, Semaphore &
> Process, we now can easily introduce another class which could allow
> us to wait for multiple events.
>
> Again, i'm not sure how properly call it..
> Just want to illustrate the usage:
>
> semaphore := Semaphore new.
> delay := Delay forMilliseconds: 100.
>
> signaler := (MultipleWait on: { semaphore. delay }) waitForOne.  "wait
> until one of the listed objects get signaled"
> or:
> result := (MultipleWait on: { semaphore. delay }) waitForAll. "wait
> until all of the listed objects get signaled"
>
> by default, MultipleWait>>waitForSignalBy: is releasing waiter once
> one of the listed objects signaled (as in waitForOne case).
> Not sure about return result of #waitForAll, IMO , it could be just a
> boolean value - return true, if all objects signaled, or false in case
> if waiting abandoned.
>
> Now, of course, since  MultipleWait acts as a waiter and as an object
> who can be waited on, we can allow nesting:
>
> mwait1 := MultipleWait on: { sema1. sema2 }.
> (MultipleWait on: { mwait1. delay }) waitForOne.
>
> and finally, as you may guess , a Semaphore>>waitTimeoutMSecs: now can
> be implemented as:
>
> waitTimeoutMSecs: anInteger
> "Wait on this semaphore for up to the given number of milliseconds,
> then timeout.
> Return true if the deadline expired, false otherwise."
> | delay |
>
>        delay := Delay forMilliseconds: (anInteger max: 0).
>        ^ delay == (MultipleWait on: { self. delay }) waitForOne.
>
>


Reply | Threaded
Open this post in threaded view
|

[squeak-dev] Re: Interaction between objects which can be waited on, and their clients(waiters)

Andreas.Raab
Just for fun, find an attached a version of MultiWait that works just
like described earlier. If you run the benchmarks on MultiWait you'll
find that DelayWaitTimeout is about 5x faster when compared with the
naive MultiWait equivalent.

Cheers,
   - Andreas

Andreas Raab wrote:

> Hi Igor -
>
> How is what you are proposing fundamentally different from a solution
> that simply creates a watcher process for each event source and then
> combines those properly? I.e.,
>
> Object subclass: #MultiWait
>   instanceVariableNames: 'eventSemas'
>   ...
>
> waitForAll
>   "waits for signals from all event sources"
>   eventSemas do:[:sema| sema wait].
>
> waitForOne
>   "Waits for signals from at least one event source.
>    Returns all the semaphores that have been signaled."
>   waitSema := Semaphore new.
>   processes := eventSemas do:[:sema| [sema wait. waitSema signal] fork].
>   waitSema wait. "or waitTimeoutMSecs:"
>   signaled := Set new.
>   eventSemas with: processes do:[:list :proc|
>     (proc suspend == list) ifTrue:[signaled add: list].
>     proc terminate.
>   ].
>   ^signaled
>
> DelayWaitTimeout is an optimization that avoids the need for additional
> processes - when you use #waitTimeoutMSecs: a *lot* (say, because you're
> running a server with heavy network activity) the additional overhead
> incurred by the additional processes (and process switches) gets
> relevant in a hurry. But other than that, the above is a straightforward
> solution that will work with anything that supports the #wait protocol.
>
> Cheers,
>   - Andreas
>
> Igor Stasenko wrote:
>> Hello list,
>>
>> I stared @ DelayWaitTimeout class, and thinking, how to implement it
>> in new scheduler..
>> First i thought , just make it quickly and forget about it, but one
>> thing which don't makes me feel good about it, that
>> DelayWaitTimeout is a perfect example, when you need to stop a Process
>> until one of the multiple events occurs - in case of DelayWaitTimeout
>> its either semaphore signaled or delay expired.
>>
>> So, i thinking, what if instead of implementing a DelayWaitTimeout ,
>> which works only for 2 objects - delay & semaphore, implement a more
>> generic class , which would allow us to wait for any number of objects
>> and blocks process until one of them is signaled. Or, blocks process
>> until all of them signaled.
>> There's nothing new, in fact, in Windoze there an API function, called
>> WaitForMultipleObjects, which does exactly what i describing - it
>> could wait on multiple objects, releasing a waiter once a single
>> object signaled, or release waiter only after all objects signaled.
>>
>> Here is my thoughts, how to implement that.
>> First, we need to allow Delay & Semaphore to work with abstract waiter
>> object (not just Process).
>> A proposed protocol between object which acts as one who can be waited
>> on, and object who acts as waiter is following:
>>
>> To start waiting, we sending a #waitForSignalBy: waiter
>> semaphore waitForSignalBy: waiter
>> or
>> delay waitForSignalBy: waiter
>>
>> then, default #wait could be implemented, in both cases, simply as:
>>
>> ^ self waitForSignalBy: Processor activeProcess.
>>
>> now, after issuing #waitForSignalBy: we have the following possible
>> cases:
>> - given object is already signaled (as a semaphore with excess
>> signals), in this case we immediately sending #waitComplete: to waiter
>>
>> - given object is scheduled for being signaled later , in this case we
>> sending #startWaitingOn: to waiter.
>>
>> - now after #startWaitingOn: issued and once a semaphore/delay
>> signaled we sending #waitComplete: to waiter.
>>
>> - in case if semaphore/delay abandoned, we sending #waitAbandoned: to
>> waiter. This happens, for instance, when we terminating a process who
>> currently waits on semaphore.
>>
>> (If you have a better idea how to name these messages, please make a
>> guess)
>>
>> Now, by having such protocol implemented for Delay, Semaphore &
>> Process, we now can easily introduce another class which could allow
>> us to wait for multiple events.
>>
>> Again, i'm not sure how properly call it..
>> Just want to illustrate the usage:
>>
>> semaphore := Semaphore new.
>> delay := Delay forMilliseconds: 100.
>>
>> signaler := (MultipleWait on: { semaphore. delay }) waitForOne.  "wait
>> until one of the listed objects get signaled"
>> or:
>> result := (MultipleWait on: { semaphore. delay }) waitForAll. "wait
>> until all of the listed objects get signaled"
>>
>> by default, MultipleWait>>waitForSignalBy: is releasing waiter once
>> one of the listed objects signaled (as in waitForOne case).
>> Not sure about return result of #waitForAll, IMO , it could be just a
>> boolean value - return true, if all objects signaled, or false in case
>> if waiting abandoned.
>>
>> Now, of course, since  MultipleWait acts as a waiter and as an object
>> who can be waited on, we can allow nesting:
>>
>> mwait1 := MultipleWait on: { sema1. sema2 }.
>> (MultipleWait on: { mwait1. delay }) waitForOne.
>>
>> and finally, as you may guess , a Semaphore>>waitTimeoutMSecs: now can
>> be implemented as:
>>
>> waitTimeoutMSecs: anInteger
>>     "Wait on this semaphore for up to the given number of milliseconds,
>> then timeout.
>>     Return true if the deadline expired, false otherwise."
>>     | delay |
>>
>>        delay := Delay forMilliseconds: (anInteger max: 0).
>>        ^ delay == (MultipleWait on: { self. delay }) waitForOne.
>>
>>
>
>
>



MultiWait.1.cs (1K) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: [squeak-dev] Re: Interaction between objects which can be waited on, and their clients(waiters)

Igor Stasenko
In reply to this post by Andreas.Raab
2009/5/5 Andreas Raab <[hidden email]>:

> Hi Igor -
>
> How is what you are proposing fundamentally different from a solution that
> simply creates a watcher process for each event source and then combines
> those properly? I.e.,
>
> Object subclass: #MultiWait
>  instanceVariableNames: 'eventSemas'
>  ...
>
> waitForAll
>  "waits for signals from all event sources"
>  eventSemas do:[:sema| sema wait].
>
> waitForOne
>  "Waits for signals from at least one event source.
>   Returns all the semaphores that have been signaled."
>  waitSema := Semaphore new.
>  processes := eventSemas do:[:sema| [sema wait. waitSema signal] fork].
>  waitSema wait. "or waitTimeoutMSecs:"
>  signaled := Set new.
>  eventSemas with: processes do:[:list :proc|
>    (proc suspend == list) ifTrue:[signaled add: list].
>    proc terminate.
>  ].
>  ^signaled
>

the fundamental difference that by adding the protocol
(waitForSignalBy:/waitComplete:) i can put any waiter to the
semaphore, instead of just process.
This means, that i can schedule a waiting for a signal from
semaphore/delay without stopping any process.
If this doesn't have any potential use cases, then let just abandon
this idea alltogether.

> DelayWaitTimeout is an optimization that avoids the need for additional
> processes - when you use #waitTimeoutMSecs: a *lot* (say, because you're
> running a server with heavy network activity) the additional overhead
> incurred by the additional processes (and process switches) gets relevant in
> a hurry. But other than that, the above is a straightforward solution that
> will work with anything that supports the #wait protocol.
>

Surely, this is an optimization. But in my current implementation
(which is not yet released due the questions above) , a Delay holds a
reference to Process, not Semaphore. So comparing to old scheduler:
a 'Delay waitForMilliseconds:  xxx '

will create 1 object (Delay) instead of two (Delay + Semaphore).

Which IMO is more optimal :)

And new DelayWaitTimeout will have same references - semaphore +
process, so there will be no difference , except that it adds
'semaphore' ivar instead of 'process' ivar.
So, everythign is same for this case, except that initially, a new
Delay don't needs to create a Semaphore instance.

> Cheers,
>  - Andreas
>
> Igor Stasenko wrote:
>>
>> Hello list,
>>
>> I stared @ DelayWaitTimeout class, and thinking, how to implement it
>> in new scheduler..
>> First i thought , just make it quickly and forget about it, but one
>> thing which don't makes me feel good about it, that
>> DelayWaitTimeout is a perfect example, when you need to stop a Process
>> until one of the multiple events occurs - in case of DelayWaitTimeout
>> its either semaphore signaled or delay expired.
>>
>> So, i thinking, what if instead of implementing a DelayWaitTimeout ,
>> which works only for 2 objects - delay & semaphore, implement a more
>> generic class , which would allow us to wait for any number of objects
>> and blocks process until one of them is signaled. Or, blocks process
>> until all of them signaled.
>> There's nothing new, in fact, in Windoze there an API function, called
>> WaitForMultipleObjects, which does exactly what i describing - it
>> could wait on multiple objects, releasing a waiter once a single
>> object signaled, or release waiter only after all objects signaled.
>>
>> Here is my thoughts, how to implement that.
>> First, we need to allow Delay & Semaphore to work with abstract waiter
>> object (not just Process).
>> A proposed protocol between object which acts as one who can be waited
>> on, and object who acts as waiter is following:
>>
>> To start waiting, we sending a #waitForSignalBy: waiter
>> semaphore waitForSignalBy: waiter
>> or
>> delay waitForSignalBy: waiter
>>
>> then, default #wait could be implemented, in both cases, simply as:
>>
>> ^ self waitForSignalBy: Processor activeProcess.
>>
>> now, after issuing #waitForSignalBy: we have the following possible cases:
>> - given object is already signaled (as a semaphore with excess
>> signals), in this case we immediately sending #waitComplete: to waiter
>>
>> - given object is scheduled for being signaled later , in this case we
>> sending #startWaitingOn: to waiter.
>>
>> - now after #startWaitingOn: issued and once a semaphore/delay
>> signaled we sending #waitComplete: to waiter.
>>
>> - in case if semaphore/delay abandoned, we sending #waitAbandoned: to
>> waiter. This happens, for instance, when we terminating a process who
>> currently waits on semaphore.
>>
>> (If you have a better idea how to name these messages, please make a
>> guess)
>>
>> Now, by having such protocol implemented for Delay, Semaphore &
>> Process, we now can easily introduce another class which could allow
>> us to wait for multiple events.
>>
>> Again, i'm not sure how properly call it..
>> Just want to illustrate the usage:
>>
>> semaphore := Semaphore new.
>> delay := Delay forMilliseconds: 100.
>>
>> signaler := (MultipleWait on: { semaphore. delay }) waitForOne.  "wait
>> until one of the listed objects get signaled"
>> or:
>> result := (MultipleWait on: { semaphore. delay }) waitForAll. "wait
>> until all of the listed objects get signaled"
>>
>> by default, MultipleWait>>waitForSignalBy: is releasing waiter once
>> one of the listed objects signaled (as in waitForOne case).
>> Not sure about return result of #waitForAll, IMO , it could be just a
>> boolean value - return true, if all objects signaled, or false in case
>> if waiting abandoned.
>>
>> Now, of course, since  MultipleWait acts as a waiter and as an object
>> who can be waited on, we can allow nesting:
>>
>> mwait1 := MultipleWait on: { sema1. sema2 }.
>> (MultipleWait on: { mwait1. delay }) waitForOne.
>>
>> and finally, as you may guess , a Semaphore>>waitTimeoutMSecs: now can
>> be implemented as:
>>
>> waitTimeoutMSecs: anInteger
>>        "Wait on this semaphore for up to the given number of milliseconds,
>> then timeout.
>>        Return true if the deadline expired, false otherwise."
>>        | delay |
>>
>>       delay := Delay forMilliseconds: (anInteger max: 0).
>>       ^ delay == (MultipleWait on: { self. delay }) waitForOne.
>>
>>
>
>
>



--
Best regards,
Igor Stasenko AKA sig.

Reply | Threaded
Open this post in threaded view
|

Re: [squeak-dev] Re: Interaction between objects which can be waited on, and their clients(waiters)

Igor Stasenko
2009/5/5 Igor Stasenko <[hidden email]>:

> 2009/5/5 Andreas Raab <[hidden email]>:
>> Hi Igor -
>>
>> How is what you are proposing fundamentally different from a solution that
>> simply creates a watcher process for each event source and then combines
>> those properly? I.e.,
>>
>> Object subclass: #MultiWait
>>  instanceVariableNames: 'eventSemas'
>>  ...
>>
>> waitForAll
>>  "waits for signals from all event sources"
>>  eventSemas do:[:sema| sema wait].
>>
>> waitForOne
>>  "Waits for signals from at least one event source.
>>   Returns all the semaphores that have been signaled."
>>  waitSema := Semaphore new.
>>  processes := eventSemas do:[:sema| [sema wait. waitSema signal] fork].
>>  waitSema wait. "or waitTimeoutMSecs:"
>>  signaled := Set new.
>>  eventSemas with: processes do:[:list :proc|
>>    (proc suspend == list) ifTrue:[signaled add: list].
>>    proc terminate.
>>  ].
>>  ^signaled
>>
>
> the fundamental difference that by adding the protocol
> (waitForSignalBy:/waitComplete:) i can put any waiter to the
> semaphore, instead of just process.
> This means, that i can schedule a waiting for a signal from
> semaphore/delay without stopping any process.
> If this doesn't have any potential use cases, then let just abandon
> this idea alltogether.
>
>> DelayWaitTimeout is an optimization that avoids the need for additional
>> processes - when you use #waitTimeoutMSecs: a *lot* (say, because you're
>> running a server with heavy network activity) the additional overhead
>> incurred by the additional processes (and process switches) gets relevant in
>> a hurry. But other than that, the above is a straightforward solution that
>> will work with anything that supports the #wait protocol.
>>
>
> Surely, this is an optimization. But in my current implementation
> (which is not yet released due the questions above) , a Delay holds a
> reference to Process, not Semaphore. So comparing to old scheduler:
> a 'Delay waitForMilliseconds:  xxx '
>
> will create 1 object (Delay) instead of two (Delay + Semaphore).
>
> Which IMO is more optimal :)
>
> And new DelayWaitTimeout will have same references - semaphore +
> process, so there will be no difference , except that it adds
> 'semaphore' ivar instead of 'process' ivar.
> So, everythign is same for this case, except that initially, a new
> Delay don't needs to create a Semaphore instance.
>

Similarily, if Semaphore implements a waiter protocol, then we don't
need to have a separate class (DelayWaitTimeout ),
we could simply do a nesting
Process --- waits on --> Semaphore --waits on--->Delay

>> Cheers,
>>  - Andreas
>>
>> Igor Stasenko wrote:
>>>
>>> Hello list,
>>>
>>> I stared @ DelayWaitTimeout class, and thinking, how to implement it
>>> in new scheduler..
>>> First i thought , just make it quickly and forget about it, but one
>>> thing which don't makes me feel good about it, that
>>> DelayWaitTimeout is a perfect example, when you need to stop a Process
>>> until one of the multiple events occurs - in case of DelayWaitTimeout
>>> its either semaphore signaled or delay expired.
>>>
>>> So, i thinking, what if instead of implementing a DelayWaitTimeout ,
>>> which works only for 2 objects - delay & semaphore, implement a more
>>> generic class , which would allow us to wait for any number of objects
>>> and blocks process until one of them is signaled. Or, blocks process
>>> until all of them signaled.
>>> There's nothing new, in fact, in Windoze there an API function, called
>>> WaitForMultipleObjects, which does exactly what i describing - it
>>> could wait on multiple objects, releasing a waiter once a single
>>> object signaled, or release waiter only after all objects signaled.
>>>
>>> Here is my thoughts, how to implement that.
>>> First, we need to allow Delay & Semaphore to work with abstract waiter
>>> object (not just Process).
>>> A proposed protocol between object which acts as one who can be waited
>>> on, and object who acts as waiter is following:
>>>
>>> To start waiting, we sending a #waitForSignalBy: waiter
>>> semaphore waitForSignalBy: waiter
>>> or
>>> delay waitForSignalBy: waiter
>>>
>>> then, default #wait could be implemented, in both cases, simply as:
>>>
>>> ^ self waitForSignalBy: Processor activeProcess.
>>>
>>> now, after issuing #waitForSignalBy: we have the following possible cases:
>>> - given object is already signaled (as a semaphore with excess
>>> signals), in this case we immediately sending #waitComplete: to waiter
>>>
>>> - given object is scheduled for being signaled later , in this case we
>>> sending #startWaitingOn: to waiter.
>>>
>>> - now after #startWaitingOn: issued and once a semaphore/delay
>>> signaled we sending #waitComplete: to waiter.
>>>
>>> - in case if semaphore/delay abandoned, we sending #waitAbandoned: to
>>> waiter. This happens, for instance, when we terminating a process who
>>> currently waits on semaphore.
>>>
>>> (If you have a better idea how to name these messages, please make a
>>> guess)
>>>
>>> Now, by having such protocol implemented for Delay, Semaphore &
>>> Process, we now can easily introduce another class which could allow
>>> us to wait for multiple events.
>>>
>>> Again, i'm not sure how properly call it..
>>> Just want to illustrate the usage:
>>>
>>> semaphore := Semaphore new.
>>> delay := Delay forMilliseconds: 100.
>>>
>>> signaler := (MultipleWait on: { semaphore. delay }) waitForOne.  "wait
>>> until one of the listed objects get signaled"
>>> or:
>>> result := (MultipleWait on: { semaphore. delay }) waitForAll. "wait
>>> until all of the listed objects get signaled"
>>>
>>> by default, MultipleWait>>waitForSignalBy: is releasing waiter once
>>> one of the listed objects signaled (as in waitForOne case).
>>> Not sure about return result of #waitForAll, IMO , it could be just a
>>> boolean value - return true, if all objects signaled, or false in case
>>> if waiting abandoned.
>>>
>>> Now, of course, since  MultipleWait acts as a waiter and as an object
>>> who can be waited on, we can allow nesting:
>>>
>>> mwait1 := MultipleWait on: { sema1. sema2 }.
>>> (MultipleWait on: { mwait1. delay }) waitForOne.
>>>
>>> and finally, as you may guess , a Semaphore>>waitTimeoutMSecs: now can
>>> be implemented as:
>>>
>>> waitTimeoutMSecs: anInteger
>>>        "Wait on this semaphore for up to the given number of milliseconds,
>>> then timeout.
>>>        Return true if the deadline expired, false otherwise."
>>>        | delay |
>>>
>>>       delay := Delay forMilliseconds: (anInteger max: 0).
>>>       ^ delay == (MultipleWait on: { self. delay }) waitForOne.
>>>
>>>
>>
>>
>>
>
>
>
> --
> Best regards,
> Igor Stasenko AKA sig.
>



--
Best regards,
Igor Stasenko AKA sig.

Reply | Threaded
Open this post in threaded view
|

Re: [squeak-dev] Re: Interaction between objects which can be waited on, and their clients(waiters)

Igor Stasenko
In reply to this post by Andreas.Raab
2009/5/5 Andreas Raab <[hidden email]>:
> Just for fun, find an attached a version of MultiWait that works just like
> described earlier. If you run the benchmarks on MultiWait you'll find that
> DelayWaitTimeout is about 5x faster when compared with the naive MultiWait
> equivalent.
>
Obviously, this is because in #waitForOne you have to create a
separate forks for each event , for waiting on them.
This is because currently, a waiter can be only the Process.
In contrast, in new implementation we could allow a waiter to be any
object - and in case of MultiWait this will be 'self'.. i.e.

MultiWait>>waitForOne
        "Waits for signals from at least one event source.
        Returns all semaphores that have been signaled."
        | waitSema processes |

       signaled := Array new: eventSemas size. "signaled is an ivar,
and actually can be initialized once we setting eventSemas collection
"
        eventSemas do: [:sema |
            sema waitForSignalBy: self. "let receiver wait for signal
from semaphore"
        ].
       self wait.
        ^signaled

> Cheers,
>  - Andreas
>
> Andreas Raab wrote:
>>
>> Hi Igor -
>>
>> How is what you are proposing fundamentally different from a solution that
>> simply creates a watcher process for each event source and then combines
>> those properly? I.e.,
>>
>> Object subclass: #MultiWait
>>  instanceVariableNames: 'eventSemas'
>>  ...
>>
>> waitForAll
>>  "waits for signals from all event sources"
>>  eventSemas do:[:sema| sema wait].
>>
>> waitForOne
>>  "Waits for signals from at least one event source.
>>   Returns all the semaphores that have been signaled."
>>  waitSema := Semaphore new.
>>  processes := eventSemas do:[:sema| [sema wait. waitSema signal] fork].
>>  waitSema wait. "or waitTimeoutMSecs:"
>>  signaled := Set new.
>>  eventSemas with: processes do:[:list :proc|
>>    (proc suspend == list) ifTrue:[signaled add: list].
>>    proc terminate.
>>  ].
>>  ^signaled
>>
>> DelayWaitTimeout is an optimization that avoids the need for additional
>> processes - when you use #waitTimeoutMSecs: a *lot* (say, because you're
>> running a server with heavy network activity) the additional overhead
>> incurred by the additional processes (and process switches) gets relevant in
>> a hurry. But other than that, the above is a straightforward solution that
>> will work with anything that supports the #wait protocol.
>>
>> Cheers,
>>  - Andreas
>>
>> Igor Stasenko wrote:
>>>
>>> Hello list,
>>>
>>> I stared @ DelayWaitTimeout class, and thinking, how to implement it
>>> in new scheduler..
>>> First i thought , just make it quickly and forget about it, but one
>>> thing which don't makes me feel good about it, that
>>> DelayWaitTimeout is a perfect example, when you need to stop a Process
>>> until one of the multiple events occurs - in case of DelayWaitTimeout
>>> its either semaphore signaled or delay expired.
>>>
>>> So, i thinking, what if instead of implementing a DelayWaitTimeout ,
>>> which works only for 2 objects - delay & semaphore, implement a more
>>> generic class , which would allow us to wait for any number of objects
>>> and blocks process until one of them is signaled. Or, blocks process
>>> until all of them signaled.
>>> There's nothing new, in fact, in Windoze there an API function, called
>>> WaitForMultipleObjects, which does exactly what i describing - it
>>> could wait on multiple objects, releasing a waiter once a single
>>> object signaled, or release waiter only after all objects signaled.
>>>
>>> Here is my thoughts, how to implement that.
>>> First, we need to allow Delay & Semaphore to work with abstract waiter
>>> object (not just Process).
>>> A proposed protocol between object which acts as one who can be waited
>>> on, and object who acts as waiter is following:
>>>
>>> To start waiting, we sending a #waitForSignalBy: waiter
>>> semaphore waitForSignalBy: waiter
>>> or
>>> delay waitForSignalBy: waiter
>>>
>>> then, default #wait could be implemented, in both cases, simply as:
>>>
>>> ^ self waitForSignalBy: Processor activeProcess.
>>>
>>> now, after issuing #waitForSignalBy: we have the following possible
>>> cases:
>>> - given object is already signaled (as a semaphore with excess
>>> signals), in this case we immediately sending #waitComplete: to waiter
>>>
>>> - given object is scheduled for being signaled later , in this case we
>>> sending #startWaitingOn: to waiter.
>>>
>>> - now after #startWaitingOn: issued and once a semaphore/delay
>>> signaled we sending #waitComplete: to waiter.
>>>
>>> - in case if semaphore/delay abandoned, we sending #waitAbandoned: to
>>> waiter. This happens, for instance, when we terminating a process who
>>> currently waits on semaphore.
>>>
>>> (If you have a better idea how to name these messages, please make a
>>> guess)
>>>
>>> Now, by having such protocol implemented for Delay, Semaphore &
>>> Process, we now can easily introduce another class which could allow
>>> us to wait for multiple events.
>>>
>>> Again, i'm not sure how properly call it..
>>> Just want to illustrate the usage:
>>>
>>> semaphore := Semaphore new.
>>> delay := Delay forMilliseconds: 100.
>>>
>>> signaler := (MultipleWait on: { semaphore. delay }) waitForOne.  "wait
>>> until one of the listed objects get signaled"
>>> or:
>>> result := (MultipleWait on: { semaphore. delay }) waitForAll. "wait
>>> until all of the listed objects get signaled"
>>>
>>> by default, MultipleWait>>waitForSignalBy: is releasing waiter once
>>> one of the listed objects signaled (as in waitForOne case).
>>> Not sure about return result of #waitForAll, IMO , it could be just a
>>> boolean value - return true, if all objects signaled, or false in case
>>> if waiting abandoned.
>>>
>>> Now, of course, since  MultipleWait acts as a waiter and as an object
>>> who can be waited on, we can allow nesting:
>>>
>>> mwait1 := MultipleWait on: { sema1. sema2 }.
>>> (MultipleWait on: { mwait1. delay }) waitForOne.
>>>
>>> and finally, as you may guess , a Semaphore>>waitTimeoutMSecs: now can
>>> be implemented as:
>>>
>>> waitTimeoutMSecs: anInteger
>>>    "Wait on this semaphore for up to the given number of milliseconds,
>>> then timeout.
>>>    Return true if the deadline expired, false otherwise."
>>>    | delay |
>>>
>>>       delay := Delay forMilliseconds: (anInteger max: 0).
>>>       ^ delay == (MultipleWait on: { self. delay }) waitForOne.
>>>
>>>
>>
>>
>>
>
>
>
>
>



--
Best regards,
Igor Stasenko AKA sig.

Reply | Threaded
Open this post in threaded view
|

[squeak-dev] Re: Interaction between objects which can be waited on, and their clients(waiters)

Andreas.Raab
In reply to this post by Igor Stasenko
Igor Stasenko wrote:
> 2009/5/5 Andreas Raab <[hidden email]>:
>> How is what you are proposing fundamentally different from a solution that
>> simply creates a watcher process for each event source and then combines
>> those properly? I.e.,
> the fundamental difference that by adding the protocol
> (waitForSignalBy:/waitComplete:) i can put any waiter to the
> semaphore, instead of just process.

And I have absolutely no clue what you mean when you say that ;-) To me,
the concept of "waiting" is intrinsically tied to the concept of
"progressing" and only processes can do either. It is meaningless to me
to say "this number is waiting" - only active objects (processes) can
wait since only active objects (processes) can advance.

Can you explain in more detail what it *means* for an object other than
a process to wait? I just don't get it.

Sorry for being slow,
   - Andreas


Reply | Threaded
Open this post in threaded view
|

Re: [squeak-dev] Re: Interaction between objects which can be waited on, and their clients(waiters)

Igor Stasenko
2009/5/5 Andreas Raab <[hidden email]>:

> Igor Stasenko wrote:
>>
>> 2009/5/5 Andreas Raab <[hidden email]>:
>>>
>>> How is what you are proposing fundamentally different from a solution
>>> that
>>> simply creates a watcher process for each event source and then combines
>>> those properly? I.e.,
>>
>> the fundamental difference that by adding the protocol
>> (waitForSignalBy:/waitComplete:) i can put any waiter to the
>> semaphore, instead of just process.
>
> And I have absolutely no clue what you mean when you say that ;-) To me, the
> concept of "waiting" is intrinsically tied to the concept of "progressing"
> and only processes can do either. It is meaningless to me to say "this
> number is waiting" - only active objects (processes) can wait since only
> active objects (processes) can advance.
>
> Can you explain in more detail what it *means* for an object other than a
> process to wait? I just don't get it.
>

Okay, the current semaphore/process interaction is based on an
implicit scheme which can be illustrated as:
process do: something after: signal/event.

this is exactly as you saying, intrinsically, a process can't advance
when its waiting on semaphore.
But at the same time, a Processor can advance, and it is, otherwise it
would be impossible to have scheduling/task switching.

If we look deeper, a #do:after: scheme is implemented on more basic
scheme, which can be illustrated as:
object on: signal/event  do: something, which, in case of
Semaphore/Delays is nothing more than an observer pattern.
So, what i proposing is to employ a custom observer pattern for event
sources (Semaphores, Delays) and for the event consumers - Processes,
or any other objects.

This is possible now, because in new scheduler the 'intrinsic' #on:do:
scheme has moved from VM to language side.
So, what stops us from having this as a basic interaction layer,
instead of #do:after: ?


> Sorry for being slow,
>  - Andreas
>


--
Best regards,
Igor Stasenko AKA sig.