Bläddra i källkod

Stop using spinlocks

Apple engineers have pointed out that OSSpinLocks are vulnerable to live locking
on iOS in cases of priority inversion:

. http://mjtsai.com/blog/2015/12/16/osspinlock-is-unsafe/
. https://lists.swift.org/pipermail/swift-dev/Week-of-Mon-20151214/000372.html
ibireme 9 år sedan
förälder
incheckning
891f543903

+ 2 - 2
YYWebImage/Cache/YYDiskCache.h

@@ -145,7 +145,7 @@
  
  @return A new cache object, or nil if an error occurs.
  
- @warning If the cache instance for the specified path already exists in memory, 
+ @warning If the cache instance for the specified path already exists in memory,
      this method will return it directly, instead of creating a new instance.
  */
 - (instancetype)initWithPath:(NSString *)path;
@@ -165,7 +165,7 @@
  
  @return A new cache object, or nil if an error occurs.
  
- @warning If the cache instance for the specified path already exists in memory, 
+ @warning If the cache instance for the specified path already exists in memory,
      this method will return it directly, instead of creating a new instance.
  */
 - (instancetype)initWithPath:(NSString *)path

+ 12 - 13
YYWebImage/Cache/YYDiskCache.m

@@ -11,13 +11,12 @@
 
 #import "YYDiskCache.h"
 #import "YYKVStorage.h"
-#import <libkern/OSAtomic.h>
 #import <CommonCrypto/CommonCrypto.h>
 #import <objc/runtime.h>
 #import <time.h>
 
-#define Lock() dispatch_semaphore_wait(_lock, DISPATCH_TIME_FOREVER)
-#define Unlock() dispatch_semaphore_signal(_lock)
+#define Lock() dispatch_semaphore_wait(self->_lock, DISPATCH_TIME_FOREVER)
+#define Unlock() dispatch_semaphore_signal(self->_lock)
 
 static const int extended_data_key;
 
@@ -48,12 +47,12 @@ static NSString *_YYNSStringMD5(NSString *string) {
 
 /// weak reference for all instances
 static NSMapTable *_globalInstances;
-static OSSpinLock _globalInstancesLock;
+static dispatch_semaphore_t _globalInstancesLock;
 
 static void _YYDiskCacheInitGlobal() {
     static dispatch_once_t onceToken;
     dispatch_once(&onceToken, ^{
-        _globalInstancesLock = OS_SPINLOCK_INIT;
+        _globalInstancesLock = dispatch_semaphore_create(1);
         _globalInstances = [[NSMapTable alloc] initWithKeyOptions:NSPointerFunctionsStrongMemory valueOptions:NSPointerFunctionsWeakMemory capacity:0];
     });
 }
@@ -61,18 +60,18 @@ static void _YYDiskCacheInitGlobal() {
 static YYDiskCache *_YYDiskCacheGetGlobal(NSString *path) {
     if (path.length == 0) return nil;
     _YYDiskCacheInitGlobal();
-    OSSpinLockLock(&_globalInstancesLock);
+    dispatch_semaphore_wait(_globalInstancesLock, DISPATCH_TIME_FOREVER);
     id cache = [_globalInstances objectForKey:path];
-    OSSpinLockUnlock(&_globalInstancesLock);
+    dispatch_semaphore_signal(_globalInstancesLock);
     return cache;
 }
 
 static void _YYDiskCacheSetGlobal(YYDiskCache *cache) {
     if (cache.path.length == 0) return;
     _YYDiskCacheInitGlobal();
-    OSSpinLockLock(&_globalInstancesLock);
+    dispatch_semaphore_wait(_globalInstancesLock, DISPATCH_TIME_FOREVER);
     [_globalInstances setObject:cache forKey:cache.path];
-    OSSpinLockUnlock(&_globalInstancesLock);
+    dispatch_semaphore_signal(_globalInstancesLock);
 }
 
 
@@ -98,12 +97,12 @@ static void _YYDiskCacheSetGlobal(YYDiskCache *cache) {
     dispatch_async(_queue, ^{
         __strong typeof(_self) self = _self;
         if (!self) return;
-        dispatch_semaphore_wait(self->_lock, DISPATCH_TIME_FOREVER);
+        Lock();
         [self _trimToCost:self.costLimit];
         [self _trimToCount:self.countLimit];
         [self _trimToAge:self.ageLimit];
         [self _trimToFreeDiskSpace:self.freeDiskSpaceLimit];
-        dispatch_semaphore_signal(self->_lock);
+        Unlock();
     });
 }
 
@@ -330,9 +329,9 @@ static void _YYDiskCacheSetGlobal(YYDiskCache *cache) {
             if (end) end(YES);
             return;
         }
-        dispatch_semaphore_wait(self->_lock, DISPATCH_TIME_FOREVER);
+        Lock();
         [_kv removeAllItemsWithProgressBlock:progress endBlock:end];
-        dispatch_semaphore_signal(self->_lock);
+        Unlock();
     });
 }
 

+ 0 - 13
YYWebImage/Cache/YYKVStorage.m

@@ -11,7 +11,6 @@
 
 #import "YYKVStorage.h"
 #import <UIKit/UIKit.h>
-#import <libkern/OSAtomic.h>
 #import <time.h>
 
 #if __has_include(<sqlite3.h>)
@@ -58,7 +57,6 @@ static NSString *const kTrashDirectoryName = @"trash";
     
     BOOL _invalidated; ///< If YES, then the db should not open again, all read/write should be ignored.
     BOOL _dbIsClosing; ///< If YES, then the db is during closing.
-    OSSpinLock _dbStateLock;
 }
 
 
@@ -66,7 +64,6 @@ static NSString *const kTrashDirectoryName = @"trash";
 
 - (BOOL)_dbOpen {
     BOOL shouldOpen = YES;
-    OSSpinLockLock(&_dbStateLock);
     if (_invalidated) {
         shouldOpen = NO;
     } else if (_dbIsClosing) {
@@ -74,7 +71,6 @@ static NSString *const kTrashDirectoryName = @"trash";
     } else if (_db){
         shouldOpen = NO;
     }
-    OSSpinLockUnlock(&_dbStateLock);
     if (!shouldOpen) return YES;
     
     int result = sqlite3_open(_dbPath.UTF8String, &_db);
@@ -91,7 +87,6 @@ static NSString *const kTrashDirectoryName = @"trash";
 
 - (BOOL)_dbClose {
     BOOL needClose = YES;
-    OSSpinLockLock(&_dbStateLock);
     if (!_db) {
         needClose = NO;
     } else if (_invalidated) {
@@ -101,7 +96,6 @@ static NSString *const kTrashDirectoryName = @"trash";
     } else {
         _dbIsClosing = YES;
     }
-    OSSpinLockUnlock(&_dbStateLock);
     if (!needClose) return YES;
     
     int  result = 0;
@@ -128,11 +122,7 @@ static NSString *const kTrashDirectoryName = @"trash";
         }
     } while (retry);
     _db = NULL;
-    
-    OSSpinLockLock(&_dbStateLock);
     _dbIsClosing = NO;
-    OSSpinLockUnlock(&_dbStateLock);
-    
     return YES;
 }
 
@@ -646,9 +636,7 @@ static NSString *const kTrashDirectoryName = @"trash";
 }
 
 - (void)_appWillBeTerminated {
-    OSSpinLockLock(&_dbStateLock);
     _invalidated = YES;
-    OSSpinLockUnlock(&_dbStateLock);
 }
 
 #pragma mark - public
@@ -675,7 +663,6 @@ static NSString *const kTrashDirectoryName = @"trash";
     _trashPath = [path stringByAppendingPathComponent:kTrashDirectoryName];
     _trashQueue = dispatch_queue_create("com.ibireme.cache.disk.trash", DISPATCH_QUEUE_SERIAL);
     _dbPath = [path stringByAppendingPathComponent:kDBFileName];
-    _dbStateLock = OS_SPINLOCK_INIT;
     _errorLogsEnabled = YES;
     NSError *error = nil;
     if (![[NSFileManager defaultManager] createDirectoryAtPath:path

+ 36 - 37
YYWebImage/Cache/YYMemoryCache.m

@@ -13,7 +13,6 @@
 #import <UIKit/UIKit.h>
 #import <CoreFoundation/CoreFoundation.h>
 #import <QuartzCore/QuartzCore.h>
-#import <libkern/OSAtomic.h>
 #import <pthread.h>
 
 #if __has_include("YYDispatchQueuePool.h")
@@ -183,7 +182,7 @@ static inline dispatch_queue_t YYMemoryCacheGetReleaseQueue() {
 
 
 @implementation YYMemoryCache {
-    OSSpinLock _lock;
+    pthread_mutex_t _lock;
     _YYLinkedMap *_lru;
     dispatch_queue_t _queue;
 }
@@ -208,26 +207,26 @@ static inline dispatch_queue_t YYMemoryCacheGetReleaseQueue() {
 
 - (void)_trimToCost:(NSUInteger)costLimit {
     BOOL finish = NO;
-    OSSpinLockLock(&_lock);
+    pthread_mutex_lock(&_lock);
     if (costLimit == 0) {
         [_lru removeAll];
         finish = YES;
     } else if (_lru->_totalCost <= costLimit) {
         finish = YES;
     }
-    OSSpinLockUnlock(&_lock);
+    pthread_mutex_unlock(&_lock);
     if (finish) return;
     
     NSMutableArray *holder = [NSMutableArray new];
     while (!finish) {
-        if (OSSpinLockTry(&_lock)) {
+        if (pthread_mutex_trylock(&_lock) == 0) {
             if (_lru->_totalCost > costLimit) {
                 _YYLinkedMapNode *node = [_lru removeTailNode];
                 if (node) [holder addObject:node];
             } else {
                 finish = YES;
             }
-            OSSpinLockUnlock(&_lock);
+            pthread_mutex_unlock(&_lock);
         } else {
             usleep(10 * 1000); //10 ms
         }
@@ -242,26 +241,26 @@ static inline dispatch_queue_t YYMemoryCacheGetReleaseQueue() {
 
 - (void)_trimToCount:(NSUInteger)countLimit {
     BOOL finish = NO;
-    OSSpinLockLock(&_lock);
+    pthread_mutex_lock(&_lock);
     if (countLimit == 0) {
         [_lru removeAll];
         finish = YES;
     } else if (_lru->_totalCount <= countLimit) {
         finish = YES;
     }
-    OSSpinLockUnlock(&_lock);
+    pthread_mutex_unlock(&_lock);
     if (finish) return;
     
     NSMutableArray *holder = [NSMutableArray new];
     while (!finish) {
-        if (OSSpinLockTry(&_lock)) {
+        if (pthread_mutex_trylock(&_lock) == 0) {
             if (_lru->_totalCount > countLimit) {
                 _YYLinkedMapNode *node = [_lru removeTailNode];
                 if (node) [holder addObject:node];
             } else {
                 finish = YES;
             }
-            OSSpinLockUnlock(&_lock);
+            pthread_mutex_unlock(&_lock);
         } else {
             usleep(10 * 1000); //10 ms
         }
@@ -277,26 +276,26 @@ static inline dispatch_queue_t YYMemoryCacheGetReleaseQueue() {
 - (void)_trimToAge:(NSTimeInterval)ageLimit {
     BOOL finish = NO;
     NSTimeInterval now = CACurrentMediaTime();
-    OSSpinLockLock(&_lock);
+    pthread_mutex_lock(&_lock);
     if (ageLimit <= 0) {
         [_lru removeAll];
         finish = YES;
     } else if (!_lru->_tail || (now - _lru->_tail->_time) <= ageLimit) {
         finish = YES;
     }
-    OSSpinLockUnlock(&_lock);
+    pthread_mutex_unlock(&_lock);
     if (finish) return;
     
     NSMutableArray *holder = [NSMutableArray new];
     while (!finish) {
-        if (OSSpinLockTry(&_lock)) {
+        if (pthread_mutex_trylock(&_lock) == 0) {
             if (_lru->_tail && (now - _lru->_tail->_time) > ageLimit) {
                 _YYLinkedMapNode *node = [_lru removeTailNode];
                 if (node) [holder addObject:node];
             } else {
                 finish = YES;
             }
-            OSSpinLockUnlock(&_lock);
+            pthread_mutex_unlock(&_lock);
         } else {
             usleep(10 * 1000); //10 ms
         }
@@ -331,7 +330,7 @@ static inline dispatch_queue_t YYMemoryCacheGetReleaseQueue() {
 
 - (instancetype)init {
     self = super.init;
-    _lock = OS_SPINLOCK_INIT;
+    pthread_mutex_init(&_lock, NULL);
     _lru = [_YYLinkedMap new];
     _queue = dispatch_queue_create("com.ibireme.cache.memory", DISPATCH_QUEUE_SERIAL);
     
@@ -356,62 +355,62 @@ static inline dispatch_queue_t YYMemoryCacheGetReleaseQueue() {
 }
 
 - (NSUInteger)totalCount {
-    OSSpinLockLock(&_lock);
+    pthread_mutex_lock(&_lock);
     NSUInteger count = _lru->_totalCount;
-    OSSpinLockUnlock(&_lock);
+    pthread_mutex_unlock(&_lock);
     return count;
 }
 
 - (NSUInteger)totalCost {
-    OSSpinLockLock(&_lock);
+    pthread_mutex_lock(&_lock);
     NSUInteger totalCost = _lru->_totalCost;
-    OSSpinLockUnlock(&_lock);
+    pthread_mutex_unlock(&_lock);
     return totalCost;
 }
 
 - (BOOL)releaseInMainThread {
-    OSSpinLockLock(&_lock);
+    pthread_mutex_lock(&_lock);
     BOOL releaseInMainThread = _lru->_releaseOnMainThread;
-    OSSpinLockUnlock(&_lock);
+    pthread_mutex_unlock(&_lock);
     return releaseInMainThread;
 }
 
 - (void)setReleaseInMainThread:(BOOL)releaseInMainThread {
-    OSSpinLockLock(&_lock);
+    pthread_mutex_lock(&_lock);
     _lru->_releaseOnMainThread = releaseInMainThread;
-    OSSpinLockUnlock(&_lock);
+    pthread_mutex_unlock(&_lock);
 }
 
 - (BOOL)releaseAsynchronously {
-    OSSpinLockLock(&_lock);
+    pthread_mutex_lock(&_lock);
     BOOL releaseAsynchronously = _lru->_releaseAsynchronously;
-    OSSpinLockUnlock(&_lock);
+    pthread_mutex_unlock(&_lock);
     return releaseAsynchronously;
 }
 
 - (void)setReleaseAsynchronously:(BOOL)releaseAsynchronously {
-    OSSpinLockLock(&_lock);
+    pthread_mutex_lock(&_lock);
     _lru->_releaseAsynchronously = releaseAsynchronously;
-    OSSpinLockUnlock(&_lock);
+    pthread_mutex_unlock(&_lock);
 }
 
 - (BOOL)containsObjectForKey:(id)key {
     if (!key) return NO;
-    OSSpinLockLock(&_lock);
+    pthread_mutex_lock(&_lock);
     BOOL contains = CFDictionaryContainsKey(_lru->_dic, (__bridge const void *)(key));
-    OSSpinLockUnlock(&_lock);
+    pthread_mutex_unlock(&_lock);
     return contains;
 }
 
 - (id)objectForKey:(id)key {
     if (!key) return nil;
-    OSSpinLockLock(&_lock);
+    pthread_mutex_lock(&_lock);
     _YYLinkedMapNode *node = CFDictionaryGetValue(_lru->_dic, (__bridge const void *)(key));
     if (node) {
         node->_time = CACurrentMediaTime();
         [_lru bringNodeToHead:node];
     }
-    OSSpinLockUnlock(&_lock);
+    pthread_mutex_unlock(&_lock);
     return node ? node->_value : nil;
 }
 
@@ -425,7 +424,7 @@ static inline dispatch_queue_t YYMemoryCacheGetReleaseQueue() {
         [self removeObjectForKey:key];
         return;
     }
-    OSSpinLockLock(&_lock);
+    pthread_mutex_lock(&_lock);
     _YYLinkedMapNode *node = CFDictionaryGetValue(_lru->_dic, (__bridge const void *)(key));
     NSTimeInterval now = CACurrentMediaTime();
     if (node) {
@@ -461,12 +460,12 @@ static inline dispatch_queue_t YYMemoryCacheGetReleaseQueue() {
             });
         }
     }
-    OSSpinLockUnlock(&_lock);
+    pthread_mutex_unlock(&_lock);
 }
 
 - (void)removeObjectForKey:(id)key {
     if (!key) return;
-    OSSpinLockLock(&_lock);
+    pthread_mutex_lock(&_lock);
     _YYLinkedMapNode *node = CFDictionaryGetValue(_lru->_dic, (__bridge const void *)(key));
     if (node) {
         [_lru removeNode:node];
@@ -481,13 +480,13 @@ static inline dispatch_queue_t YYMemoryCacheGetReleaseQueue() {
             });
         }
     }
-    OSSpinLockUnlock(&_lock);
+    pthread_mutex_unlock(&_lock);
 }
 
 - (void)removeAllObjects {
-    OSSpinLockLock(&_lock);
+    pthread_mutex_lock(&_lock);
     [_lru removeAll];
-    OSSpinLockUnlock(&_lock);
+    pthread_mutex_unlock(&_lock);
 }
 
 - (void)trimToCount:(NSUInteger)count {

+ 6 - 7
YYWebImage/Categories/UIButton+YYWebImage.m

@@ -12,7 +12,6 @@
 #import "UIButton+YYWebImage.h"
 #import "YYWebImageOperation.h"
 #import "_YYWebImageSetter.h"
-#import <libkern/OSAtomic.h>
 #import <objc/runtime.h>
 
 // Dummy class for category
@@ -46,29 +45,29 @@ static int _YYWebImageBackgroundSetterKey;
 
 @implementation _YYWebImageSetterDicForButton {
     NSMutableDictionary *_dic;
-    OSSpinLock _lock;
+    dispatch_semaphore_t _lock;
 }
 - (instancetype)init {
     self = [super init];
-    _lock = OS_SPINLOCK_INIT;
+    _lock = dispatch_semaphore_create(1);
     _dic = [NSMutableDictionary new];
     return self;
 }
 - (_YYWebImageSetter *)setterForState:(NSNumber *)state {
-    OSSpinLockLock(&_lock);
+    dispatch_semaphore_wait(_lock, DISPATCH_TIME_FOREVER);
     _YYWebImageSetter *setter = _dic[state];
-    OSSpinLockUnlock(&_lock);
+    dispatch_semaphore_signal(_lock);
     return setter;
     
 }
 - (_YYWebImageSetter *)lazySetterForState:(NSNumber *)state {
-    OSSpinLockLock(&_lock);
+    dispatch_semaphore_wait(_lock, DISPATCH_TIME_FOREVER);
     _YYWebImageSetter *setter = _dic[state];
     if (!setter) {
         setter = [_YYWebImageSetter new];
         _dic[state] = setter;
     }
-    OSSpinLockUnlock(&_lock);
+    dispatch_semaphore_signal(_lock);
     return setter;
 }
 @end

+ 8 - 8
YYWebImage/Categories/_YYWebImageSetter.m

@@ -19,7 +19,7 @@ const NSTimeInterval _YYWebImageProgressiveFadeTime = 0.4;
 
 
 @implementation _YYWebImageSetter {
-    OSSpinLock _lock;
+    dispatch_semaphore_t _lock;
     NSURL *_imageURL;
     NSOperation *_operation;
     int32_t _sentinel;
@@ -27,14 +27,14 @@ const NSTimeInterval _YYWebImageProgressiveFadeTime = 0.4;
 
 - (instancetype)init {
     self = [super init];
-    _lock = OS_SPINLOCK_INIT;
+    _lock = dispatch_semaphore_create(1);
     return self;
 }
 
 - (NSURL *)imageURL {
-    OSSpinLockLock(&_lock);
+    dispatch_semaphore_wait(_lock, DISPATCH_TIME_FOREVER);
     NSURL *imageURL = _imageURL;
-    OSSpinLockUnlock(&_lock);
+    dispatch_semaphore_signal(_lock);
     return imageURL;
 }
 
@@ -61,7 +61,7 @@ const NSTimeInterval _YYWebImageProgressiveFadeTime = 0.4;
         completion(nil, imageURL, YYWebImageFromNone, YYWebImageStageFinished, [NSError errorWithDomain:@"com.ibireme.webimage" code:-1 userInfo:userInfo]);
     }
     
-    OSSpinLockLock(&_lock);
+    dispatch_semaphore_wait(_lock, DISPATCH_TIME_FOREVER);
     if (sentinel == _sentinel) {
         if (_operation) [_operation cancel];
         _operation = operation;
@@ -69,7 +69,7 @@ const NSTimeInterval _YYWebImageProgressiveFadeTime = 0.4;
     } else {
         [operation cancel];
     }
-    OSSpinLockUnlock(&_lock);
+    dispatch_semaphore_signal(_lock);
 }
 
 - (int32_t)cancel {
@@ -78,14 +78,14 @@ const NSTimeInterval _YYWebImageProgressiveFadeTime = 0.4;
 
 - (int32_t)cancelWithNewURL:(NSURL *)imageURL {
     int32_t sentinel;
-    OSSpinLockLock(&_lock);
+    dispatch_semaphore_wait(_lock, DISPATCH_TIME_FOREVER);
     if (_operation) {
         [_operation cancel];
         _operation = nil;
     }
     _imageURL = imageURL;
     sentinel = OSAtomicIncrement32(&_sentinel);
-    OSSpinLockUnlock(&_lock);
+    dispatch_semaphore_signal(_lock);
     return sentinel;
 }
 

+ 6 - 7
YYWebImage/Image/YYAnimatedImageView.m

@@ -12,19 +12,18 @@
 #import "YYAnimatedImageView.h"
 #import "YYImageCoder.h"
 #import <pthread.h>
-#import <libkern/OSAtomic.h>
 #import <mach/mach.h>
 
 
 #define BUFFER_SIZE (10 * 1024 * 1024) // 10MB (minimum memory buffer size)
 
-#define LOCK(...) OSSpinLockLock(&self->_lock); \
+#define LOCK(...) dispatch_semaphore_wait(self->_lock, DISPATCH_TIME_FOREVER); \
 __VA_ARGS__; \
-OSSpinLockUnlock(&self->_lock);
+dispatch_semaphore_signal(self->_lock);
 
-#define LOCK_VIEW(...) OSSpinLockLock(&view->_lock); \
+#define LOCK_VIEW(...) dispatch_semaphore_wait(view->_lock, DISPATCH_TIME_FOREVER); \
 __VA_ARGS__; \
-OSSpinLockUnlock(&view->_lock);
+dispatch_semaphore_signal(view->_lock);
 
 
 static int64_t _YYDeviceMemoryTotal() {
@@ -126,7 +125,7 @@ typedef NS_ENUM(NSUInteger, YYAnimatedImageType) {
     UIImage <YYAnimatedImage> *_curAnimatedImage;
     
     dispatch_once_t _onceToken;
-    OSSpinLock _lock; ///< lock for _buffer
+    dispatch_semaphore_t _lock; ///< lock for _buffer
     NSOperationQueue *_requestQueue; ///< image request queue, serial
     
     CADisplayLink *_link; ///< ticker for change frame
@@ -227,7 +226,7 @@ typedef NS_ENUM(NSUInteger, YYAnimatedImageType) {
 // init the animated params.
 - (void)resetAnimated {
     dispatch_once(&_onceToken, ^{
-        _lock = OS_SPINLOCK_INIT;
+        _lock = dispatch_semaphore_create(1);
         _buffer = [NSMutableDictionary new];
         _requestQueue = [[NSOperationQueue alloc] init];
         _requestQueue.maxConcurrentOperationCount = 1;

+ 8 - 9
YYWebImage/Image/YYImage.m

@@ -10,7 +10,6 @@
 //
 
 #import "YYImage.h"
-#import <libkern/OSAtomic.h>
 
 /**
  An array of NSNumber objects, shows the best order for path scale search.
@@ -88,7 +87,7 @@ static CGFloat _NSStringPathScale(NSString *string) {
 @implementation YYImage {
     YYImageDecoder *_decoder;
     NSArray *_preloadedFrames;
-    OSSpinLock _preloadedLock;
+    dispatch_semaphore_t _preloadedLock;
     NSUInteger _bytesPerFrame;
 }
 
@@ -145,7 +144,7 @@ static CGFloat _NSStringPathScale(NSString *string) {
 - (instancetype)initWithData:(NSData *)data scale:(CGFloat)scale {
     if (data.length == 0) return nil;
     if (scale <= 0) scale = [UIScreen mainScreen].scale;
-    _preloadedLock = OS_SPINLOCK_INIT;
+    _preloadedLock = dispatch_semaphore_create(1);
     @autoreleasepool {
         YYImageDecoder *decoder = [YYImageDecoder decoderWithData:data scale:scale];
         YYImageFrame *frame = [decoder frameAtIndex:0 decodeForDisplay:YES];
@@ -180,13 +179,13 @@ static CGFloat _NSStringPathScale(NSString *string) {
                     [frames addObject:[NSNull null]];
                 }
             }
-            OSSpinLockLock(&_preloadedLock);
+            dispatch_semaphore_wait(_preloadedLock, DISPATCH_TIME_FOREVER);
             _preloadedFrames = frames;
-            OSSpinLockUnlock(&_preloadedLock);
+            dispatch_semaphore_signal(_preloadedLock);
         } else {
-            OSSpinLockLock(&_preloadedLock);
+            dispatch_semaphore_wait(_preloadedLock, DISPATCH_TIME_FOREVER);
             _preloadedFrames = nil;
-            OSSpinLockUnlock(&_preloadedLock);
+            dispatch_semaphore_signal(_preloadedLock);
         }
     }
 }
@@ -229,9 +228,9 @@ static CGFloat _NSStringPathScale(NSString *string) {
 
 - (UIImage *)animatedImageFrameAtIndex:(NSUInteger)index {
     if (index >= _decoder.frameCount) return nil;
-    OSSpinLockLock(&_preloadedLock);
+    dispatch_semaphore_wait(_preloadedLock, DISPATCH_TIME_FOREVER);
     UIImage *image = _preloadedFrames[index];
-    OSSpinLockUnlock(&_preloadedLock);
+    dispatch_semaphore_signal(_preloadedLock);
     if (image) return image == (id)[NSNull null] ? nil : image;
     return [_decoder frameAtIndex:index decodeForDisplay:YES].image;
 }

+ 16 - 17
YYWebImage/Image/YYImageCoder.m

@@ -19,7 +19,6 @@
 #import <AssetsLibrary/AssetsLibrary.h>
 #import <objc/runtime.h>
 #import <pthread.h>
-#import <libkern/OSAtomic.h>
 #import <zlib.h>
 
 
@@ -924,7 +923,7 @@ CGImageRef YYCGImageCreateAffineTransformCopy(CGImageRef imageRef, CGAffineTrans
     if (srcWidth == 0 || srcHeight == 0 || destWidth == 0 || destHeight == 0) return NULL;
     
     CGDataProviderRef tmpProvider = NULL, destProvider = NULL;
-    CGImageRef tmpImage = NULL, destImage = NULL;;
+    CGImageRef tmpImage = NULL, destImage = NULL;
     vImage_Buffer src = {0}, tmp = {0}, dest = {0};
     if(!YYCGImageDecodeToBitmapBufferWith32BitFormat(imageRef, &src, kCGImageAlphaFirst | kCGBitmapByteOrderDefault)) return NULL;
     
@@ -1506,7 +1505,7 @@ CGImageRef YYCGImageCreateWithWebPData(CFDataRef webpData,
 
 
 @implementation YYImageDecoder {
-    pthread_mutex_t _lock;
+    pthread_mutex_t _lock; // recursive lock
     
     BOOL _sourceTypeDetected;
     CGImageSourceRef _source;
@@ -1516,7 +1515,7 @@ CGImageRef YYCGImageCreateWithWebPData(CFDataRef webpData,
 #endif
     
     UIImageOrientation _orientation;
-    OSSpinLock _framesLock;
+    dispatch_semaphore_t _framesLock;
     NSArray *_frames; ///< Array<GGImageDecoderFrame>, without image
     BOOL _needBlend;
     NSUInteger _blendFrameIndex;
@@ -1548,7 +1547,7 @@ CGImageRef YYCGImageCreateWithWebPData(CFDataRef webpData,
     self = [super init];
     if (scale <= 0) scale = 1;
     _scale = scale;
-    _framesLock = OS_SPINLOCK_INIT;
+    _framesLock = dispatch_semaphore_create(1);
     
     pthread_mutexattr_t attr;
     pthread_mutexattr_init (&attr);
@@ -1577,11 +1576,11 @@ CGImageRef YYCGImageCreateWithWebPData(CFDataRef webpData,
 
 - (NSTimeInterval)frameDurationAtIndex:(NSUInteger)index {
     NSTimeInterval result = 0;
-    OSSpinLockLock(&_framesLock); // for better performance when play animation...
+    dispatch_semaphore_wait(_framesLock, DISPATCH_TIME_FOREVER);
     if (index < _frames.count) {
         result = ((_YYImageDecoderFrame *)_frames[index]).duration;
     }
-    OSSpinLockUnlock(&_framesLock);
+    dispatch_semaphore_signal(_framesLock);
     return result;
 }
 
@@ -1746,9 +1745,9 @@ CGImageRef YYCGImageCreateWithWebPData(CFDataRef webpData,
     _loopCount = 0;
     if (_webpSource) WebPDemuxDelete(_webpSource);
     _webpSource = NULL;
-    OSSpinLockLock(&_framesLock);
+    dispatch_semaphore_wait(_framesLock, DISPATCH_TIME_FOREVER);
     _frames = nil;
-    OSSpinLockUnlock(&_framesLock);
+    dispatch_semaphore_signal(_framesLock);
     
     /*
      https://developers.google.com/speed/webp/docs/api
@@ -1832,9 +1831,9 @@ CGImageRef YYCGImageCreateWithWebPData(CFDataRef webpData,
     _loopCount = webpLoopCount;
     _needBlend = needBlend;
     _webpSource = demuxer;
-    OSSpinLockLock(&_framesLock);
+    dispatch_semaphore_wait(_framesLock, DISPATCH_TIME_FOREVER);
     _frames = frames;
-    OSSpinLockUnlock(&_framesLock);
+    dispatch_semaphore_signal(_framesLock);
 #endif
 }
 
@@ -1930,9 +1929,9 @@ CGImageRef YYCGImageCreateWithWebPData(CFDataRef webpData,
     _loopCount = apng->apng_loop_num;
     _needBlend = needBlend;
     _apngSource = apng;
-    OSSpinLockLock(&_framesLock);
+    dispatch_semaphore_wait(_framesLock, DISPATCH_TIME_FOREVER);
     _frames = frames;
-    OSSpinLockUnlock(&_framesLock);
+    dispatch_semaphore_signal(_framesLock);
 }
 
 - (void)_updateSourceImageIO {
@@ -1940,9 +1939,9 @@ CGImageRef YYCGImageCreateWithWebPData(CFDataRef webpData,
     _height = 0;
     _orientation = UIImageOrientationUp;
     _loopCount = 0;
-    OSSpinLockLock(&_framesLock);
+    dispatch_semaphore_wait(_framesLock, DISPATCH_TIME_FOREVER);
     _frames = nil;
-    OSSpinLockUnlock(&_framesLock);
+    dispatch_semaphore_signal(_framesLock);
     
     if (!_source) {
         if (_finalized) {
@@ -2026,9 +2025,9 @@ CGImageRef YYCGImageCreateWithWebPData(CFDataRef webpData,
             CFRelease(properties);
         }
     }
-    OSSpinLockLock(&_framesLock);
+    dispatch_semaphore_wait(_framesLock, DISPATCH_TIME_FOREVER);
     _frames = frames;
-    OSSpinLockUnlock(&_framesLock);
+    dispatch_semaphore_signal(_framesLock);
 }
 
 - (CGImageRef)_newUnblendedImageAtIndex:(NSUInteger)index

+ 6 - 6
YYWebImage/YYWebImageOperation.m

@@ -72,31 +72,31 @@ static NSData *JPEGSOSMarker() {
 
 
 static NSMutableSet *URLBlacklist;
-static OSSpinLock URLBlacklistLock;
+static dispatch_semaphore_t URLBlacklistLock;
 
 static void URLBlacklistInit() {
     static dispatch_once_t onceToken;
     dispatch_once(&onceToken, ^{
         URLBlacklist = [NSMutableSet new];
-        URLBlacklistLock = OS_SPINLOCK_INIT;
+        URLBlacklistLock = dispatch_semaphore_create(1);
     });
 }
 
 static BOOL URLBlackListContains(NSURL *url) {
     if (!url || url == (id)[NSNull null]) return NO;
     URLBlacklistInit();
-    OSSpinLockLock(&URLBlacklistLock);
+    dispatch_semaphore_wait(URLBlacklistLock, DISPATCH_TIME_FOREVER);
     BOOL contains = [URLBlacklist containsObject:url];
-    OSSpinLockUnlock(&URLBlacklistLock);
+    dispatch_semaphore_signal(URLBlacklistLock);
     return contains;
 }
 
 static void URLInBlackListAdd(NSURL *url) {
     if (!url || url == (id)[NSNull null]) return;
     URLBlacklistInit();
-    OSSpinLockLock(&URLBlacklistLock);
+    dispatch_semaphore_wait(URLBlacklistLock, DISPATCH_TIME_FOREVER);
     [URLBlacklist addObject:url];
-    OSSpinLockUnlock(&URLBlacklistLock);
+    dispatch_semaphore_signal(URLBlacklistLock);
 }