Confused by a ToGo GUI app that may be *too* simple

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

Confused by a ToGo GUI app that may be *too* simple

Chris Uppal-3
Hi,

I'm having minor problems getting my head around the right way to write a very
simple ToGo GUI app that has an unusually simple UI.

Like the app I mentioned yesterday, it just sends some data to a server by
(blocking) TCP/IP , reads the response, and (optionally) displays it to the
user.  The catch is that the GUI is built entirely out of message boxes.

It either starts up and immediately does a MessageBox>>warning: if there's some
problem with the settup.  Or else it starts up, sends the data, and then does a
MessageBox>>notify: when the data comes back from the server.  In either case
it exits as soon as the user clicks OK on the popup box.

I've got it to the point where it works, sort of, but I've been adding stuff
more-or-less at random and I don't know if I'm missing something or doing too
much.

First, I vaguely remember some post by Blair (which I can't find now, not even
on Google) in which he said something about doing a #forkMainIfMain if using
MessageBoxes before the GUI system was settup.  I'm calling #forkMainIfMain
from my app's MySessionManager>>main, before I do anything else, but I suspect
that it's making no difference.  Is it necessary here ?

Second, I'm not sure how the make the app exit early (e.g, if the wrong
parameters have been set).  I'm calling #exit: 2, *and* doing an early return
from #main, *and* setting a flag for my #keepAlive method (see below), and it
seems as if I'm probably doing more than I need to, just to exit an
application.

Third, I've overridden #keepAlive so that it just checks a flag to see if the
#main has finished (possibly early-outed), if the flag is set then it calls
#quit: with the same exit status that was recorded when a call to #exit: was
made (I overrode #exit: to save the status).  Is that necessary, I know I need
a #keepAlive method, but could I make it simpler and just always ignore it,
relying on the call to #exit: to ensure that the app dies at the right time ?
Alternatively, would the right thing be to set the flag and early-out from
main, and rely on #keepAlive calling #quit:  (and what is the difference
between #quit: and #exit: anyway) ?

Lastly, I'm getting a warning written to the Windows debug stream:
    WARNING: no processes are Ready to run
in the period between my sending the data to the server and getting a response.
I *think* that means that the idle process hasn't been set up properly, in
which case there's an architectural problem with the way I'm doing things.  The
app does work, however, so is that a warning I should try to do anything about
?  If so what ?

Thanks in advance for any help or insight.

    -- chris


Reply | Threaded
Open this post in threaded view
|

Re: Confused by a ToGo GUI app that may be *too* simple

Maxim Fridental
> It either starts up and immediately does a MessageBox>>warning: if there's
some
> problem with the settup.  Or else it starts up, sends the data, and then
does a
> MessageBox>>notify: when the data comes back from the server.  In either
case
> it exits as soon as the user clicks OK on the popup box.
Is your problem that you can't use MessageBox before you show a Shell in the
deployed app? If yes try the following:
(MessageBox new)

taskModal;

caption: 'myCap';

errorMsg: 'myMessage'.

I have no idea why it works.


Reply | Threaded
Open this post in threaded view
|

Re: Confused by a ToGo GUI app that may be *too* simple

Blair McGlashan
In reply to this post by Chris Uppal-3
Chris

You wrote in message news:3ee6f1cb$0$45175$[hidden email]...
> Hi,
>
> I'm having minor problems getting my head around the right way to write a
very
> simple ToGo GUI app that has an unusually simple UI.
> ....
> I've got it to the point where it works, sort of, but I've been adding
stuff
> more-or-less at random and I don't know if I'm missing something or doing
too
> much.
>
> First, I vaguely remember some post by Blair (which I can't find now, not
even
> on Google) in which he said something about doing a #forkMainIfMain if
using
> MessageBoxes before the GUI system was settup.  I'm calling
#forkMainIfMain
> from my app's MySessionManager>>main, before I do anything else, but I
suspect
> that it's making no difference.  Is it necessary here ?

No. It is true that you would need to do that if using a non-task modal
message box at some point in the startup processing _before_ #main. This is
because all the system startup happens on the single high priority startup
thread. This thread is the same one as that which was running when the image
was saved (i.e. the old main thread), although this is immaterial. As one of
the last actions in the startup the new main UI thread is started. Your
override of #main is called from this thread as the result of a deferred
action being processed when the message loop first becomes empty. You can
safely open a message box from #main, or any subsidiary function, which can
readily be demonstrated by modifying HelloWordSessionManager>>main as
follows, and then deploying and running the resulting .exe:

main
 (MessageBox confirm: 'Are you sure you want to say hello')
  ifTrue: [self mainShellClass new show position: 0 @ 0]

Non-task modal message boxes cause problems in early startup stages (or
after shutdown has been initiated), because they implicitly start another UI
process so they can block the one that wants to open the message box. This
is undesirable because it may mean processing occurring before the system
has properly initialised itself, or (if on shutdown) it might allow further
processing to occur rather too late in the life of the session. You can use
message boxes at these times by making them task modal - these start up a
nested message loop, and effectively block the UI thread while allowing
repainting etc. However it is generally not necessary to intervene in the
startup or shutdown activities of the session manager, and from what you say
you are not doing that anyway.

>
> Second, I'm not sure how the make the app exit early (e.g, if the wrong
> parameters have been set).  I'm calling #exit: 2, *and* doing an early
return
> from #main, *and* setting a flag for my #keepAlive method (see below), and
it
> seems as if I'm probably doing more than I need to, just to exit an
> application.
>
> Third, I've overridden #keepAlive so that it just checks a flag to see if
the
> #main has finished (possibly early-outed), if the flag is set then it
calls
> #quit: with the same exit status that was recorded when a call to #exit:
was
> made (I overrode #exit: to save the status).  Is that necessary, I know I
need
> a #keepAlive method, but could I make it simpler and just always ignore
it,
> relying on the call to #exit: to ensure that the app dies at the right
time ?
> Alternatively, would the right thing be to set the flag and early-out from
> main, and rely on #keepAlive calling #quit:  (and what is the difference
> between #quit: and #exit: anyway) ?

In the above example the application will exit if you press 'No'. This is
because the standard keep alive processing will detect the absence of any
windows and automatically shut down the system. Since you have no windows,
you need to prevent this happenning. As you know, the way to do this is to
override #keepAlive, and it is easiest to just override this to do nothing
if you have some point you can explicitly trigger shutdown.

SessionManager>>exit: posts a WM_QUIT message to the application. This is
the "approved" way to terminate a Windows application, and in Dolphin this
is detected in the message loop (see InputState>>pumpMessage:), and results
in a #onQuit: event being sent back to the SessionManager. The important
point about is this is that the WM_QUIT is only a request, and it is
possible to override #onQuit: to prompt the user or otherwise decline the
request - any message box here should be taskModal to avoid getting more
than one of them popping up BTW. See DevelopmentSessionManager>>onQuit: for
an example.

Note that #onQuit: may also get invoked when Windows itself is shutting
down, and again it is possible to veto this shutdown by returning false from
#onQuit:. Generally speaking one does not need to differentiate between the
end of the Windows session and normal application shutdown - certainly the
development system does not, and prompts to save the image using the same
mechanism in both cases.

So the difference between #quit: and #exit: is that the latter posts a
WM_QUIT to the message loop that may result in the former being called if
the application accepts the request to exit. #quit: calls the VM primitive
which actually shuts down the system.

In your case even though you don't have any Windows, I would still call
#exit:, since the WM_QUIT will cleanly close any windows open in your app
(message boxes, hidden windows, etc), and the route it takes will be the
same as that for windows session termination which you need to allow for
anyway. You probably won't need to override #onQuit:, etc.

>
> Lastly, I'm getting a warning written to the Windows debug stream:
>     WARNING: no processes are Ready to run
> in the period between my sending the data to the server and getting a
response.
> I *think* that means that the idle process hasn't been set up properly, in
> which case there's an architectural problem with the way I'm doing things.
The
> app does work, however, so is that a warning I should try to do anything
about
> ?  If so what ?

It does indeed mean that the VM found that no processes were in a runnable
state. This will only happen if the idle process gets blocked for some
reason. It causes the VM to send an idle panic interrupt to the Processor
global, which gets forwarded to InputState>>idlePanic, the instance of
InputState being the owner of main UI and idle processes. InputState
responds by starting a new idle process. You could insert some debug at this
point to dump out the stack of the current idler onto the system debug trace
stream - I'd do this with outputDebugString() directly, rather than using
the Trace global, as that is an instance of DebugTraceStream which has a
mutex protecting against more than one process trying to output at once. You
probably don't want to cause more process system primitives to be called
when handling an idle panic interrupt!

You might find the issue goes away if you take out the #forkMainIfMain
anyway.

In summary:
    1) Remove the #forkMainIfMain
    2) Override SessionManager>>keepAlive to do nothing (hmmm, the keep
alive method is actually responsible for killing the system, bit of bad
naming that)
    3) Call #exit: to shut down when you are ready.
    4) Be aware that non-task modal message boxes will cause a new main UI
process to be started, so use a task modal box when this may be undesirable.
In your case you might want to just use these by default, since the main
purpose of the other sort is to avoid going into a "mode" when multiple
windows are displayed.

Regards

Blair


Reply | Threaded
Open this post in threaded view
|

Re: Confused by a ToGo GUI app that may be *too* simple

Steve Alan Waring
In reply to this post by Chris Uppal-3
Hi Chris,

As Maxim mentioned, using a TaskModal MessageBox will stop the application
immediately exiting.

> First, I vaguely remember some post by Blair (which I can't find now,
> not even on Google) in which he said something about doing a
> #forkMainIfMain if using MessageBoxes before the GUI system was
> settup.  I'm calling #forkMainIfMain from my app's
> MySessionManager>>main, before I do anything else,

I think you have this around the wrong way. Anything that causes a new main
process to be forked can cause the application to exit early if there are no
visibleWindows. (See InputState>>aboutToIdle). Showing a non-taskModal
MessageBox or sending #forkMainIfMain will cause a new main to be forked.
Even if you were not sending #forkMainIfMain yourself, if you are using a
Socket, and it blocks, then it will send #forkMainIfMain (See
Socket>>waitOn:)

The solution is to either make sure you show your shell before using a
socket, or as you have done, take manual control with your own
implementation of #keepAlive.

> Second, I'm not sure how the make the app exit early (e.g, if the
> wrong parameters have been set).  I'm calling #exit: 2, *and* doing
> an early return from #main, *and* setting a flag for my #keepAlive
> method (see below), and it seems as if I'm probably doing more than I
> need to, just to exit an application.

In a similar situation, I just send #exit:.

> Third, I've overridden #keepAlive so that it just checks a flag to
> see if the #main has finished (possibly early-outed), if the flag is
> set then it calls #quit: with the same exit status that was recorded
> when a call to #exit: was made (I overrode #exit: to save the
> status).  Is that necessary, I know I need a #keepAlive method, but
> could I make it simpler and just always ignore it, relying on the
> call to #exit: to ensure that the app dies at the right time ?
> Alternatively, would the right thing be to set the flag and early-out
> from main, and rely on #keepAlive calling #quit:

I have an application where I set a flag to stop #keepAlive quiting at
certain stages during startup/shutdown. I decided not to always ignore it
because it seemed like a good backup to ensure that my process would
terminate if something unexpected occurs.

> (and what is the
> difference between #quit: and #exit: anyway) ?

I believe that #exit is more graceful because it first calls PostQuitMessage
and responds to the resulting WM_QUIT by sending #quit (in
InputState>>pumpMessage:) #quit then does the graceful shutdown of the
image. I am not sure what the consequences are of not calling the
PostQuitMessage function, but I have done it plenty of times without seeing
any obvious problems.

>
> Lastly, I'm getting a warning written to the Windows debug stream:
>     WARNING: no processes are Ready to run

I am also seeing this.

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


Reply | Threaded
Open this post in threaded view
|

Re: Confused by a ToGo GUI app that may be *too* simple

Chris Uppal-3
Steve,

> > Second, I'm not sure how the make the app exit early (e.g, if the
> > wrong parameters have been set).  I'm calling #exit: 2, *and* doing
> > an early return from #main, *and* setting a flag for my #keepAlive
> > method (see below), and it seems as if I'm probably doing more than
> > I need to, just to exit an application.
>
> In a similar situation, I just send #exit:.

That didn't seem to be enough.  My #main just went on processing after the call
to #exit.  I have to early-out from #main *and* call #exit..

> I have an application where I set a flag to stop #keepAlive quiting at
> certain stages during startup/shutdown. I decided not to always
> ignore it because it seemed like a good backup to ensure that my
> process would terminate if something unexpected occurs.

That's a good point.  What I've now ended up with is something like:

main
    [self exit: (self realMain). ^ self]
        on: Exception
        do: [... default handling...].
    self exit: 3.

realMain
    "answers exit status"
    (..a check..) ifFalse: [^ 1].
    (..another check..) ifFalse: [^ 1].
    ... real processing ...
    ^ (... processing worked..)
        ifTrue: [0]
        ifFalse: [1].

where I've overriden #exit: to set the "I've finished" flag before doing a
super-send.  #keepAlive just checks the flag and does a #quit: if the flag is
set.  Seems to work OK, and doesn't feel nearly as clumsy as what I had before.

I think if I needed to exit directly from deeper into the call chain from #main
(i.e. without having entered the message-loop), then I'd throw a special
ExitException in order to get straight back to the #main.  Fortunately I don't
have to bother in this case.


> > Lastly, I'm getting a warning written to the Windows debug stream:
> >     WARNING: no processes are Ready to run
>
> I am also seeing this.

Thanks for the confirmation.  I now suspect it to be a bug.  See my response to
Blair.

    -- chris


Reply | Threaded
Open this post in threaded view
|

Re: Confused by a ToGo GUI app that may be *too* simple

Chris Uppal-3
In reply to this post by Blair McGlashan
Blair,

Thank you for the information and clarifications.


> [...] You can safely
> open a message box from #main, or any subsidiary function

Good.  The call to #forkMainIfMain goes on the scrap heap.


> In the above example the application will exit if you press 'No'.
> This is because the standard keep alive processing will detect the
> absence of any windows and automatically shut down the system. Since
> you have no windows, you need to prevent this happening. As you
> know, the way to do this is to override #keepAlive, and it is easiest
> to just override this to do nothing if you have some point you can
> explicitly trigger shutdown.

Just to add.  Without the #keepAlive override, the app didn't die at any
reliable time.  Sometimes it died before sending the message, sometimes it was
able to stay around long enough to send the message but not read the response,
sometimes it ran to completion.  I'm assuming that that's what would be
expected if the absence of windows is detected by a different Process.


> > Lastly, I'm getting a warning written to the Windows debug stream:
> >     WARNING: no processes are Ready to run
> > in the period between my sending the data to the server and getting
> > a response. [...]
>
> It does indeed mean that the VM found that no processes were in a
> runnable state. This will only happen if the idle process gets
> blocked for some reason. It causes the VM to send an idle panic
> interrupt to the Processor global, which gets forwarded to
> InputState>>idlePanic, the instance of InputState being the owner of
> main UI and idle processes. InputState responds by starting a new
> idle process. You could insert some debug at this point [...]

Quite a bit of tracing later, I'm convinced that the problem is that the
SessionManager hasn't started an idle process at all.  I've added:
    self inputState ensureIdlerRunning.
at the start of my #main, and the problem goes away completely.  A bug ?  Or
something that I just didn't realise that I was expected to do myself ?

On the way, I found that I was getting sporadic errors generated, so I think
there may be a real bug here.  It seemed, when it happened at all, to be
something going wrong as a new Idler was started after the panic.

I added some tracing to the relevant methods of InputState that sent the
Process name and method names to the debug stream as they were invoked; I also
added the Process name to my own logging. Here's an annotated trace of the app
deciding to exit with an error popup (rather than send any data to the server
at all):

(1st column is time in seconds, 2nd column is thread name, rest is logging)

#    We start here, the first messages are from InputState
#    before my #main is entered.
#    Note that there is no call to InputState>>forkIdler.
#    The second line comes from logging added to
#    the
#            idler isNil ifFalse: [...]
#    clause in #primaryStartup.
0.00000000 Main: in: InputState>>primaryStartup
0.00526911 Main: Killing existing idler
0.00857455 Main: in: InputState>>guiStartup
0.00980851 Main: in: InputState>>forkMain
0.01004681 Main: in: InputState>>aboutToForkUI

#    Now we are in my #main.  The first thing I do is change
#    the process name to 'Snippet' (the name of my app) so
#    it doesn't get confused with any new # Process called main.
0.01048066 Snippet: Startup -- Changed Process name to 'Snippet'

#    Now snippet decides it has invalid arguments so it should
#    show a popup error message and exit.
#    Showing the popup causes the normal forking of a new
#    main Process.
0.01084048 Snippet: About to show message: ...
0.01115449 Snippet: in: InputState>>forkMain
0.01211048 Snippet: in: InputState>>aboutToForkUI

#    Now Snippet has gone idle waiting for the response from the user.
#    At this point the VM causes an idle panic
0.02256879 WARNING: No processes are Ready to run

#    We are in #idlePanic, the second line shows that the 'idler'
#    instvar of the InputState is nil.  However the Process
#    that is fielding the VM interrupt is called 'Idler'.  I don't
#    understand this.
0.02298002 Idler: in: InputState>>idlePanic
0.02315350 Idler: Idler is: nil

#    Now we are in #forkIdler.  Sometimes that worked as
#    you would expect and start a new idler, other times,
#    as on this occasion, it would throw an exception and
#    write a dump file.
#    NB: 'InputState>>idlePanic' is the name of the Process
#    here.
0.02348176 InputState>>idlePanic: in: InputState>>forkIdler
0.02413798 Dolphin: Writing dump to '....ERRORS'

#    Some Process is now starting a new idleLoop.
#    I don't know if this is the one mentioned above,
#    or the one forked from InputState>>forkIdler which
#    has been given a new name.
0.16015527 Idler: in: InputState>>idleLoop

#    At this point there's a new popup to inform me that:
#        Primitive Process>>primTerminate failed (2)
#    I (the user) cancel that, and my application exits
3.76724304 Snippet: Exit with: 2
3.76770036 Idler: in: InputState>>ensureMainRunning

And that's the end of the log.  I get a similar sort of log if I actually do
send data to the server, the only difference is that the idle panic occurs when
the app's waiting for the response.  It is similar in that it *sometimes*
causes the above error when it tries to start the new idler.  I never did see
the error without the tracing, which further suggests that there's some sort
of timing issue here.

In both cases, the generated dump file is similar.  As it say, the whole
problem goes away if I do an #ensureIdlerRunning early on in my #main.  So
maybe the problem is just that there is no idler, but it does seem odd that
starting one causes sporadic errors.  The dump file follows, sorry about the
munged formatting.

    -- chris


************************** Dolphin Virtual Machine Dump Report
***************************

12:57:10, 12/06/2003: Primitive Process>>primTerminate failed (2)

*----> VM Context <----*
Process: {07430004:size 214 words, suspended frame 07430351, priority 1,
callbacks 0
last failure 2:nil, FPE mask 3, thread nil}
Active Method: SessionManager>>logError:
IP: 0736C81D (13)
SP: 07430408
BP: 074303E0 (231)
ActiveFrame: {074303E4: cf 074303C9, sp 074303F8, bp 074303E0, ip 5,
SnippetGUISessionManager(SessionManager)>>logError:}
 receiver: a SnippetGUISessionManager
 arg[0]: a Error


New Method: VMLibrary>>dump:path:stackDepth:walkbackDepth:
Message Selector: #dump:path:stackDepth:walkbackDepth:

*----> Stack Back Trace <----*
{074303E4: cf 074303C9, sp 074303F8, bp 074303E0, ip 5,
SnippetGUISessionManager(SessionManager)>>logError:}
 receiver: a SnippetGUISessionManager
 arg[0]: a Error

{074303C8: cf 074303AD, sp 074303D8, bp 074303C4, ip 4,
SnippetGUISessionManager(SessionManager)>>unhandledException:}
 receiver: a SnippetGUISessionManager
 arg[0]: a Error

{074303AC: cf 07430391, sp 074303BC, bp 074303A8, ip 4,
SnippetGUISessionManager(SessionManager)>>onUnhandledError:}
 receiver: a SnippetGUISessionManager
 arg[0]: a Error

{07430390: cf 07430379, sp 074303A0, bp 07430390, ip 5, Error>>defaultAction}
 receiver: a Error

{07430378: cf 07430365, sp 07430388, bp 07425D68, ip 57,
Error(Exception)>>_propagateFrom:}
 receiver: a Error
 arg[0]: a ExceptionHandler
 temp[0]: nil
 temp[1]: a ExceptionHandler
 temp[2]: nil
 temp[3]: a Process('Idler' base 07430000 [ACTIVE] in SessionManager>>logError:
sp=00000000 ip=8 list=nil)
 temp[4]: nil

{07430364: cf 07430349, sp 07430374, bp 07430360, ip 6,
Error(Exception)>>_propagate}
 receiver: a Error
 temp[0]: nil

{07430348: cf 07430331, sp 07430358, bp 07430348, ip 12,
Error(Exception)>>signal}
 receiver: a Error

{07430330: cf 07430315, sp 07430340, bp 0743032C, ip 7,
Error(Exception)>>signal:}
 receiver: a Error
 arg[0]: 'Primitive Process>>primTerminate failed (2)'

{07430314: cf 074302F9, sp 07430324, bp 07430310, ip 5, Error class(Exception
class)>>signal:}
 receiver: Error
 arg[0]: 'Primitive Process>>primTerminate failed (2)'

{074302F8: cf 074302DD, sp 07430308, bp 074302F4, ip 4,
Process(Object)>>error:}
 receiver: a Process('Idler' base 07430000 [ACTIVE] in
SessionManager>>logError: sp=00000000 ip=8 list=nil)
 arg[0]: 'Primitive Process>>primTerminate failed (2)'

{074302DC: cf 074302C1, sp 074302EC, bp 074302D8, ip 21,
Process(Object)>>primitiveFailed}
 receiver: a Process('Idler' base 07430000 [ACTIVE] in
SessionManager>>logError: sp=00000000 ip=8 list=nil)
 temp[0]: a Process('Idler' base 07430000 [ACTIVE] in SessionManager>>logError:
sp=00000000 ip=8 list=nil)

{074302C0: cf 074302A9, sp 074302D0, bp 074302C0, ip 8, Process>>primTerminate}
 receiver: a Process('Idler' base 07430000 [ACTIVE] in
SessionManager>>logError: sp=00000000 ip=8 list=nil)

{074302A8: cf 07430291, sp 074302B8, bp 074302A8, ip 15, Process>>shutdown}
 receiver: a Process('Idler' base 07430000 [ACTIVE] in
SessionManager>>logError: sp=00000000 ip=8 list=nil)

{07430290: cf 07430279, sp 074302A0, bp 07430290, ip 10, Process>>kill}
 receiver: a Process('Idler' base 07430000 [ACTIVE] in
SessionManager>>logError: sp=00000000 ip=8 list=nil)

{07430278: cf 0743025D, sp 07430288, bp 07430274, ip 6,
ProcessorScheduler>>kill:}
 receiver: a ProcessorScheduler
 arg[0]: nil

{0743025C: cf 07430241, sp 0743026C, bp 074259E8, ip 11, [] in
ProcessorScheduler>>vmi:list:no:with:}
 receiver: a ProcessorScheduler
 arg[0]: 532
 arg[1]: a LinkedList
 arg[2]: 9
 arg[3]: nil

{07430240: cf 0743022D, sp 07430258, bp 07425A90, ip 17,
BlockClosure>>ifCurtailed:}
 receiver: [] @ 9535494 in nil
 arg[0]: [] @ 16 in ProcessorScheduler>>vmi:list:no:with:
 temp[0]: nil
 temp[1]: nil
 temp[2]: nil

{0743022C: cf 07430219, sp 0743023C, bp 074259E8, ip 20,
ProcessorScheduler>>vmi:list:no:with:}
 receiver: a ProcessorScheduler
 arg[0]: 532
 arg[1]: a LinkedList
 arg[2]: 9
 arg[3]: nil

{07430218: cf 07430201, sp 07430228, bp 07430218, ip 1,
InputState>>isInputAvailable}
 receiver: a InputState

{07430200: cf 074301E9, sp 07430210, bp 07430200, ip 19, InputState>>idleNT}
 receiver: a InputState

{074301E8: cf 074301D1, sp 074301F8, bp 074301E8, ip 16, InputState>>idleLoop}
 receiver: a InputState

{074301D0: cf 074301BD, sp 074301E0, bp 0742B118, ip 18, [] in
InputState>>forkIdler}
 receiver: a InputState
 temp[0]: nil

{074301BC: cf 074301A9, sp 074301CC, bp 07428B80, ip 11,
ExceptionHandler(ExceptionHandlerAbstract)>>markAndTry}
 receiver: a ExceptionHandler
 temp[0]: nil

{074301A8: cf 0743018D, sp 074301B8, bp 074298B0, ip 21, [] in
ExceptionHandler(ExceptionHandlerAbstract)>>try:}
 receiver: a ExceptionHandler
 arg[0]: [] @ 16 in InputState>>forkIdler
 temp[0]: a ExceptionHandler
 temp[1]: nil
 temp[2]: a Process('Idler' base 07430000 [ACTIVE] in SessionManager>>logError:
sp=00000000 ip=8 list=nil)

{0743018C: cf 07430179, sp 074301A4, bp 07429C30, ip 17,
BlockClosure>>ifCurtailed:}
 receiver: [] @ 9535494 in nil
 arg[0]: [] @ 34 in ExceptionHandlerAbstract>>try:
 temp[0]: nil
 temp[1]: nil
 temp[2]: nil

{07430178: cf 07430159, sp 07430188, bp 07430170, ip 4, BlockClosure>>ensure:}
 receiver: [] @ 15 in ExceptionHandlerAbstract>>try:
 arg[0]: [] @ 34 in ExceptionHandlerAbstract>>try:
 temp[0]: nil

{07430158: cf 07430145, sp 07430168, bp 074298B0, ip 39,
ExceptionHandler(ExceptionHandlerAbstract)>>try:}
 receiver: a ExceptionHandler
 arg[0]: [] @ 16 in InputState>>forkIdler
 temp[0]: a ExceptionHandler
 temp[1]: nil
 temp[2]: a Process('Idler' base 07430000 [ACTIVE] in SessionManager>>logError:
sp=00000000 ip=8 list=nil)

{07430144: cf 07430125, sp 07430154, bp 0743013C, ip 7, BlockClosure>>on:do:}
 receiver: [] @ 16 in InputState>>forkIdler
 arg[0]: a Signal
 arg[1]: [] @ 25 in InputState>>forkIdler

{07430124: cf 07430109, sp 07430134, bp 0742B118, ip 37, [] in
InputState>>forkIdler}
 receiver: a InputState
 temp[0]: nil

{07430108: cf 074300F5, sp 07430120, bp 07429D90, ip 17,
BlockClosure>>ifCurtailed:}
 receiver: [] @ 9535494 in nil
 arg[0]: [] @ 42 in InputState>>forkIdler
 temp[0]: nil
 temp[1]: nil
 temp[2]: nil

{074300F4: cf 074300E1, sp 07430104, bp 0742B118, ip 53, [] in
InputState>>forkIdler}
 receiver: a InputState
 temp[0]: nil

{074300E0: cf 074300CD, sp 074300F0, bp 0742B448, ip 11,
ExceptionHandler(ExceptionHandlerAbstract)>>markAndTry}
 receiver: a ExceptionHandler
 temp[0]: nil

{074300CC: cf 074300B1, sp 074300DC, bp 07429C50, ip 21, [] in
ExceptionHandler(ExceptionHandlerAbstract)>>try:}
 receiver: a ExceptionHandler
 arg[0]: [] @ 8 in InputState>>forkIdler
 temp[0]: nil
 temp[1]: nil
 temp[2]: a Process('Idler' base 07430000 [ACTIVE] in SessionManager>>logError:
sp=00000000 ip=8 list=nil)

{074300B0: cf 0743009D, sp 074300C8, bp 07429A10, ip 17,
BlockClosure>>ifCurtailed:}
 receiver: [] @ 9535494 in nil
 arg[0]: [] @ 34 in ExceptionHandlerAbstract>>try:
 temp[0]: nil
 temp[1]: nil
 temp[2]: nil

{0743009C: cf 0743007D, sp 074300AC, bp 07430094, ip 4, BlockClosure>>ensure:}
 receiver: [] @ 15 in ExceptionHandlerAbstract>>try:
 arg[0]: [] @ 34 in ExceptionHandlerAbstract>>try:
 temp[0]: nil

{0743007C: cf 07430069, sp 0743008C, bp 07429C50, ip 39,
ExceptionHandler(ExceptionHandlerAbstract)>>try:}
 receiver: a ExceptionHandler
 arg[0]: [] @ 8 in InputState>>forkIdler
 temp[0]: nil
 temp[1]: nil
 temp[2]: a Process('Idler' base 07430000 [ACTIVE] in SessionManager>>logError:
sp=00000000 ip=8 list=nil)

{07430068: cf 07430049, sp 07430078, bp 07430060, ip 7, BlockClosure>>on:do:}
 receiver: [] @ 8 in InputState>>forkIdler
 arg[0]: ProcessTermination
 arg[1]: [] @ 12 in BlockClosure>>newProcess

{07430048: cf 00000001, sp 07430058, bp 07428BB0, ip 17, [] in
BlockClosure>>newProcess}
 receiver: [] @ 8 in InputState>>forkIdler
 temp[0]: nil

<Bottom of stack>

***** End of dump *****