【问题标题】:Swift access race with os_unfair_lock_lock使用 os_unfair_lock_lock 进行 Swift 访问竞赛
【发布时间】:2021-10-07 09:23:43
【问题描述】:

我制作了一个自定义属性包装器,它提供了一种使用os_unfair_lock 在互斥上下文中访问数据的方法。在启用 TSAN 测试我的包装器后,使用 os_unfair_lock_lock 获取锁时报告了访问竞争错误(如下图所示)

不知何故,一个被认为是线程安全的锁定结构被 TSAN 报告为不是这样。这是怎么回事?

【问题讨论】:

标签: swift locking data-race thread-sanitizer


【解决方案1】:

根据 WWDC 2016 演讲“在 Swift 3 中使用 GCD 进行并发编程”,演讲者在 18:07 左右表示

传统上,您会使用锁。而在 Swift 中,由于您拥有整个 Darwin 模块,您实际上会看到基于结构的传统 C 锁。 然而 [强调],Swift 假设任何结构体都可以移动,而这不适用于互斥体或锁。

解决方案是连接到 Objective-C 并创建一个将 os_unfair_lock 包装为 ivar 的类:

如果你想要一些更小并且看起来像 C 中的锁的东西,那么你必须调用 Objective-C 并在 Objective-C 中引入一个基类,它将你的锁作为 ivar

在这种情况下,类似于

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

【讨论】:

    【解决方案2】:

    您的self-answer 的另一种(可能更直接)方法是直接在 Swift 中堆分配锁,而不是桥接到 Objective-C 来执行此操作。 Objective-C 方法通过从不同的语言调用具有不同语义的锁定函数来避免这个问题——C 和 Objective-C 不会 move 或墓碑值类型通过 inout 引用传递给函数;但是你也可以通过不使用 inout 引用来避免纯 Swift 中的问题:

    let lock = UnsafeMutablePointer<os_unfair_lock>.allocate(capacity: 1)
    lock.initialize(to: .init())
    
    // later:
    
    os_unfair_lock_lock(lock)
    defer { os_unfair_lock_unlock(lock) }
    

    堆分配允许您将指针直接传递到函数中,并且指针在 Swift 中是 reference 类型 — 虽然 Swift 可以移动指针值本身,但它引用的内存将保持不变(并且有效)。

    如果你走这条路,当你想拆锁的时候别忘了去初始化和释放内存:

    lock.deinitialize(count: 1)
    lock.deallocate()
    

    如果您愿意,您可以在 Swift 中创建类似的UnfairLock 接口,包括您自己的mutexExecute 等功能:

    typealias UnfairLock = UnsafeMutablePointer<os_unfair_lock>
    
    extension UnfairLock {
        static func createLock() -> UnfairLock {
            let l = UnfairLock.allocate(capacity: 1)
            l.initialize(to: .init())
            return l
        }
    
        static func destructLock(_ lock: UnfairLock) {
            lock.deinitialize(count: 1)
            lock.deallocate()
        }
    
        func whileLocked<T>(_ action: () throws -> T) rethrows -> T {
            os_unfair_lock_lock(self)
            defer { os_unfair_lock_unlock(self) }
            return try action()
        }
    }
    

    用法:

    init() {
        lock = UnfairLock.createLock()
    }
    
    deinit {
        UnfairLock.destructLock(lock)
    }
    
    func performThing() -> Foo {
        return lock.whileLocked {
            // some operation that returns a Foo
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2016-11-24
      • 2011-08-21
      • 2012-07-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-06-06
      • 1970-01-01
      相关资源
      最近更新 更多