Is LLVM compiler an optimization bug?

I have an interesting issue related to the optimization level of the LLVM compiler. I use:

  • Xcode 8.2.1
  • LLVM 8.0

It is better to explain this with example code. I turned the problem into a simple objective-c class. First see the code below:

@interface Foo()  {
    BOOL is_loading;
}
@end

@implementation Foo

- (void)test {

    printf("started loading \n");

    // set loading flag to YES
    is_loading = YES;

    // schedule a timer to fire in 2 seconds, to simulate the end of loading
    [NSTimer scheduledTimerWithTimeInterval:2.0
                                     target:self
                                   selector:@selector(timerFired)
                                   userInfo:nil
                                    repeats:NO];

    // wait asynchronously until loading flag is set to NO
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

        while (is_loading) {
            // loop until timer event modifies is_loading flag 
        }

        printf("finished loading \n");
    });
}

- (void)timerFired {

    printf("timer fired \n");

    // set loading flag to NO
    is_loading = NO;

}

@end

      

If you instantiate the class Foo

and call the method load

, it will simulate the loading progress and asynchronously observe the flag is_loading

to determine if the loading has finished.

And after that, the console output will look like this:

started loading
timer fired
finished loading  

      

But if you enable compiler optimizations, you will see this output:

started loading
timer fired

      

Apparently the while loop never ends, and execution cannot reach the next printf () message.

Am I missing an obvious reason for this, or could it be an optimizer bug?

+3


source to share


2 answers


As Apple states on the page, the compiler may not load the variable more than once when the code is optimized. He doesn't know that he might be edited from another thread, so this happens.



Marking the variable as volatile

will force the compiler to load the value every time it needs to, and it won't.

+2


source


Even Sami's answer is the answer to your Q question, perhaps misleading.

Since variables volatile

are not thread safe, your whole approach can fail when blocks form two different access streams is_loading

. The use is volatile

not intended for thread synchronization.



Use GCD semaphores instead.

+1


source







All Articles