ibireme / YYWebImage

Asynchronous image loading framework.
MIT License
3.56k stars 615 forks source link

在模拟器上运行时,使用ThreadSanitizer检测时有时候会出现data race #187

Open Bruce-pac opened 7 years ago

Bruce-pac commented 7 years ago

Read of size 4 at 0x7d0800095a58 by main thread:

0 __104-[UIImageView(YYWebImage) yy_setImageWithURL:placeholder:options:manager:progress:transform:completion:]_block_invoke_2.39 UIImageView+YYWebImage.m:158 (youxueyun:x86_64+0x1009a5716)

#1 __tsan::invoke_and_release_block(void*) <null>:1356554656 (libclang_rt.tsan_iossim_dynamic.dylib:x86_64+0x6043b)
#2 _dispatch_client_callout <null>:1356554656 (libdispatch.dylib:x86_64+0x2b05b)
#3 start <null>:1356554656 (libdyld.dylib:x86_64+0x465c)

Previous write of size 4 at 0x7d0800095a58 by thread T13:

0 __104-[UIImageView(YYWebImage) yy_setImageWithURL:placeholder:options:manager:progress:transform:completion:]_block_invoke_2 UIImageView+YYWebImage.m:180 (youxueyun:x86_64+0x1009a4c5a)

#1 __tsan::invoke_and_release_block(void*) <null>:1356554656 (libclang_rt.tsan_iossim_dynamic.dylib:x86_64+0x6043b)
#2 _dispatch_client_callout <null>:1356554656 (libdispatch.dylib:x86_64+0x2b05b)

Location is heap block of size 32 at 0x7d0800095a40 allocated by thread T13:

0 malloc :1356554688 (libclang_rt.tsan_iossim_dynamic.dylib:x86_64+0x4406a)

#1 _Block_object_assign <null>:1356554688 (libsystem_blocks.dylib:x86_64+0xb9d)
#2 _Block_copy <null>:1356554688 (libsystem_blocks.dylib:x86_64+0x8ef)
#3 __tsan::invoke_and_release_block(void*) <null>:1356554688 (libclang_rt.tsan_iossim_dynamic.dylib:x86_64+0x6043b)
#4 _dispatch_client_callout <null>:1356554688 (libdispatch.dylib:x86_64+0x2b05b)

Thread T13 (tid=4751287, running) created by thread T-1 [failed to restore the stack]

SUMMARY: ThreadSanitizer: data race UIImageView+YYWebImage.m:158 in __104-[UIImageView(YYWebImage) yy_setImageWithURL:placeholder:options:manager:progress:transform:completion:]_block_invoke_2.39

syw-biu-biu commented 7 years ago

没人解决这个问题么。。。

cddjr commented 6 years ago

我审视了一下代码,可能触发这个问题的地方在setOperationWithSentinel函数内, 它在setterQueue线程中执行,其内部检测条件不满足时会直接调用completion触发主线程异步读取newSentinel, 接着该函数返回时会对newSentinel进行赋值,如果此时主线程正好并发执行了读取newSentinel 就会产生Data Race

- (int32_t)setOperationWithSentinel:(int32_t)sentinel
                            url:(NSURL *)imageURL
                        options:(YYWebImageOptions)options
                        manager:(YYWebImageManager *)manager
                       progress:(YYWebImageProgressBlock)progress
                      transform:(YYWebImageTransformBlock)transform
                     completion:(YYWebImageCompletionBlock)completion {
if (sentinel != _sentinel) {
    此处应避免直接调用 if (completion) completion(nil, imageURL, YYWebImageFromNone, YYWebImageStageCancelled, nil);
    return _sentinel;
}

if (!operation && completion) {
    NSDictionary *userInfo = @{ NSLocalizedDescriptionKey : @"YYWebImageOperation create failed." };
    此处应避免直接调用 completion(nil, imageURL, YYWebImageFromNone, YYWebImageStageFinished, [NSError errorWithDomain:@"com.ibireme.webimage" code:-1 userInfo:userInfo]);
}