Process

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

Process

Louis Sumberg-2
I want to start a process when some user-initiated action occurs.  If the
user repeats their action before the process has completed, I want to stop
the current process and start a new one.  I have the following code in a
subclass of Shell, where process is an instance variable.

onSomeEvent
    process notNil ifTrue: [process terminate].
    process := [self doSomething: self someMessage]
        forkAt: Processor userBackgroundPriority]

It looks like if the current process has completed, then all is well, but if
the current process has not completed, then the first line of the method is
executed, but not the second.  I tried putting in 'process := nil' after
'process terminate' which had no effect, and I also tried changing
'userBackgroundPriority' to higher priorities, again with no effect.

Any help would be appreciated.

-- Louis


Reply | Threaded
Open this post in threaded view
|

Re: Process

Steve Alan Waring
Louis Sumberg wrote:

> I want to start a process when some user-initiated action occurs.  If
> the user repeats their action before the process has completed, I
> want to stop the current process and start a new one.  I have the
> following code in a subclass of Shell, where process is an instance
> variable.
>
> onSomeEvent
>     process notNil ifTrue: [process terminate].
>     process := [self doSomething: self someMessage]
>         forkAt: Processor userBackgroundPriority]
>
> It looks like if the current process has completed, then all is well,
> but if the current process has not completed, then the first line of
> the method is executed, but not the second.

Hi Louis,

 From the description of the problem, it sounds like the #onSomeEvent message
is being sent in the "process" Process. You could confirm that with an
assertion:

  onSomeEvent
     process notNil ifTrue: [
     self assert: [Processor activeProcess ~~ process]
     ....]
  ....

If so, you could probably fix the problem by sending #onSomeEvent in the
"main" process using:

    SessionManager inputState queueDeferredAction: [self onSomeEvent].

I have methods that look like yours, and I have had no problems with them,
however I do try to cleanly separate methods and state that control the
forked process, from the messages that the forked process sends. At the
least it makes the code easier to test.

> I tried putting in
> 'process := nil' after 'process terminate' which had no effect,

In this case, since you are creating a new process immediately I dont think
it is necessary, however terminating a process that is not the active
process does lead to an interesting chain of events. While you can send
#terminate to a process twice, I avoid it, and in D5 I have been using
something like:

    process ifNotNil: [ :theProcess |
       process := nil.
       theProcess terminate].


A couple of other thoughts, while not relevant to the code fragment you
posted, they may be to the bigger picture:

 - Are you assuming any timing order between the first process being
terminated (and any unwind blocks being evaluated) and the second process
being started?

 - Are you doing any clean-up before terminating the process. For example:

      process notNil ifTrue: [
            self doCleanUp.
            process terminate].

To get the #doCleanUp send out of the controlling process, this could be
rewritten as:

     process := [[self doSomething: self someMessage]] ensure: [self
doCleanUp]].
       forkAt: Processor userBackgroundPriority]


Steve

--
Steve Waring
Email: [hidden email]
Journal: http://www.stevewaring.net/blog/home/index.html


Reply | Threaded
Open this post in threaded view
|

Re: Process

Louis Sumberg-2
Hi Steve,

Wow.  Thanks for the wealth of information.

> From the description of the problem, it sounds like the #onSomeEvent
message
> is being sent in the "process" Process. You could confirm that with an
> assertion:

Yes, that confirmed it alright.  However, see below, I really don't
understand.

> If so, you could probably fix the problem by sending #onSomeEvent in the
> "main" process using:
>
>     SessionManager inputState queueDeferredAction: [self onSomeEvent].

I'm still not understanding something.  Like ShellExplorer,
#createSchematicWiring sets up an observer (on a ShellTreePresenter) that
calls #onFolderSelectionChanged when the user clicks on a folder. Unlike
ShellExplorer, I want to spawn a process in #onFolderSelectionChanged (which
we probably would have had to do in ShellExplorer if it wasn't so fast) that
can be killed when the user switches to another folder.  Isn't
#onFolderSelectionChanged, as with all UI events, coming in on the "main"
process, i.e., the Dolphin thread?  I tried putting your suggestion above in
a couple of places, but I got the same assertion failure.

> I have methods that look like yours,

Sorry to hear that :)

> ... I do try to cleanly separate methods and state that control the
> forked process, from the messages that the forked process sends.

I suspect understanding the above will help me understand this part too.
Thanks also for the heads-up on timing and clean-up.  No doubt I'll be
getting to those.

-- Louis.


Reply | Threaded
Open this post in threaded view
|

Re: Process

Steve Alan Waring
Hi Louis,

Louis Sumberg wrote:

> switches to another folder.  Isn't #onFolderSelectionChanged, as with
> all UI events, coming in on the "main" process, i.e., the Dolphin
> thread?

Not necessarily. If your forked process manipulates the model, then events
can be triggered and handled in the forked process.

Looking at TreeView and the selectionChanged event, it might be as a result
of nodes being deleted or collapsed by the forked process. Does that make
any sense?

I think good practice is to keep what the forked process does (and accesses)
separate from your UI models, and always send messages to a model (ie a
ListModel or TreeModel) in the main process. Even still, I wouldnt start
forking processes without carefully considering the added complexity.

As an example your doSomething: method could look like:

  [| newNode |
    newNode := self getNextSlowThing.
    SessionManager inputState queueDeferredAction: [treeModel add: newNode
asChildOf: newNode parent].
    self hasMoreNodes] whileTrue


If that doesnt help, can you send me the changes to ShellExplorer which show
the problem?

Thanks,
Steve

--
Steve Waring
Email: [hidden email]
Journal: http://www.stevewaring.net/blog/home/index.html


Reply | Threaded
Open this post in threaded view
|

Re: Process

L. M. Rappaport
In reply to this post by Louis Sumberg-2
On Mon, 20 Jan 2003 14:20:38 -0800, "Louis Sumberg"
<[hidden email]> wrote (with possible editing):

>I want to start a process when some user-initiated action occurs.  If the
>user repeats their action before the process has completed, I want to stop
>the current process and start a new one.  I have the following code in a
>subclass of Shell, where process is an instance variable.
>
>onSomeEvent
>    process notNil ifTrue: [process terminate].
>    process := [self doSomething: self someMessage]
>        forkAt: Processor userBackgroundPriority]
>
>It looks like if the current process has completed, then all is well, but if
>the current process has not completed, then the first line of the method is
>executed, but not the second.  I tried putting in 'process := nil' after
>'process terminate' which had no effect, and I also tried changing
>'userBackgroundPriority' to higher priorities, again with no effect.
>
>Any help would be appreciated.
>
>-- Louis
>

Just a thought - couldn't you use a singleton to keep track?  

--
Larry
[hidden email]


Reply | Threaded
Open this post in threaded view
|

Re: Process

Louis Sumberg-2
In reply to this post by Steve Alan Waring
Hi Steve,

> If your forked process manipulates the model, then events
> can be triggered and handled in the forked process. <snip>
>Does that make any sense?

Yes, it makes sense, or at least enough sense that I know not to go there
for now. *s*

I've managed to get it to work by removing the process instance variable and
still forking a process, and essentially putting the code that's forked in
an Error block.  This has the effect that when a new process is created,
state in the old one is invalid and so an Error bubbles up, is caught, and
the old process dies a quiet death.

Also, the app I'm working on is not ShellExplorer, sorry for the confusion.
It's actually the NickelViewer (for viewing thumbnails) I mentioned the
other day.  So far, the app itself doesn't have a model *gasp*, but it uses
a ShellTreePresenter (which does, of course, have a model, and is where
onSelectionChanged is caught).

Thanks again for your time, effort, and help.

-- Louis


Reply | Threaded
Open this post in threaded view
|

Re: Process

Louis Sumberg-2
In reply to this post by L. M. Rappaport
> Just a thought - couldn't you use a singleton to keep track?

Larry, thanks for the suggestion.  I don't understand how that would work
though.  In the meantime, I think I've solved this particular problem.

-- Louis