Rx 從 SinkDisposer 一窺線程安全

// SinkDisposer
fileprivate final class SinkDisposer: Cancelable {
    fileprivate enum DisposeState: UInt32 {
        case disposed = 1
        case sinkAndSubscriptionSet = 2
    }

    // Jeej, swift API consistency rules
    fileprivate enum DisposeStateInt32: Int32 {
        case disposed = 1
        case sinkAndSubscriptionSet = 2
    }
    
    private var _state: AtomicInt = 0
    private var _sink: Disposable? = nil
    private var _subscription: Disposable? = nil

    var isDisposed: Bool {
        return AtomicFlagSet(DisposeState.disposed.rawValue, &_state)
    }

    func setSinkAndSubscription(sink: Disposable, subscription: Disposable) {
        _sink = sink
        _subscription = subscription

        let previousState = AtomicOr(DisposeState.sinkAndSubscriptionSet.rawValue, &_state)
        if (previousState & DisposeStateInt32.sinkAndSubscriptionSet.rawValue) != 0 {
            rxFatalError("Sink and subscription were already set")
        }

        if (previousState & DisposeStateInt32.disposed.rawValue) != 0 {
            sink.dispose()
            subscription.dispose()
            _sink = nil
            _subscription = nil
        }
    }
    
    func dispose() {
        let previousState = AtomicOr(DisposeState.disposed.rawValue, &_state)

        if (previousState & DisposeStateInt32.disposed.rawValue) != 0 {
            return
        }

        if (previousState & DisposeStateInt32.sinkAndSubscriptionSet.rawValue) != 0 {
            guard let sink = _sink else {
                rxFatalError("Sink not set")
            }
            guard let subscription = _subscription else {
                rxFatalError("Subscription not set")
            }

            sink.dispose()
            subscription.dispose()

            _sink = nil
            _subscription = nil
        }
    }
}



首先我們要分析下需求库倘,哪些操作是需要保證線程安全的挫以。 顯然_state的設(shè)置是要保證線程安全的诫惭, 那么與之相關(guān)的讀寫操作都是要加鎖的,因此 isDisposed,setSinkAndSubscription,dispose都是需要加鎖的桩了。RX 通過Atomic Operation 保證操作的原子性附帽。這里值的注意的是通過or設(shè)置具體標志位,通過&操作檢測具體相應(yīng)標志位井誉。

setSinkAndSubscription 為例子:

      let previousState = AtomicOr(DisposeState.sinkAndSubscriptionSet.rawValue, &_state)
        if (previousState & DisposeStateInt32.sinkAndSubscriptionSet.rawValue) != 0 {
            rxFatalError("Sink and subscription were already set")
        }

        if (previousState & DisposeStateInt32.disposed.rawValue) != 0 {
            sink.dispose()
            subscription.dispose()
            _sink = nil
            _subscription = nil
        }


現(xiàn)假設(shè)執(zhí)行setSinkAndSubscription_state = 0bxy

那么執(zhí)行 let previousState = AtomicOr(DisposeState.sinkAndSubscriptionSet.rawValue, &_state)

previousState = 0bxy

_state = 0bxy | 0b10 = 0b1y

也就是說這個操作最終導(dǎo)致_state 的第二位置1
previousState & DisposeStateInt32.sinkAndSubscriptionSet.rawValue

0bxy & 0b10 = 0bx0

x = 1 則最終結(jié)果為0b10,否則則為0b00, 再通過if語句即可檢測第二位是否為0.

這里還有一個小坑:

        if (previousState & DisposeStateInt32.disposed.rawValue) != 0 {
             print("triger  disposed in setSinkAndSubscription function \(self) \n \(Thread.current)")
            sink.dispose()
            subscription.dispose()
            _sink = nil
            _subscription = nil
        }

在檢測完setSinkAndSubscription flag之后蕉扮,立馬又檢測 disposed flag 如果為真則立即執(zhí)行dispose操作,也就是說一旦set disposed flag,則再設(shè)置setSinkAndSubscription 則是無效操作,這里我不確定具體是什么情況會觸發(fā)這個操作颗圣,不過我跑了下單元測試喳钟,確實某些case是會觸發(fā)這種情況,以下便是一個觸發(fā)該case的單元測試:

    func test1323() {
        func performSharingOperatorsTest(share: @escaping (Observable<Int>) -> Observable<Int>) {
            _ = share(Observable<Int>.create({ observer in
                    observer.on(.next(1))
                    Thread.sleep(forTimeInterval: 0.1)
                    observer.on(.completed)
                    return Disposables.create()
                })
                .flatMap { (int) -> Observable<Int> in
                    return Observable.create { (observer) -> Disposable in
                        DispatchQueue.global().async {
                            observer.onNext(int)
                            observer.onCompleted()
                        }
                        return Disposables.create()
                    }
                })
                .subscribe { (e) in
                }
        }

        for op in [
            { $0.share(replay: 0, scope: .whileConnected) },
            { $0.share(replay: 0, scope: .forever) },
            { $0.share(replay: 1, scope: .whileConnected) },
            { $0.share(replay: 1, scope: .forever) },
            { $0.share(replay: 2, scope: .whileConnected) },
            { $0.share(replay: 2, scope: .forever) },
            ] as [(Observable<Int>) -> Observable<Int>] {
            performSharingOperatorsTest(share: op)
        }
    }

但是具體怎么原理還有待細究在岂。

相關(guān)資料:

property屬性的atomic和nonatomic區(qū)別

理解Memory Barrier(內(nèi)存屏障)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末奔则,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子洁段,更是在濱河造成了極大的恐慌应狱,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,427評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件祠丝,死亡現(xiàn)場離奇詭異疾呻,居然都是意外死亡除嘹,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,551評論 3 395
  • 文/潘曉璐 我一進店門岸蜗,熙熙樓的掌柜王于貴愁眉苦臉地迎上來尉咕,“玉大人,你說我怎么就攤上這事璃岳∧甓校” “怎么了?”我有些...
    開封第一講書人閱讀 165,747評論 0 356
  • 文/不壞的土叔 我叫張陵铃慷,是天一觀的道長单芜。 經(jīng)常有香客問我,道長犁柜,這世上最難降的妖魔是什么洲鸠? 我笑而不...
    開封第一講書人閱讀 58,939評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮馋缅,結(jié)果婚禮上扒腕,老公的妹妹穿的比我還像新娘。我一直安慰自己萤悴,他們只是感情好瘾腰,可當(dāng)我...
    茶點故事閱讀 67,955評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著覆履,像睡著了一般蹋盆。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上内狗,一...
    開封第一講書人閱讀 51,737評論 1 305
  • 那天怪嫌,我揣著相機與錄音,去河邊找鬼柳沙。 笑死岩灭,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的赂鲤。 我是一名探鬼主播噪径,決...
    沈念sama閱讀 40,448評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼数初!你這毒婦竟也來了找爱?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,352評論 0 276
  • 序言:老撾萬榮一對情侶失蹤泡孩,失蹤者是張志新(化名)和其女友劉穎车摄,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,834評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡吮播,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,992評論 3 338
  • 正文 我和宋清朗相戀三年变屁,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片意狠。...
    茶點故事閱讀 40,133評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡粟关,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出环戈,到底是詐尸還是另有隱情闷板,我是刑警寧澤,帶...
    沈念sama閱讀 35,815評論 5 346
  • 正文 年R本政府宣布院塞,位于F島的核電站遮晚,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏迫悠。R本人自食惡果不足惜鹏漆,卻給世界環(huán)境...
    茶點故事閱讀 41,477評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望创泄。 院中可真熱鬧,春花似錦括蝠、人聲如沸鞠抑。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,022評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽搁拙。三九已至,卻和暖如春法绵,著一層夾襖步出監(jiān)牢的瞬間箕速,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,147評論 1 272
  • 我被黑心中介騙來泰國打工朋譬, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留盐茎,地道東北人。 一個月前我還...
    沈念sama閱讀 48,398評論 3 373
  • 正文 我出身青樓徙赢,卻偏偏與公主長得像字柠,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子狡赐,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,077評論 2 355

推薦閱讀更多精彩內(nèi)容

  • 開篇 一直覺得自己似乎越來越浮躁了窑业,可能當(dāng)代的大多數(shù)年輕人都活在恐慌里,問題已經(jīng)從小時候的不思進取變成了“太思進取...
    Maru閱讀 3,806評論 13 26
  • 當(dāng)程序員原來越浮躁了,項目做多了大都是雷同的, 對技術(shù)沒啥幫助,讀一些牛逼的第三方框架,有助于提升,關(guān)于RxSwi...
    水落斜陽閱讀 763評論 0 1
  • 轉(zhuǎn)載自:https://halfrost.com/go_map_chapter_one/ https://half...
    HuJay閱讀 6,154評論 1 5
  • 接著上節(jié) mutex枕屉,本節(jié)主要介紹atomic的內(nèi)容常柄,練習(xí)代碼地址。本文參考http://www.cplusplu...
    jorion閱讀 73,667評論 1 14
  • 上周日早晨,睡的迷迷糊糊西潘,手機叮咚一聲卷玉,銀行通知短信,常用的卡上收到筆轉(zhuǎn)賬秸架。 還在琢磨到底是什么錢揍庄。我媽的...
    一粒葵瓜子閱讀 331評論 0 0