Events again

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

Events again

Fernando Rodríguez
Hi,

I'm almost done with events. One last question, though: can I fire
asynchronous events? It looks like all the events are syncrhonous...

Thanks


Reply | Threaded
Open this post in threaded view
|

Re: Events again

Martin Rubi
Although I actually don't remember having seen this, I guess it could be
done with

[self trigger: #someEvent] fork.

but perhaps there is a good reason, which I'm missing, for not ever having
seen this before ... is there ?

Regards.
Martin

> Hi,
>
> I'm almost done with events. One last question, though: can I fire
> asynchronous events? It looks like all the events are syncrhonous...
>
> Thanks
>


Reply | Threaded
Open this post in threaded view
|

Re: Events again

Chris Uppal-3
Martin wrote:

> [self trigger: #someEvent] fork.
>
> but perhaps there is a good reason, which I'm missing, for not ever having
> seen this before ... is there ?

I don't think there's any reason not to, it's more a case of it not being
something you need all that often.

There seem to be two reasons why you might want to do something like that, one
is to ensure that a buggy receiver of the trigger can't screw up the operation
of the sender.  That case could also be handled (synchronously) by:

    [self trigger: #someEvent]
        on: Error        "or should it be Exception ?"
        do: [:err | err notifiy].

The other is to ensure that long-running handlers do not slow the sender down,
but then if that's the motivation it might be better for the receiver of the
notification to do the fork instead.  After all it knows when its going to do
something slow, but the observee can only guess.

One special-case of asynchronous events that I've occasionally wanted is some
sort of deferred event where the handler is guaranteed to be invoked in the
"Main" (user interface) Process while the trigger can be issued from any
Process.  I've never got around to trying to implement that, though.  It's easy
to do if the sender of the event is required to use a special form (equivalent
to [self trigger: #someEvent] postToInputQueue), but ideally I'd want it to be
the observer that used a special form (#when:deferSending:to: or some-such).

    -- chris


Reply | Threaded
Open this post in threaded view
|

Re: Events again

Martin Rubi
Hello Chris

> There seem to be two reasons why you might want to do something like that,
one
> is to ensure that a buggy receiver of the trigger can't screw up the
operation
> of the sender.  That case could also be handled (synchronously) by:
>
>     [self trigger: #someEvent]
>         on: Error        "or should it be Exception ?"
>         do: [:err | err notifiy].

Never thought in this, it's good to know it.

> One special-case of asynchronous events that I've occasionally wanted is
some
> sort of deferred event where the handler is guaranteed to be invoked in
the
> "Main" (user interface) Process while the trigger can be issued from any
> Process.  I've never got around to trying to implement that, though.  It's
easy
> to do if the sender of the event is required to use a special form
(equivalent
> to [self trigger: #someEvent] postToInputQueue), but ideally I'd want it
to be
> the observer that used a special form (#when:deferSending:to: or
some-such).

Is it possible that this would do the trick ? Perhaps it has perfomance
penalties, but still, if all it takes to do such a thing are 3 short
methods, then I just can't believe smalltalk (once again).

---
Object>>when: anEventSymbol deferSend: aSelector to: anObject

    self
        when: anEventSymbol
        perform:
            [SessionManager inputState
                queueDeferredAction: (EventMessageSend receiver: anObject
selector: aSelector)]

---

I tested it with this script, and seems to work, but then again, I might be
missing something, since a few moments ago I didn't even think about doing
such a thing ...

---

    subject := Object new.
    block := [Transcript
                    nextPutAll: Processor activeProcess id printString;
                    nextPutAll: ' is ui process: ';
                    nextPutAll: Processor isActiveMain printString;
                    cr].
    subject
        when: #event
        send: #value
        to: block;

        when: #event
        deferSend: #value
        to: block.

    [subject trigger: #event] forkAt: 9.
    1 to: 10 do: [:i | Transcript nextPutAll: 'hi there'; cr]


Reply | Threaded
Open this post in threaded view
|

Re: Events again

Chris Uppal-3
Martin wrote:

> ---
> Object>>when: anEventSymbol deferSend: aSelector to: anObject
>
>     self
>         when: anEventSymbol
>         perform:
>             [SessionManager inputState
>                 queueDeferredAction: (EventMessageSend receiver: anObject
> selector: aSelector)]
>
> ---

Yes, that should work.  Thanks.

For some reason I was imagining something a lot more complicated.  A different
kind of EventMessageSend perhaps...

    -- chris


Reply | Threaded
Open this post in threaded view
|

Q: "Best practice" when using asynchronous processes (was: Re: Events again)

Bernhard Kohlhaas-6
In reply to this post by Chris Uppal-3
Chris Uppal wrote:

> Martin wrote:
>
>
>>[self trigger: #someEvent] fork.
>>
>>but perhaps there is a good reason, which I'm missing, for not ever having
>>seen this before ... is there ?
>
>
> I don't think there's any reason not to, it's more a case of it not being
> something you need all that often.

[...]

> One special-case of asynchronous events that I've occasionally wanted is some
> sort of deferred event where the handler is guaranteed to be invoked in the
> "Main" (user interface) Process while the trigger can be issued from any
> Process.  I've never got around to trying to implement that, though.  It's easy
> to do if the sender of the event is required to use a special form (equivalent
> to [self trigger: #someEvent] postToInputQueue), but ideally I'd want it to be
> the observer that used a special form (#when:deferSending:to: or some-such).

So far I have posted all my asynchronous events into the input queue,
just because I didn't want to take too many assumptions on the
"process-safety"/"thread-safety" (or whatever the correct term here is)
of the receiver.

In fact in most of my coding, whenever I have used asynchronous
processes, I have tried only to do the bare minimum in those processes
(i.e. overlapped blocking system calls) and forward the results to the
"main" process for further processing ASAP.

I am just wondering, if that is just undue caution on my part - being a
Smalltalk beginner - and how lavishly other people use asynchronous
processes. What would be the "best practice" when using async processes
both in regards to writing your own classes as well as interacting with
"foreign" classes?

Looking forward to any comments and insights on the matter,

Bernhard


Reply | Threaded
Open this post in threaded view
|

Re: Q: "Best practice" when using asynchronous processes

Schwab,Wilhelm K
Bernhard,

> So far I have posted all my asynchronous events into the input queue,
> just because I didn't want to take too many assumptions on the
> "process-safety"/"thread-safety" (or whatever the correct term here is)
> of the receiver.

FWIW, my experience is that somebody will correct you no matter which
term you use :)  They are called Process for historical reasons; the
reality is that they act more like threads.  Either way, you are wise to
respect both what can do for you, and to you.


> In fact in most of my coding, whenever I have used asynchronous
> processes, I have tried only to do the bare minimum in those processes
> (i.e. overlapped blocking system calls) and forward the results to the
> "main" process for further processing ASAP.
>
> I am just wondering, if that is just undue caution on my part - being a
> Smalltalk beginner

When in doubt about the receiver, it's reasonable.  However, you are
denying yourself considerable flexibility if you run everything on the
main thread.

Any GUI updates should be synchronized with the main thread (posted to
the message queue via #post* or #queueDeferredAction:), but you can
frequently do a lot of computation on a background thread first, and
then do only the work required for the actual update on the main thread.

If multiple threads access an object, then either it or you must arrange
for proper synchronization; SharedQueue, SharedLookupTable, most if not
all weak collections are thread safe.

For other classes, you need to help them.  See Mutex>>critical:.  Note
that some problems might require multiple mutexes.  You might use either
a shared collection or a mutex-protected collection to hold
"connections" (to just about anything) and then give each connection its
own mutex (or two??) to synchronize reads and writes over the individual
connection.  A search of the archives will turn up some of my past
mumblings on the subject, including mention of tools useful for hunting
down deadlocks.

Note that you will typically want to overprotect, as a deadlock is far
easier to detect than random results due to inadequate synchronization.

The Process Safe Class pattern from Dolphin's early days is still well
worth reading.

Have a good one,

Bill

--
Wilhelm K. Schwab, Ph.D.
[hidden email]


Reply | Threaded
Open this post in threaded view
|

Re: Q: "Best practice" when using asynchronous processes

Bernhard Kohlhaas-6
Bill Schwab wrote:
> Bernhard,

[...]

> FWIW, my experience is that somebody will correct you no matter which
> term you use :)  They are called Process for historical reasons; the
> reality is that they act more like threads.  Either way, you are wise to
> respect both what can do for you, and to you.

Very true, as I've had _some_ expeciences (in other programming
languages) of what asynchronous thread can do to me ;) That gives me a
healthy dose of respect for them.

[...]

> When in doubt about the receiver, it's reasonable.  However, you are
> denying yourself considerable flexibility if you run everything on the
> main thread.
>
> Any GUI updates should be synchronized with the main thread (posted to
> the message queue via #post* or #queueDeferredAction:), but you can
> frequently do a lot of computation on a background thread first, and
> then do only the work required for the actual update on the main thread.

So what about triggering of events? Should the sender only do them in
the main thread or should the receiver-method post a send to itself into
the message queue, if the current thread isn't already the main one?

> If multiple threads access an object, then either it or you must arrange
> for proper synchronization; SharedQueue, SharedLookupTable, most if not
> all weak collections are thread safe.
>
> For other classes, you need to help them.  See Mutex>>critical:.  Note
> that some problems might require multiple mutexes.  You might use either
> a shared collection or a mutex-protected collection to hold
> "connections" (to just about anything) and then give each connection its
> own mutex (or two??) to synchronize reads and writes over the individual
> connection.  A search of the archives will turn up some of my past
> mumblings on the subject, including mention of tools useful for hunting
> down deadlocks.
>
> Note that you will typically want to overprotect, as a deadlock is far
> easier to detect than random results due to inadequate synchronization.
>
> The Process Safe Class pattern from Dolphin's early days is still well
> worth reading.

Thank you, Bill, for the reading pointers. I'll read up on it.

Bernhard


Reply | Threaded
Open this post in threaded view
|

Re: Q: "Best practice" when using asynchronous processes

Schwab,Wilhelm K
Bernhard,

> So what about triggering of events? Should the sender only do them in
> the main thread or should the receiver-method post a send to itself into
> the message queue, if the current thread isn't already the main one?

I will defer to OA on this, but the event mechanism itself appears to be
sufficiently thread safe to not blow up over various threads registering
and unregistering interest in events, but the event sinks are in general
not going to be thread safe.

When in doubt, you can do something like

[ self trigger:#notThreadSafe: with:resultsOfSomeUglyCalculation. ] post

or otherwise queue the triggering so it happens on the GUI thread.

Have a good one,

Bill

--
Wilhelm K. Schwab, Ph.D.
[hidden email]


Reply | Threaded
Open this post in threaded view
|

Re: Q: "Best practice" when using asynchronous processes

Bernhard Kohlhaas-7
Bill,

>> So what about triggering of events? Should the sender only do them in
>> the main thread or should the receiver-method post a send to itself
>> into the message queue, if the current thread isn't already the main one?
>
>
> I will defer to OA on this, but the event mechanism itself appears to be
> sufficiently thread safe to not blow up over various threads registering
> and unregistering interest in events, but the event sinks are in general
> not going to be thread safe.

So if the event sinks are not thread-safe, then the triggering object
should only do it in the main thread, correct?

> When in doubt, you can do something like
>
> [ self trigger:#notThreadSafe: with:resultsOfSomeUglyCalculation. ] post
>
> or otherwise queue the triggering so it happens on the GUI thread.

So adding a method like:

        BlockClosure>>evaluateInMainProcess
                Processor isActiveMain
          ifTrue: [ self value ]
                        ifFalse: [ self postToInputQueue ]

and then doing something like:
     [ self trigger: #notThreadSafe: with: resultsOfSomeUglyCalculation ]
        evaluateInMainProcess

should be sufficiently convenient in addition to being safe.

Bernhard