objective-c - macOS 10.14、Xcode 9:即使使用 @synchronized,tsan 仍然会吠叫
问题描述
在一个用 Objective-C 编写的旧项目中,手动保留/释放,在 NSOperationQueue 的子类中,我有以下代码:
- (NSError*)error {
NSError* error;
@synchronized(self) {
error = m_error; // Debugger stops here
}
return error;
}
- (void)setError:(NSError*)error
operationGroup:(NSString*)operationGroup {
error = [error errorByAddingUserInfoObject:operationGroup
forKey:constKeySSYOperationGroup];
@synchronized(self) {
[m_error release];
m_error = error;
[m_error retain];
}
}
您会看到它用于@synchronized
保护对实例变量 m_error 的访问。尽管如此,当这段代码在 macOS 10.14 Mojave Beta 4 (18A336e) 中运行时,在 Xcode 9.4.1 中,我有时会收到来自 Thread Sanitizer (tsan) 的投诉:
WARNING: ThreadSanitizer: data race (pid=60795)
Read of size 8 at 0x7b14000cc528 by thread T20:
#0 -[SSYOperationQueue error] <null> (Bkmxwork:x86_64+0x2b3613)
#1 -[SSYOperationQueue observeValueForKeyPath:ofObject:change:context:] <null> (Bkmxwork:x86_64+0x2b50d7)
#2 NSKeyValueNotifyObserver <null> (Foundation:x86_64+0x3b08b)
#3 _dispatch_client_callout <null> (libdispatch.dylib:x86_64+0x44c0)
Previous write of size 8 at 0x7b14000cc528 by thread T13:
#0 -[SSYOperationQueue setError:operationGroup:] <null> (Bkmxwork:x86_64+0x2b382a)
#1 -[SSYOperationQueue setError:operation:] <null> (Bkmxwork:x86_64+0x2b3a80)
#2 -[SSYOperationQueue observeValueForKeyPath:ofObject:change:context:] <null> (Bkmxwork:x86_64+0x2b525d)
#3 NSKeyValueNotifyObserver <null> (Foundation:x86_64+0x3b08b)
#4 _dispatch_client_callout <null> (libdispatch.dylib:x86_64+0x44c0)
调试器停在 commented 行// Debugger stops here
,这表明这两个访问器之间存在某种对 m_error 的竞争。
可能有什么问题?有人告诉我,tsan 从来没有发出过错误的警报,而且直到现在也没有理由怀疑它。使用去年的 Xcode (9) 和今年的 macOS (10.14) 是否有可能让 tsan 感到困惑?或者它是否像往常一样使用新操作系统变得更智能?在我从 macOS 10.13 升级之前,我没有看到这个警告。
解决方案
感谢 David Philip Oster 向我解释了这个问题:我的 getter 不是原子的。
假设 getter 的 @synchronized 块执行,分配m_error
给error
. 但在 getter 返回之前,调用了 setter 并执行其 @synchronized 块,释放 m_error。然后 getter 返回一个已释放且可能无效的 m_error。
解决方案是添加一个保留和自动释放:
- (NSError*)error {
NSError* error ;
@synchronized(self) {
error = [m_error retain];
}
return [error autorelease];
}
有关这个古老主题的更多信息,请参阅Matt Gallagher 于 2009 年发表的这篇博文
推荐阅读
- python - Edge detection in noisy binary image
- python - A question on dynamic programming leetcode problem 221
- ios - How to open local HTML files in iPhone browsers?
- python - 当程序的一部分在 Python 中等待时,如何做其他事情?
- javascript - 如何在反应中将功能和对象传输到功能组件
- probability - Is there a better way to program this,
- html - Display bug for email on Outlook (Microsoft 365) in a table cell
- javascript - 如何将属性函数添加到 Java 中另一个类的对象?
- android - Android App 中的异常:Lorg/jacoco/agent/rt/internal_8ff85ea/Offline 的解析失败;
- vue.js - 我可以在 Phaser Scene 函数(即创建或更新)中访问 Vue.js 选项 API 方法吗?