LLVM编译器 - 这是一个优化错误吗?

时间:2017-04-05 15:09:51

标签: ios objective-c xcode llvm compiler-optimization

我有一个与LLVM编译器的优化级别相关的有趣问题。我正在使用:

  • Xcode 8.2.1
  • LLVM 8.0

最好用示例代码解释它。我将问题归结为一个简单的Objective-c类。请先看下面的代码:

@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

如果您实例化类Foo并调用load方法,它将模拟加载进度并异步观察is_loading标志以确定加载是否已完成。

之后,控制台输出将如下:

started loading
timer fired
finished loading  

但是如果你打开编译器优化,你会看到这个输出:

started loading
timer fired

显然while循环永远不会结束,执行无法到达下一个printf()消息。

我是否错过了发生这种情况的明显原因,或者它是否是优化错误?

2 个答案:

答案 0 :(得分:2)

当Apple在synchronization page上声明时,编译器在优化代码时可能不会多次加载变量。它不知道它可以从另一个线程编辑,所以这发生了。

将变量标记为volatile将强制编译器在每次需要时加载值,这不会发生。

答案 1 :(得分:1)

即使萨米的回答也是对你问答的回答,也许会产生误导。

由于volatile变量线程安全,当块形成两个不同的线程访问is_loading时,整个方法可能会失败。 volatile的用法用于线程同步。

改为使用GCD信号量。