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. |
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. > > |
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 |
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. |
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. |
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. |
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 |
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. |
Free forum by Nabble | Edit this page |