Problem with updating NSProgressIndicator

  • Hello
    I work on Cocoa application that uses posix thread to perform big
    calculations. I need to display the progress. Big caculations are done
    with C++ and I have a callback function that called by the calculating
    thread with the current progress (structure with data). I put this
    structures to the queue (std::vector, guarded by the mutex). This is
    the separate thread.

    Now the main thread: I need to check my queue and (among other things)
    update the progress. So I created NSTimer (with period of 0.1 second)
    that checks my queue and (among other things) calls -setDoubleValue
    for NSProgressIndicator. Maybe it looks a bit tricky, but it is just a
    marshalling of structures from one thread to another, it worked fine
    for me in Windows, it is tested etc....

    Now the problem: progress indicator freezes. It doesn't even play
    animation! It may move to 30% and freeze there. I tried
    -setNeedsDisplay, -displayIfNeeded, even -display - they all don't
    help. Progress doesn't move, but if I click the window at this moment
    - progress returns to life and moves as it have to.

    I tried to call -animate for progress in every timer cycle. In that
    case progress animates but doesn't move!

    I added NSLog() calls to make sure the code is called - it does. I
    pass absolutely correct values. I placed NSTextField near the progress
    and added -setDoubleValue call with the same value for the text field
    - it updates, but progress is still frozen. I checked the thread where
    I update progress - it is the main thread.

    I tried to move progress update from timer to selector that was called
    by performSelectorFromMainThread - doesn't help too.

    While searching the Internet I found similar problems, but all of them
    were related to main loop's block. Mine is not blocked - all
    calculations performed in the separate thread. Calling -display that
    always helps, doesn't help to me.

    One more thing: I tried to replace simple -setDoubleValue with the pair:
    [progress setDoubleValue:0];
    [progress setDoubleValue:nActualValue];
    this helps, progress moves, but animation does not work anyway...

    The problem... Ideas?
  • I recently wrote a blog post with a demo xcode project that shows an
    implementation of updating a progress indicator for file copy. You can
    see it here: http://www.cimgf.com/2008/05/03/cocoa-tutorial-file-copy-with-progress-indi
    cator/


    It's a lowest common denominator example, so it might give you some
    insight. Hope it helps.

    -Matt

    On May 20, 2008, at 12:53 AM, Vitaly Ovchinnikov wrote:

    > Hello
    > I work on Cocoa application that uses posix thread to perform big
    > calculations. I need to display the progress. Big caculations are done
    > with C++ and I have a callback function that called by the calculating
    > thread with the current progress (structure with data). I put this
    > structures to the queue (std::vector, guarded by the mutex). This is
    > the separate thread.
    >
    > Now the main thread: I need to check my queue and (among other things)
    > update the progress. So I created NSTimer (with period of 0.1 second)
    > that checks my queue and (among other things) calls -setDoubleValue
    > for NSProgressIndicator. Maybe it looks a bit tricky, but it is just a
    > marshalling of structures from one thread to another, it worked fine
    > for me in Windows, it is tested etc....
    >
    > Now the problem: progress indicator freezes. It doesn't even play
    > animation! It may move to 30% and freeze there. I tried
    > -setNeedsDisplay, -displayIfNeeded, even -display - they all don't
    > help. Progress doesn't move, but if I click the window at this moment
    > - progress returns to life and moves as it have to.
    >
    > I tried to call -animate for progress in every timer cycle. In that
    > case progress animates but doesn't move!
    >
    > I added NSLog() calls to make sure the code is called - it does. I
    > pass absolutely correct values. I placed NSTextField near the progress
    > and added -setDoubleValue call with the same value for the text field
    > - it updates, but progress is still frozen. I checked the thread where
    > I update progress - it is the main thread.
    >
    > I tried to move progress update from timer to selector that was called
    > by performSelectorFromMainThread - doesn't help too.
    >
    > While searching the Internet I found similar problems, but all of them
    > were related to main loop's block. Mine is not blocked - all
    > calculations performed in the separate thread. Calling -display that
    > always helps, doesn't help to me.
    >
    > One more thing: I tried to replace simple -setDoubleValue with the
    > pair:
    > [progress setDoubleValue:0];
    > [progress setDoubleValue:nActualValue];
    > this helps, progress moves, but animation does not work anyway...
    >
    > The problem... Ideas?
  • That's a very strange problem. Ordinarily, even if the main thread
    were hung, the progress bar would still show its animation (that's
    done on the "UI heartbeat thread".) And the way it updates when you
    click implies something's messed up with window-redraw behavior.

    My first guess is that your code is calling AppKit unsafely from a
    background thread. But it sounds like your other thread only does
    calculations. Any other threads that might be making AppKit calls?

    Second guess: something throwing an exception for some reason. Check
    the console output for any exception logs. Set a symbolic breakpoint
    at objc_exception_throw (on 10.5) or NSRaiseError (10.4) and see if it
    gets hit.

    —Jens
  • On May 19, 2008, at 11:53 PM, Vitaly Ovchinnikov wrote:

    > I tried to move progress update from timer to selector that was called
    > by performSelectorFromMainThread - doesn't help too.

    Does this comment mean that you're currently calling -setDoubleValue:
    from a background thread? That's, AFAIK, not supported. You need to
    change this, and any other calls that you do on UI components, to the
    main thread. This is likely the source of your problems.

    Verify that your background threads never make any thread unsafe
    calls, and use -performSelectorOnMainThread: to forward your status
    updates to the main thread for display.
    If you still have this problem, I think that the next step would be
    for you to create a small sample project that reproduces the problem
    and then sending a link to the list so that we can check it out.

    On May 20, 2008, at 6:40 AM, Matt Long wrote:

    > I recently wrote a blog post with a demo xcode project that shows an
    > implementation of updating a progress indicator for file copy. You
    > can see it here: http://www.cimgf.com/2008/05/03/cocoa-tutorial-file-copy-with-progress-indi
    cator/


    Two things:

    * The "context" pointer of your callback function is designed to allow
    you to pass information about the object that is interested in the
    callback. In that way you don't have to do things like creating a
    separate static variable for the progress indicator. I find this to be
    a "cleaner", or at least more object oriented, solution.

    * If you're not blocking the main thread you don't have to call -
    displayIfNeeded. The "if needed" part probably doesn't make much sense
    here in any case - You know that it's needed, you just updated the
    value. If you need to force display you should call -display, but you
    should only have to do this in exceptional situations. It is much more
    common to flag a control as needing to update display, in which case
    you would call -setNeedsDisplay:. In this case though, you shouldn't
    have to do either, as the call to -setDoubleValue: will flag the
    control as needing display.

    j o a r
  • http://www.appsforlife.com/progress.zip (36Kb)
    Idea is simple:

    - (void) awakeFromNib
    {
      [progress setHidden:YES];
      timer = [NSTimer init.....];
    }

    - (void) onTimer: (id) timer
    {
      [progress setDoubleValue:...];
      [progress setHidden:NO]; // once or every timer cycle - doesn't
    matter (in real project I call this once)
    }

    if we call -setHidden before -setDoubleValue - all work fine. In
    attached project progress freezes immediately after start. At every
    run.
    Ideas?
  • On Mon, May 19, 2008 at 11:53 PM, Vitaly Ovchinnikov
    <vitaly.ovchinnikov...> wrote:
    > Hello
    > I work on Cocoa application that uses posix thread to perform big
    > calculations. I need to display the progress. Big caculations are done
    > with C++ and I have a callback function that called by the calculating
    > thread with the current progress (structure with data). I put this
    > structures to the queue (std::vector, guarded by the mutex). This is
    > the separate thread.
    >
    > Now the main thread: I need to check my queue and (among other things)
    > update the progress. So I created NSTimer (with period of 0.1 second)
    > that checks my queue and (among other things) calls -setDoubleValue
    > for NSProgressIndicator. Maybe it looks a bit tricky, but it is just a
    > marshalling of structures from one thread to another, it worked fine
    > for me in Windows, it is tested etc....
    >
    > Now the problem: progress indicator freezes. It doesn't even play
    > animation! It may move to 30% and freeze there.

    If you send your progress view a setUsesThreadedAnimation: message
    with a value of YES do it continue to animate?

    If it does then you are somehow blocking your main thread.

    -Shawn
  • > If you send your progress view a setUsesThreadedAnimation: message
    > with a value of YES do it continue to animate?

    Just tried to send this message in my test project - it doesn't animate.
    I removed almost all code, including all my C++ code. Finally it takes
    about 10 lines to hang a progress ;)
    http://www.appsforlife.com/progress.zip
  • On Tue, May 20, 2008 at 1:54 PM, Vitaly Ovchinnikov
    <vitaly.ovchinnikov...> wrote:

    > Ideas?

    What version of Mac OS X are you running? What version of Xcode are you using?

    -Shawn
  • > What version of Mac OS X are you running? What version of Xcode are you
    > using?

    Mac OS X 10.4.11 (running at G4)
    XCode 2.0 (IDE - 514, Core - 515, ToolSupport - 514)
  • On Tue, May 20, 2008 at 1:59 PM, Vitaly Ovchinnikov
    <vitaly.ovchinnikov...> wrote:
    >> If you send your progress view a setUsesThreadedAnimation: message
    >> with a value of YES do it continue to animate?
    >
    > Just tried to send this message in my test project - it doesn't animate.
    > I removed almost all code, including all my C++ code. Finally it takes
    > about 10 lines to hang a progress ;)
    > http://www.appsforlife.com/progress.zip

    It is working fine for me using Mac OS X 10.5 and Mac OS X 10.4 when
    compiled with Xcode 2.5 against the 10.4u SDK (not tried any other
    version of Xcode or SDK).

    -Shawn
  • > It is working fine for me using Mac OS X 10.5 and Mac OS X 10.4 when
    > compiled with Xcode 2.5 against the 10.4u SDK (not tried any other
    > version of Xcode or SDK).

    well, it seems that I know what my mac will download tonight ;)
    thanks for testing, I'll try to download and install 2.5
  • On Tue, May 20, 2008 at 2:06 PM, Vitaly Ovchinnikov
    <vitaly.ovchinnikov...> wrote:
    >> It is working fine for me using Mac OS X 10.5 and Mac OS X 10.4 when
    >> compiled with Xcode 2.5 against the 10.4u SDK (not tried any other
    >> version of Xcode or SDK).
    >
    > well, it seems that I know what my mac will download tonight ;)
    > thanks for testing, I'll try to download and install 2.5

    Well on my 8th or so try on a 10.4 system I got it to do something
    like what I think you are reporting. If you double-click launch the
    application and do NOT move the mouse, click the mouse button, or
    press any key on the keyboard the progress UI will not start moving.
    As soon as you generate an input event of some type then it appears to
    jump ahead to the expected progress position (aka the timer appears to
    be firing in the background) and the bar animation runs as expected.

    -Shawn
  • > Well on my 8th or so try on a 10.4 system I got it to do something
    > like what I think you are reporting. If you double-click launch the
    > application and do NOT move the mouse, click the mouse button, or
    > press any key on the keyboard the progress UI will not start moving.
    > As soon as you generate an input event of some type then it appears to
    > jump ahead to the expected progress position (aka the timer appears to
    > be firing in the background) and the bar animation runs as expected.

    Yes, that's it. But I can reproduce it at every run. Just Command+R at
    XCode at here it is.
    Do I need to report this to Apple? Or what?
  • > It is working fine for me using Mac OS X 10.5 and Mac OS X 10.4 when
    > compiled with Xcode 2.5 against the 10.4u SDK (not tried any other
    > version of Xcode or SDK).

    just installed XCode 2.5 and tried the demo project - result is the
    same, progress freezes.
    I have a slow computer, maybe the reason is here. it is G4 1.4GHz with
    1 Gb RAM...
  • Hi, there,

    I apologize for not being able to offer an "Ah-ha!!!" sort of solution
    immediately, and also for this very long e-mail.  I don't know if my
    suggestions will help, but they may at least lead to cleaner code, and
    may perhaps solve your problems.  If I may...

    First, you may wish to rename your class to "VOMainController" or some
    such -- use a two-letter prefix (one that Apple doesn't use -- and "V"
    and "O" appear to be your initials), as directed by Apple (as far as I
    know) to prevent naming collisions, and give it a descriptive name
    that represents what it is -- "Wnd" seems to be to represent "window,"
    but your class is neither an NSWindowController nor a subclass of
    NSWindow/NSPanel, and "controller" is a generic class name suffix for
    such a -- well, for such a controller (hence the convention).

    Secondly, instead of using a static xxx = 0 declaration, I'd use an
    instance variable, and give it a name that will be clear what it
    represents, then initialize it in your -init method.  Also, name it
    "steps" (or something like that).  Also, *you haven't specified what
    xxx is*!!!  I don't know what it defaults to -- but "static xxx = 0"
    doesn't explicitly make it into an int, a double, or a char -- it may
    (or may not be) compiler specific.  Thus do give it a type.

    Thirdly, by making giving the onTimer local variable the name "timer",
    you have just created a conflict with your instance variable "timer".
    So I'd rename the *local* one to "aTimer".

    Additionally (and this might, or might not, help with your crash), you
    might want to retain your timer, in case it ever gets removed from the
    runloop but you wish to keep a reference to it.  (Not sure why you'd
    do that, but it's good form to retain any ivars that are a subclass of
    NSObject, unless you're using Garbage Collection under Leopard.)

    Also: you call [progress setHidden:NO] every time that the timer
    fires.  This may prove inefficient later on, so I'd simply leave it
    unhidden.

    My version of your code follows.  Note that it was typed in Mail.app,
    so there may be an error or two.  It should be fairly good to go,
    though.  If you still have the freezing problem, try creating a *new*
    project under Xcode 2.5, if you haven't already, instead of importing
    your old one.  That might do the trick, if some build settings got
    corrupted.

    // === BEGIN INTERFACE CODE ===

    @interface VOMainController : NSObject {
    unsigned int steps; // on Leopard you can use "NSUInteger" instead of
    "unsigned int"
    NSTimer *timer;
    IBOutlet NSProgressIndicator *progress;
    }

    // I include these prototypes to show that we've overridden them
    - (id)init;
    - (void)dealloc;
    - (void)awakeFromNib

    // now for your own method:
    - (void)onTimer:(NSTImer *)aTimer;

    @end

    // === END INTERFACE CODE ===

    // === BEGIN IMPLEMENTATION CODE ===

    @implementation VOMainController

    - (id)init {
    self = [super init];
    if (self) {
      steps = 0;
    }
    return self;
    }

    - (void)dealloc {
    // there have been debates on this mailing list as to the usefulness of
    // nullifying instance variables (in this case your timer variable);
    I do it
    // since I *feel* it to be good form, even if it appears to be a
    matter of taste

    [timer invalidate]; // very important
    [timer release]; // since we retained earlier
    timer = nil;  //

    [super dealloc];
    }

    - (void)awakeFromNib
    {
    timer = [[NSTimer scheduledTimerWithTimeInterval:0.1
      target:self
      selector:@selector(onTimer:)
      userInfo:nil
      repeats:YES]
      retain];

    //[progress setHidden:YES];
    }

    - (void)onTimer:(NSTimer *)aTimer
    {
    steps = (steps + 1) % 100;
    [progress setDoubleValue:steps];
    //[progress setHidden:NO];
    }

    @end

    // === END IMPLEMENTATION CODE ===

    Cheers,
    Andrew

    On May 20, 2008, at 2:52 PM, Vitaly Ovchinnikov wrote:

    >> It is working fine for me using Mac OS X 10.5 and Mac OS X 10.4 when
    >> compiled with Xcode 2.5 against the 10.4u SDK (not tried any other
    >> version of Xcode or SDK).
    >
    > just installed XCode 2.5 and tried the demo project - result is the
    > same, progress freezes.
    > I have a slow computer, maybe the reason is here. it is G4 1.4GHz with
    > 1 Gb RAM...
  • > I apologize for not being able to offer an "Ah-ha!!!" sort of solution
    > immediately, and also for this very long e-mail.  I don't know if my
    > suggestions will help, but they may at least lead to cleaner code, and may
    > perhaps solve your problems.  If I may...

    Thanks for your suggestions, but the goal of this example was to show
    the problem instead of showing a clean code ;) In real project class
    names are better, trees are bigger and the sun is brighter. The only
    "real" thing to check is a missed -retain call for timer, but I
    already tried it - it gave nothing.
  • one more topic to comment:
    > Also: you call [progress setHidden:NO] every time that the timer fires.
    > This may prove inefficient later on, so I'd simply leave it unhidden.
    real code is a bit more complex: if user changed some parameters, I
    start the "big calculation" and show the progress bar. Among this I
    resizes other controls a bit to free some place in the window for
    progress indicator. The problem is in the order of calls
    -setDoubleValue and -setHidden.

    In my example -setHidden calls look unnecessary, but they need to
    reproduce the problem.
  • On 20 May '08, at 2:30 PM, Vitaly Ovchinnikov wrote:

    > Yes, that's it. But I can reproduce it at every run. Just Command+R at
    > XCode at here it is.

    I can't reproduce it at all on 10.5.2. I've tried launching in Xcode
    and from the Finder, and being careful not to click/type/move the
    mouse after launching.

    The code looks OK to me.

    > Do I need to report this to Apple? Or what?

    Not unless you can reproduce it on 10.5 somehow. The only thing Apple
    would fix in 10.4 nowadays is some kind of critical security bug or
    catastrophic system error.

    —Jens
  • >> Do I need to report this to Apple? Or what?
    >
    > Not unless you can reproduce it on 10.5 somehow. The only thing Apple would
    > fix in 10.4 nowadays is some kind of critical security bug or catastrophic
    > system error.

    Well, actually I already submitted this problem to apple. Maybe
    they'll take a look.
    Anyway, should I "forget" about Tiger? I want to release my app this
    summer and I don't think that it is a good idea to drop support of the
    previous OS. Or it is OK for Mac world? (In Windows world we have to
    support at least XP and Vista)
  • On 20 May '08, at 9:16 PM, Vitaly Ovchinnikov wrote:

    > Anyway, should I "forget" about Tiger? I want to release my app this
    > summer and I don't think that it is a good idea to drop support of the
    > previous OS.

    Some apps are Leopard-only, usually because they require new APIs
    (like Core Animation.) But I think most current apps still support
    Tiger.

    > Or it is OK for Mac world? (In Windows world we have to support at
    > least XP and Vista)

    Mac users are more likely than Windows users to keep their OS up to
    date, for a number of reasons (the OS is much easier to update, Apple
    does a good job of hyping each release, the new versions really are
    better than the old, apps require the new OS because they're adopting
    new features...) And the users that don't update the OS are the ones
    who don't tend to buy new software of any kind.

    It's up to you what OS you want to support, a trade-off between
    development/testing time and market size.

    —Jens
  • Thank you.

    > It's up to you what OS you want to support, a trade-off between
    > development/testing time and market size.
previous month may 2008 next month
MTWTFSS
      1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31  
Go to today