lmi
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [lmi] Wakeful sleep


From: Greg Chicares
Subject: Re: [lmi] Wakeful sleep
Date: Mon, 11 Nov 2013 16:03:02 +0000
User-agent: Mozilla/5.0 (Windows NT 5.1; rv:17.0) Gecko/20130801 Thunderbird/17.0.8

On 2013-11-08 14:23Z, Vadim Zeitlin wrote:
> On Fri, 08 Nov 2013 14:03:43 +0000 Greg Chicares <address@hidden> wrote:
> 
> GC> Ideally, this pause would have two properties:
> GC> 
> GC> (1) The GUI still updates normally.
> ...
> GC> (2) Except for the minimal activity in (1), no resources are consumed.
> ...
> GC> How should I do this with wx? In HEAD (revision 5821), I'm just calling
> GC> wxSleep(), which satisfies (2) but not (1). This alternative seems okay:
> GC> 
> GC>     for(int i = 0; i < 10 * seconds_to_pause; ++i)
> GC>         {
> GC>         wxMilliSleep(100);
> GC>         wxTheApp && wxTheApp->Yield(true);
> GC>         }
> GC> 
> GC> but is there a better way?
> 
>  This loop is not a bad solution for this particular problem, especially if
> you use wxWindowDisabler to disable all the other windows except the one
> which is supposed to accept input. It's not totally clear to me whether
> this window is a wxProgressDialog or something else. If it is a
> wxProgressDialog, then you actually could just call its Update() method
> instead which calls Yield() internally already and also checks for the
> Cancel button presses.

It is indeed implemented in terms of wxProgressDialog. In current HEAD,
using Update() as you suggest...

    std::ostringstream oss;
    oss.precision(1);
    oss << std::fixed;
    for(int i = 10 * seconds; 0 < i && !progress_dialog_.WasCancelled(); --i)
        {
        wxMilliSleep(100);
        oss.str("");
        oss << "Waiting " << 0.1 * i << " seconds for printer";
        progress_dialog_.Update(count(), oss.str());
        }

...has several advantages:

(1) Within wx, Update() calls YieldFor() selectively: better than the
wxApp::Yield() I had previously used.

(2) Using Update() to display the remaining wait time shows the user that
lmi is still working properly, and hasn't "frozen".

(3) With WasCancelled() as part of the loop condition, pressing Cancel
has a nearly immediate effect.

>  The perfect solution wouldn't use Sleep() at all but would just disable
> everything except the Cancel button and start a timer with a period of 10
> seconds which would re-enable the rest of the UI on its expiration. But
> this would require more extensive changes.

Now that you mention it, yes, of course an event-driven application spends
most of its time idle, consuming no resources. I was so focused on the idea
of calling sleep() that I overlooked that. But I suppose the implementation
above, with wxProgressDialog::Update(), is a solid alternative; and it's
similar to what the command-line and CGI interfaces do.

>  Finally, let me remind you about the dangers of Yield(): when using it,
> events can be dispatched in unforeseen circumstances and while the use of
> wxWindowDisabler mitigates this for most types of the events, you should be
> very careful if you have any "background" events, such as (other) timers,
> threads, or IO operations (LMI doesn't use the latter, but I'm not sure
> about the first two).

lmi remains single-threaded. (It's responsive enough as it is, and I never
have to worry about threading issues.)

There is one background timer, though, which resets the FPU control word
if it has been changed:

// Member of wxApp-derived class
    wxTimer               timer_;

// Started in wxApp-derived class's constructor:
    timer_.Start(100);
// and connected in OnInit() to wxEVT_TIMER

// Entire body of wxEVT_TIMER handler:
    if(0 == fenv_guard::instance_count())
        {
        if(!fenv_is_valid())
            {
            status() << "Resetting floating-point control word. " << std::flush;
            }
        fenv_validate(e_fenv_indulge_0x027f);
        }

> Typical solution is to have a global "blocking" flag
> which would be set before calling Yield() and checked by all the handlers
> for these events to avoid (usually fatal) reentrancies which would result
> from dispatching these events from inside this code.

Class fenv_guard above works like wxWindowUpdateLocker--if I create an
fenv_guard object on the stack, then the condition in the wxEVT_TIMER
handler fails and it returns without doing anything. Should I use that
guard around the wxMilliSleep...Update code above? It seems weird to
do that--fenv_guard usually guards critical sections of calculations,
when there's a lot of processing going on, whereas in this case there's
really nothing happening. However, the FPU reset code, which is called
through the timer when it's not guarded, can potentially write to the
statusbar or pop up a messagebox...so should I use a guard throughout
this "sleep" function? or even whenever a wxProgressDialog is active?




reply via email to

[Prev in Thread] Current Thread [Next in Thread]