简体   繁体   中英

LLVM compiler - Is this an optimization bug?

I have an interesting problem related to the optimization level of LLVM compiler. I'm using:

  • Xcode 8.2.1
  • LLVM 8.0

It is better to explain it with an example code. I boiled down the problem into a simple objective-c class. Please see the code below first:

@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 class Foo and call load method, it will simulate a loading progress and asynchronously observe is_loading flag to determine if the loading is finished.

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

started loading
timer fired
finished loading  

But if you turn on the compiler optimization, you will see this output instead:

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 to happen, or can it be an optimizer bug?

As Apple states on their synchronization page the compiler may well not load the variable more than once when the code is optimized. It doesn't know that it may be edited from another thread so this happens.

Marking the variable as volatile will force the compiler to load the value each time it's needed and this won't happen.

Even Sami's answer is an answer to your Q, it maybe is misleading.

Since volatile variables are not thread safe, your whole approach can fail, when blocks form two different threads access is_loading . The usage of volatile is not intended for thread synchronization.

Use GCD semaphores instead.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM