setNeedsDisplay broken in Tiger

  • I have a thread that wants to tell a view to update and then the
    thread must wait until the view is updated before continuing. This
    thread worked fine until Tiger. The code fragment is

    [myView setNeedsDisplay:YES]
    NSLog(@"needsDisplay is now %d",(int)[myView needsDisplay]);      //
    it shows "1"
    [myView displayIfNeeded];            // force update (but was not
    needed before Tiger)
    while(YES)
    {    [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:
    0.2]];
          if([myView needsDisplay]==NO) break;
    }

    But the loop that waits until the view is updated never exits. I
    checked and drawRect is being called and successfully finished, but
    needsDisplay is never set back to NO. This was all fine until
    compiling in Tiger. What has changed in Tiger about updating views in
    a thread? Or is there another way to have a thread wait until a view
    has completed an update?

    ----------------
    John Nairn (1-801-581-3413, FAX: 1-801-581-4816)
    Web page: http://www.mse.utah.edu/~nairn
  • On May 24, 2005, at 9:31 AM, John Nairn wrote:

    > I have a thread that wants to tell a view to update and then the
    > thread must wait until the view is updated before continuing. This
    > thread worked fine until Tiger. The code fragment is
    >
    > [myView setNeedsDisplay:YES]
    > NSLog(@"needsDisplay is now %d",(int)[myView
    > needsDisplay]);      // it shows "1"
    > [myView displayIfNeeded];            // force update (but was not
    > needed before Tiger)
    > while(YES)
    > {    [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:
    > 0.2]];
    > if([myView needsDisplay]==NO) break;
    > }
    >
    > But the loop that waits until the view is updated never exits. I
    > checked and drawRect is being called and successfully finished, but
    > needsDisplay is never set back to NO. This was all fine until
    > compiling in Tiger. What has changed in Tiger about updating views
    > in a thread? Or is there another way to have a thread wait until a
    > view has completed an update?

    This shouldn't have ever worked.  These messages are not thread-safe,
    and must be sent on the main thread.  In general, Cocoa isn't thread-
    safe except in places where the documentation explicitly says it is,
    and those NSView messages are not exceptions.

    As currently documented, ONLY the following NSView messages are
    thread-safe:

    -beginDocument
    -lockFocusIfCanDraw
    -lockFocusIfCanDrawInContext:

    -bob
  • On 24-May-05, at 12:31 PM, John Nairn wrote:

    > Or is there another way to have a thread wait until a view has
    > completed an update?

    The best way is to use NSLock to synchronize the threads.
    But I think you can get away with using your own flag - e.g. with a
    BOOL variable declared as 'volatile' and depend on the atomicity of
    reading & writing this variable.

    --
    Cameron Hayne
    <hayne...>
  • On May 24, 2005, at 9:31 AM, John Nairn wrote:
    > I have a thread that wants to tell a view to update and then the
    > thread must wait until the view is updated before continuing. This
    > thread worked fine until Tiger. The code fragment is
    >
    > [myView setNeedsDisplay:YES]
    > NSLog(@"needsDisplay is now %d",(int)[myView
    > needsDisplay]);      // it shows "1"
    > [myView displayIfNeeded];            // force update (but was not
    > needed before Tiger)
    > while(YES)
    > {    [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:
    > 0.2]];
    > if([myView needsDisplay]==NO) break;
    > }
    >
    > But the loop that waits until the view is updated never exits. I
    > checked and drawRect is being called and successfully finished, but
    > needsDisplay is never set back to NO. This was all fine until
    > compiling in Tiger. What has changed in Tiger about updating views
    > in a thread? Or is there another way to have a thread wait until a
    > view has completed an update?
    >
    > ----------------
    > John Nairn (1-801-581-3413, FAX: 1-801-581-4816)
    > Web page: http://www.mse.utah.edu/~nairn

    As Bob Ippolito pointed out, messaging views from a secondary thread
    is in general not thread-safe and therefore not a recommended
    practice, on any version of Mac OS X.

    If you must force a view to be redrawn synchronously, and block on
    completion of the redraw, you can have your secondary thread send the
    view a "display" message on the main thread:

        [someControllerObject performSelectorOnMainThread:@selector
    (displayTheView:) withObject:myView waitUntilDone:YES];

    where someControllerObject implements:

        - (void)displayTheView:(id)theView {
            [theView display];
        }

    Regarding the -needsDisplay behavior you are seeing: Is "myView"
    fully visible, or is part of it clipped by an ancestor view (e.g. an
    NSScrollView/NSClipView)?  A view may be marked as needing display
    anywhere within its bounds, including areas that are not within its
    current "visibleRect" (areas, for example, that are currently
    scrolled out of view).  It is therefore possible for -needsDisplay to
    report YES after a view's visible area has been redrawn, if occluded
    portions of the view remain invalidated.  On Tiger, this state is
    respected and preserved more consistently than it was on Panther.

    --
    Troy Stephens
    Cocoa Frameworks
    Apple Computer, Inc.
  • On May 24, 2005, at 11:32 AM, Troy Stephens wrote:
    > As Bob Ippolito pointed out, messaging views from a secondary
    > thread is in general not thread-safe and therefore not a
    > recommended practice, on any version of Mac OS X.

    To clarify a bit: There _are_ some aspects of view functionality that
    are OK to use in a multithreaded manner.  For example, locking focus
    to draw in a view on a secondary thread is supported.

    The -setNeedsDisplay: and -setNeedsDisplayInRect: operations,
    however, are not thread-safe, and should therefore always be
    performed on the application's main thread.

    --
    Troy Stephens
    Cocoa Frameworks
    Apple Computer, Inc.
  • On May 24, 2005, at 4:01 PM, Troy Stephens wrote:

    > On May 24, 2005, at 11:32 AM, Troy Stephens wrote:As Bob Ippolito
    > pointed out, messaging views from a secondary thread is in general not
    > thread-safe and therefore not a recommended practice, on any version
    > of Mac OS X.
    > To clarify a bit: There _are_ some aspects of view functionality that
    > are OK to use in a multithreaded manner.  For example, locking focus
    > to draw in a view on a secondary thread is supported.
    >
    > The -setNeedsDisplay: and -setNeedsDisplayInRect: operations, however,
    > are not thread-safe, and should therefore always be performed on the
    > application's main thread.

    Interesting contrast with Java AWT, where the repaint() method on
    Component is one of the very few AWT/Swing operations which *is*
    explicitly thread safe and can be called from any thread.

    Of course, they achieve that by queuing repaint events on the AWT event
    queue and thus actually firing the paint from the AWT Thread "some time
    later" (thus it really is serial, but the application developer can
    ignore that). This does allow them to do repaint coalescing to cut down
    on unnecessary paints.

    How interesting that -setNeedsDisplay: is exactly the opposite; it
    would seem a perfect candidate for some kind of queue... I'd love to
    know why it's that way :)

    AndyT (lordpixel - the cat who walks through walls)
    A little bigger on the inside

            (see you later space cowboy ...)
  • On May 24, 2005, at 9:43 PM, Andrew Thompson wrote:

    >
    > On May 24, 2005, at 4:01 PM, Troy Stephens wrote:
    >
    >
    >> On May 24, 2005, at 11:32 AM, Troy Stephens wrote:As Bob Ippolito
    >> pointed out, messaging views from a secondary thread is in general
    >> not thread-safe and therefore not a recommended practice, on any
    >> version of Mac OS X.
    >> To clarify a bit: There _are_ some aspects of view functionality
    >> that are OK to use in a multithreaded manner.  For example,
    >> locking focus to draw in a view on a secondary thread is supported.
    >>
    >> The -setNeedsDisplay: and -setNeedsDisplayInRect: operations,
    >> however, are not thread-safe, and should therefore always be
    >> performed on the application's main thread.
    >>
    >
    > Interesting contrast with Java AWT, where the repaint() method on
    > Component is one of the very few AWT/Swing operations which *is*
    > explicitly thread safe and can be called from any thread.
    >
    > Of course, they achieve that by queuing repaint events on the AWT
    > event queue and thus actually firing the paint from the AWT Thread
    > "some time later" (thus it really is serial, but the application
    > developer can ignore that). This does allow them to do repaint
    > coalescing to cut down on unnecessary paints.
    >
    > How interesting that -setNeedsDisplay: is exactly the opposite; it
    > would seem a perfect candidate for some kind of queue... I'd love
    > to know why it's that way :)

    It's not that way, you're mistaken.  Calling setNeedsDisplay: doesn't
    cause anything to happen immediately either, but it's not safe to
    call from random threads whenever you want to.  Read the docs, when a
    view is set as needing display, it will be displayed during the next
    pass of the event loop.

    Cocoa's threading model is quite simple: unless something is
    explicitly thread safe, it's almost certainly not.  If it happens to
    be, pretend it's not, because that's an implementation detail that's
    subject to change.  Threads should be rather isolated from one
    another in general; so if you find yourself wishing that more of
    Cocoa was thread safe, then you should probably learn some more about
    preemptive multithreading first.

    -bob
  • On May 25, 2005, at 3:07 AM, Bob Ippolito wrote:

    > It's not that way, you're mistaken.  Calling setNeedsDisplay: doesn't
    > cause anything to happen immediately either, but it's not safe to call
    > from random threads whenever you want to.  Read the docs, when a view
    > is set as needing display, it will be displayed during the next pass
    > of the event loop.

    That makes perfect sense, it just leaves me wondering why what's
    presumably such a simple method wouldn't be thread safe. One wonders if
    it's an efficiency thing - assuming it's just a boolean flag, then
    you'll have problems unless NSLock (or possibly volatile) is used to
    make the changes visible across threads. Or maybe it's something else
    entirely. That's what I'm curious about, but I doubt anyone at Apple is
    going to say :)

    > Cocoa's threading model is quite simple: unless something is
    > explicitly thread safe, it's almost certainly not.  If it happens to
    > be, pretend it's not, because that's an implementation detail that's
    > subject to change.  Threads should be rather isolated from one another
    > in general; so if you find yourself wishing that more of Cocoa was
    > thread safe, then you should probably learn some more about preemptive
    > multithreading first.

    The exact same rule applies to AWT/Swing in Java for precisely the same
    reasons. Very few methods are documented thread safe.
    Many things in fact do work from multiple threads, but, especially if
    you're targeting multiple platforms, you're better off behaving as if
    they're not thread safe and doing everything from the AWT thread, which
    plays the role of the main thread in AppKit.

    So you're preaching to the choir here. I understand the principle, and
    whilst it would be nice if more things in both toolkits (Cocoa,
    AWT/Swing) were thread safe, it's certainly no silver bullet! I'm just
    curious about the reasons behind things.

    AndyT (lordpixel - the cat who walks through walls)
    A little bigger on the inside

            (see you later space cowboy ...)
  • Andrew,

    On 25.5.2005, at 16:00, Andrew Thompson wrote:

    > ... it would be nice if more things in both toolkits (Cocoa, AWT/
    > Swing) were thread safe

    I am not that sure myself. Thread-safeness tends to be costly, and,
    in a vast majority of cases, actually not needed at all: although
    there definitely are tasks to be served best by threads, very very
    often it is *much* better just to use event loop properly.
    ---
    Ondra ÄŒada
    OCSoftware:    <ocs...>              http://www.ocs.cz
    private        <ondra...>            http://www.ocs.cz/oc
  • On May 25, 2005, at 7:00 AM, Andrew Thompson wrote:

    >> It's not that way, you're mistaken.  Calling setNeedsDisplay:
    >> doesn't cause anything to happen immediately either, but it's not
    >> safe to call from random threads whenever you want to.  Read the
    >> docs, when a view is set as needing display, it will be displayed
    >> during the next pass of the event loop.
    >>
    >
    > That makes perfect sense, it just leaves me wondering why what's
    > presumably such a simple method wouldn't be thread safe. One
    > wonders if it's an efficiency thing - assuming it's just a boolean
    > flag, then you'll have problems unless NSLock (or possibly
    > volatile) is used to make the changes visible across threads. Or
    > maybe it's something else entirely.

    Well, to truly call it "thread safe," you'd need to handle other
    threads interacting with that HIView at the same time as your code
    interacts with it--i.e. what if you call setNeedsDisplay while
    another thread is currently rendering, resizing, moving, or otherwise
    altering the view? You have to account for anything that might put
    the NSView object in an unstable state.
previous month may 2005 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
MindNode
MindNode offered a free license !