User Interface single or multi threaded

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

User Interface single or multi threaded

Bill Dargel
I've always thought of the user interface process of being just that ---
a single process. At least it would act that way, even if the VM was
using some slight of hand to pull off some tricks. I basically thought,
that if one didn't use "fork" in your application, that you didn't have
to worry about concurrent access issues. I found a case where this
breaks down when using a MessageBox.

A distillation of the key methods is:

MyPresenter>>myButtonWasClicked
  (MessageBox confirm: 'OK to do that?') ifTrue:
      [self doOperationWhichAlsoClearsTheCachedUIData].

MyImageView>>onPaintRequired: aPaintEvent
  self setImageByBuildingTheCacheOfUIData.
  ^super onPaintRequired: aPaintEvent

I hadn't put any mutex's on the cached data, because I thought of the UI
as being single threaded. But MessageBox>>basicOpen does a "Processor
forkMainIfMain". The net result is that two different processes can be
building and clearing the cache of UI data at the same time. The process
building the data can, midway through its operation, suddenly find
things have been nil'ed out by the other process.

I see that using DialogViews with #showModal, is also forking the UI
process. But in DialogView>>runModalLoop it is incrementing the priority
of the process, if it had been the main process. The comment says:
"Boost the initiating UI processes priority so that the operation
requested by the user completes more quickly when the dialog is closed."

It seems like the net effect would be that the code following the modal
dialog would run without being interrupted by the (now lower priority)
main UI process working on things from Windows input queue. Does the
event that terminates the modal dialog come through Windows input queue?
If so, then the main UI process would presumably not be in the middle of
something else when the higher priority code after the dialog kicks in.
If this analysis is accurate (please comment if you know), then overall,
it still acts as if the UI was a single process. Is that right?

I tried the same priority boosting trick in MessageBox>>basicOpen after
the #forkMainIfMain. And my particular problem went away. Is this a
recommended change to make? Should it be included in the base?

thanks,
-Bill

-------------------------------------------
Bill Dargel            [hidden email]
Shoshana Technologies
100 West Joy Road, Ann Arbor, MI 48105  USA


Reply | Threaded
Open this post in threaded view
|

Re: User Interface single or multi threaded

Blair McGlashan
Bill

You wrote in message news:[hidden email]...

> I've always thought of the user interface process of being just that ---
> a single process. At least it would act that way, even if the VM was
> using some slight of hand to pull off some tricks. I basically thought,
> that if one didn't use "fork" in your application, that you didn't have
> to worry about concurrent access issues. I found a case where this
> breaks down when using a MessageBox.
> [...snip...]
> I hadn't put any mutex's on the cached data, because I thought of the UI
> as being single threaded. But MessageBox>>basicOpen does a "Processor
> forkMainIfMain".

Yes this is one of the less satisfactory aspects of the way Dolphin manages
multiple top-level native windows on a single OS thread, yet still
maintaining correct (synchronous) behavior in the handling of messages, and
permitting multiple modal dialogs. Ideally one would have one native OS
thread per top-level Window, since this is the way Windows is designed to
work - when a modal dialog is opened, the calling thread will usually
disable the owner window and then go into a "local" message loop to service
the dialog. This works fine for the thread-per-window model, as other UI
threads can continue to service their associated windows without being
affectged. If however there is only a single UI thread, then one can really
only permit a single modal dialog to be opened when using a modal message
loop. To see what I mean, try the following:

1) Modify the DialogView>>showModalTo: class to send #runModalInProcessLoop
in place of #runModalLoop
2) Open a class browser, and bring up the find class prompter
3) Do the same with another class browser
4) Go back to the first class browser and prompter, and type in a class name
to search for, save View, press Enter to close the dialog, noting that
nothing happens!
5) Now go back to the second class browser, and search for another class.
Note that when the second dialog is dismissed, the first browser eventually
gets to complete the find operation.

Essentially what this demonstrates is that the dialogs can't really be
closed out of order if a modal in-process message loop is used, and hence
the illusion of multiple independent top-level windows is lost.

Other alternatives are even more unsatisfactory, for example:
1) Permit only a single modal dialog at a time. I think this would be pretty
poor - and in fact it was, since this was the way some of the very early
versions of Dolphin worked.
2) Implement an architecture with multiple "green" UI threads, one per
top-level Window, dequeueing messages from the real Windows message queue,
and pushing them off onto the internal queues of the green UI threads. This
seems like a neat solution, but in fact if one does this, then one can never
really get correct Windows behaviour, since a significant proportion of
Windows messages are supposed to be handled synchronously - that is further
events from the queue should not be retrieved and dispatched, until one has
completed processing for those that occurred earlier. Handling the messages
asynchronously would destroy any pretensions of tight (and correct) Win32
integration, and make it impossible to debug many things properly, or at
all, at the Smalltalk level.

So we're left with starting a new UI process when opening a modal dialog,
and blocking the old one, and unfortunately this does mean that process
synchronisation issues can arise unexpectedly. Where this is a problem with
MessageBoxes, you have the option of flagging the MB as #taskModal. If you
do this then it opens modal to all Windows, using a modal message loop, so a
new UI process does not need to be started. Another technique is to make use
of #queueDeferredAction: to push the processing back into the context of the
(new) UI process.

As an aside you may be wondering why, when the dialog is closed, the old UI
process that was blocked when the dialog was opened, doesn't just reaassert
itself over the new UI process. I can't quite remember the reason, so I will
have to think about it.

> ....
> I see that using DialogViews with #showModal, is also forking the UI
> process. But in DialogView>>runModalLoop it is incrementing the priority
> of the process, if it had been the main process. The comment says:
> "Boost the initiating UI processes priority so that the operation
> requested by the user completes more quickly when the dialog is closed."
>
> It seems like the net effect would be that the code following the modal
> dialog would run without being interrupted by the (now lower priority)
> main UI process working on things from Windows input queue.

Yes, that is the effect. Even if some other higher priority activity (e.g. a
time firing) causes the processes to be rescheduled, Dolphin will always run
the highest priority process that is ready to run, to the exclusion of any
other lower priority processes.

>...Does the
> event that terminates the modal dialog come through Windows input queue?

Yes....

> If so, then the main UI process would presumably not be in the middle of
> something else when the higher priority code after the dialog kicks in.
> If this analysis is accurate (please comment if you know), then overall,
> it still acts as if the UI was a single process. Is that right?

...so the UI process does get blocked until the processing following the
modal dialog completes, unless that processing should block due to waiting
on a Mutex/Semaphore.

>
> I tried the same priority boosting trick in MessageBox>>basicOpen after
> the #forkMainIfMain. And my particular problem went away. Is this a
> recommended change to make? Should it be included in the base?

Yes, I think it should. Its not ideal, but like I say, a pragmatic solution.

Regards

Blair