I made a custom property wrapper which provides a method to access data in a mutually exclusive context using an os_unfair_lock
. After testing my wrapper with TSAN enabled, an access race error was reported at the point of lock acquisition using os_unfair_lock_lock
(shown in the image below)
Somehow a locking structure which is supposedly thread-safe is reported by TSAN as not being so. What is going on here?
According to the WWDC 2016 talk "Concurrent Programming with GCD in Swift 3" at around 18:07 the speaker states that
Traditionally you would use a lock. And in Swift, since you have the entire Darwin module at your disposition, you will actually see the struct based traditional C locks. However [emphasis added], Swift assumes that anything that's a struct can be moved, and that doesn't work with a mutex or with a lock.
The solution is to bridge to Objective-C and create a class which wraps the os_unfair_lock
as an ivar:
And if you want something that's smaller and that looks like the locks that you have in C, then you have to call into Objective-C and introduce a base class in Objective-C that has your lock as an ivar
In this case, something like
UnfairLock.h
#ifndef UnfairLock_h
#define UnfairLock_h
@import Foundation;
@import os;
@interface UnfairLock : NSObject
-(void)unfairlyAcquire;
-(void)unlock;
@end
#endif /* UnfairLock_h */
UnfairLock.m
#import <Foundation/Foundation.h>
#import "UnfairLock.h"
@implementation UnfairLock {
os_unfair_lock _lock;
}
-(instancetype)init {
self = [super init];
if (self) {
_lock = OS_UNFAIR_LOCK_INIT;
}
return self;
}
-(void)unfairlyAcquire {
os_unfair_lock_lock(&_lock);
}
-(void)unlock {
os_unfair_lock_unlock(&_lock);
}
@end