forked from OSchip/llvm-project
[tsan] Add interceptors for objc_sync_enter and objc_sync_exit
Objective-C's @synchronize synchronization primitive uses calls to objc_sync_enter and objc_sync_exit runtime functions. In most cases, they end up just calling pthread_mutex_lock/pthread_mutex_unlock, but there are some cases where the synchronization from pthread_mutex_lock/pthread_mutex_unlock interceptors isn't enough. Let's add explicit interceptors for objc_sync_enter and objc_sync_exit to handle all cases. Differential Revision: https://reviews.llvm.org/D45487 llvm-svn: 329982
This commit is contained in:
parent
223f4c7c99
commit
296ce3bd23
|
@ -118,7 +118,7 @@ if(APPLE)
|
|||
RTUbsan
|
||||
CFLAGS ${TSAN_RTL_CFLAGS}
|
||||
LINK_FLAGS ${SANITIZER_COMMON_LINK_FLAGS} ${WEAK_SYMBOL_LINK_FLAGS}
|
||||
LINK_LIBS ${TSAN_LINK_LIBS}
|
||||
LINK_LIBS ${TSAN_LINK_LIBS} objc
|
||||
PARENT_TARGET tsan)
|
||||
add_compiler_rt_object_libraries(RTTsan_dynamic
|
||||
OS ${TSAN_SUPPORTED_OS}
|
||||
|
|
|
@ -294,6 +294,19 @@ TSAN_INTERCEPTOR(void, xpc_connection_cancel, xpc_connection_t connection) {
|
|||
|
||||
#endif // #if defined(__has_include) && __has_include(<xpc/xpc.h>)
|
||||
|
||||
TSAN_INTERCEPTOR(int, objc_sync_enter, void *obj) {
|
||||
SCOPED_TSAN_INTERCEPTOR(objc_sync_enter, obj);
|
||||
int result = REAL(objc_sync_enter)(obj);
|
||||
if (obj) Acquire(thr, pc, (uptr)obj);
|
||||
return result;
|
||||
}
|
||||
|
||||
TSAN_INTERCEPTOR(int, objc_sync_exit, void *obj) {
|
||||
SCOPED_TSAN_INTERCEPTOR(objc_sync_enter, obj);
|
||||
if (obj) Release(thr, pc, (uptr)obj);
|
||||
return REAL(objc_sync_exit)(obj);
|
||||
}
|
||||
|
||||
// On macOS, libc++ is always linked dynamically, so intercepting works the
|
||||
// usual way.
|
||||
#define STDCXX_INTERCEPTOR TSAN_INTERCEPTOR
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
// RUN: %clangxx_tsan %s -o %t -framework Foundation -fobjc-arc %darwin_min_target_with_full_runtime_arc_support
|
||||
// RUN: %run %t 2>&1 | FileCheck %s
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface MyClass : NSObject {
|
||||
long field;
|
||||
}
|
||||
@property (nonatomic, readonly) long value;
|
||||
@end
|
||||
|
||||
dispatch_group_t group;
|
||||
|
||||
@implementation MyClass
|
||||
|
||||
- (void) start {
|
||||
dispatch_queue_t q = dispatch_queue_create(NULL, NULL);
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
for (int i = 0; i < 1000; i++) {
|
||||
dispatch_async(q, ^{
|
||||
@synchronized(self) {
|
||||
self->field = i;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
- (long) value {
|
||||
@synchronized(self) {
|
||||
return self->field;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
dispatch_group_leave(group);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
int main() {
|
||||
group = dispatch_group_create();
|
||||
@autoreleasepool {
|
||||
for (int j = 0; j < 100; ++j) {
|
||||
dispatch_group_enter(group);
|
||||
MyClass *obj = [[MyClass alloc] init];
|
||||
[obj start];
|
||||
long x = obj.value;
|
||||
(void)x;
|
||||
}
|
||||
}
|
||||
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
|
||||
NSLog(@"Hello world");
|
||||
}
|
||||
|
||||
// CHECK: Hello world
|
||||
// CHECK-NOT: WARNING: ThreadSanitizer
|
Loading…
Reference in New Issue