[squeak-dev] Debugging of other processes with wait

Jakob Reschke forums.jakob at resfarm.de
Thu Jul 23 16:57:12 UTC 2020


Hi Eliot,

Am Do., 23. Juli 2020 um 16:37 Uhr schrieb Eliot Miranda
<eliot.miranda at gmail.com>:
>
> > On Jul 23, 2020, at 5:50 AM, Jakob Reschke <forums.jakob at resfarm.de> wrote:
> >
> > Hello,
> >
> > Yesterday I needed to debug a process that is not the UI process. At
> > one point this background process waits on a Promise (so it will wait
> > on a Semaphore) that is supposed to be resolved in the UI process, so
> > the background process will block. If this point is reached while
> > stepping in the debugger, the UI process will block instead, since it
> > evaluates the debugger stepping on behalf of the background process.
>
> Can you describe the control flow when this occurs normally, outside the debugger?  I need to understand which processes execute waits.
>

There is a "background" process that implements a certain workflow.
There are one or more occasions during this workflow where user input
is required. To move away from modal dialogs, nesting the world loop,
and from writing all the business logic in a promise-style control
flow, I want this background process to suspend until the user has
provided the input (which happens in the UI process of course).

So:
1. The background process triggers a request in the UI process via
addDeferredUIMessage: or similar. Mind that this is not a modal
invocation, so it will return immediately.
2. The background process waits for the input. In this particular case
using Promise>>wait, which uses a Semaphore internally.
3. When the user is finished filling in the data, clicking the OK or
Cancel buttons (which happens in the UI process) must somehow signal
the background process to continue. In my current case, this happens
by fulfilling the Promise on which the background process waits. This
will signal the internal Promise Semaphore from the UI process.
4. The background process receives the signal on the Semaphore. It
resumes to get the user's input from the Promise and continues the
workflow.

Instead of a Promise, other mechanisms could be used as well:
- Use a Semaphore directly without Promise.
- Let the background process read from a shared queue.
- Let the background process suspend itself and be resumed from the UI
callback explicitly.


> > What I did for now is to add an accessor on ProcessScheduler that
> > answers the activeProcess without sending #effectiveProcess, and check
> > in Promise>>wait whether this real active process is the UI process.
> > If it is, halt the debugged process before the Semaphore wait
> > primitive, and I proceed manually as soon as I am confident that the
> > promise is already resolved or rejected, so the wait will return
> > immediately.
>
> An alternative might be to have the debugger spawn a new UI process whenever it detects that the UI process is blocked.  This may have to be done by a background helper process that the debugger spawns or allows to run whenever the debugger steps, because of course the ui process is executing the step.
>
> Without thinking things through, another alternative might be to have the debugger always use its own process.  The underlying issue is that if the ui process blocks in reality then it should block in the debugger, but we’d like to keep the debugger alive and we’d like to keep the ui alive.
>
> So I suppose we could introduce a background process whose job it is to observe the ui process and if it detects the ui process is blocked for some time (1 second?) spawns a new ui process had reports (in the Transcript?, a pop-up in the new ui process?) that the ui process blocked.
>

This sounds good at first and for my particular case. But in general
this might break some things even outside of a debugging session:
modal dialogs are allergic to switching the UI process (I guess
everyone has seen Monticello's "This modal dialog was interrupted,
please close it" message at some point). The other thing I can imagine
is that after blocking a few seconds the no-longer-UI process might do
something that needs to be done in the UI process, but won't use
addDeferredUIMessage: because the code was designed to run in the UI
process in the first place.

If we can get around this, an always responsive UI would be great of course.

> >
> > wait
> >    "Wait unconditionally for this promise to become fulfilled or rejected."
> >    | sema |
> >    (state = #pending and: [Processor realProcess == Project
> > uiProcess]) ifTrue: [self halt].
> >    sema := Semaphore new.
> >    ...
> >
> > But of course this is just a workaround. Is there a chance to extend
> > the Squeak debugger to deal with critical sections and waits?
>
> I don’t see how.  The debugger can spot a critical section entry or a wait if one is doing “send”, because then the debugger actually executes the primitive in Context>>#tryPrimitive:..., (IIRC, I’m on my phone).  But if the debugger steps then the critical section entry or wait will be executed by running code (the debugger runs step at full speed by using perform: and the code executed by the perform, while surrounded by an exception handler and an unwind-protect, is not virtualised in any way and so we have no easy way of intercepting critical section entry or waits).
>

We could add an AboutToWait notification around the code that calls
the primitive... but it would slow down processes that don't need it.
And if you add another blocking mechanism and forget to signal the
notification, the debugger will block again.

> > In
> > Eclipse for example, there is a list of threads; blocked/waiting
> > threads are annotated with an icon, as well as the top frame that
> > waits for something; you can debug other threads in the meantime. But
> > in Java the debugger is in another VM than the debugged process, so
> > the situation that the debugger steps in the UI process needs not be
> > resolved there in the first place.
> >
> > Maybe if we know all the points/primitives where processes can be
> > blocked, we could make the stepping detect these, run them in another
> > process, make the debugger observe that process and show some info
> > about the waiting in a special top context in the meantime. Once the
> > other process is unblocked (or terminated if it does nothing else than
> > the wait), the debugger would continue to step the background process.
> > Could this work?
> >
> > Or is there already another facility that works and which I should use?
>
> As I say, I think a way that can work is detecting that the ui process has blocked and spawning a new ui process when that happens.  But when in the debugger we want to avoid the debugger blocking in step/send and hence only executing any actions before step/send and not those after step/send (eg removing exception handler and unwind-protect from the stack).  So architecturally, not having the debugger use the ui process and to use its own process seems like a good idea but a major change.
>

Agreed.



More information about the Squeak-dev mailing list