10
10
#import < libkern/OSAtomic.h>
11
11
12
12
@interface RACSerialDisposable () {
13
- // A reference to the receiver's `disposable`. This variable must only be
14
- // modified atomically.
15
- //
16
- // If this is `self`, no `disposable` has been set, but the receiver has not
17
- // been disposed of yet. `self` is never stored retained.
18
- //
19
- // If this is `nil`, the receiver has been disposed.
13
+ // The receiver's `disposable`. This variable must only be referenced while
14
+ // _spinLock is held.
15
+ RACDisposable * _disposable;
16
+
17
+ // YES if the receiver has been disposed. This variable must only be modified
18
+ // while _spinLock is held.
19
+ BOOL _disposed;
20
+
21
+ // A spinlock to protect access to _disposable and _disposed.
20
22
//
21
- // Otherwise, this is a retained reference to the inner disposable and the
22
- // receiver has not been disposed of yet .
23
- void * volatile _disposablePtr ;
23
+ // It must be used when _disposable is mutated or retained and when _disposed
24
+ // is mutated .
25
+ OSSpinLock _spinLock ;
24
26
}
25
27
26
28
@end
@@ -30,12 +32,17 @@ @implementation RACSerialDisposable
30
32
#pragma mark Properties
31
33
32
34
- (BOOL )isDisposed {
33
- return _disposablePtr == nil ;
35
+ return _disposed ;
34
36
}
35
37
36
38
- (RACDisposable *)disposable {
37
- RACDisposable *disposable = (__bridge id )_disposablePtr;
38
- return (disposable == self ? nil : disposable);
39
+ RACDisposable *result;
40
+
41
+ OSSpinLockLock (&_spinLock);
42
+ result = _disposable;
43
+ OSSpinLockUnlock (&_spinLock);
44
+
45
+ return result;
39
46
}
40
47
41
48
- (void )setDisposable : (RACDisposable *)disposable {
@@ -50,16 +57,6 @@ + (instancetype)serialDisposableWithDisposable:(RACDisposable *)disposable {
50
57
return serialDisposable;
51
58
}
52
59
53
- - (id )init {
54
- self = [super init ];
55
- if (self == nil ) return nil ;
56
-
57
- _disposablePtr = (__bridge void *)self;
58
- OSMemoryBarrier ();
59
-
60
- return self;
61
- }
62
-
63
60
- (id )initWithBlock : (void (^)(void ))block {
64
61
self = [self init ];
65
62
if (self == nil ) return nil ;
@@ -69,65 +66,42 @@ - (id)initWithBlock:(void (^)(void))block {
69
66
return self;
70
67
}
71
68
72
- - (void )dealloc {
73
- self.disposable = nil ;
74
- }
75
-
76
69
#pragma mark Inner Disposable
77
70
78
71
- (RACDisposable *)swapInDisposable : (RACDisposable *)newDisposable {
79
- void * const selfPtr = (__bridge void *)self ;
80
-
81
- // Only retain the new disposable if it's not `self`.
82
- // Take ownership before attempting the swap so that a subsequent swap
83
- // receives an owned reference.
84
- void *newDisposablePtr = selfPtr;
85
- if (newDisposable != nil ) {
86
- newDisposablePtr = ( void *) CFBridgingRetain ( newDisposable) ;
72
+ RACDisposable *existingDisposable ;
73
+ BOOL alreadyDisposed;
74
+
75
+ OSSpinLockLock (&_spinLock);
76
+ alreadyDisposed = _disposed;
77
+ if (!alreadyDisposed) {
78
+ existingDisposable = _disposable;
79
+ _disposable = newDisposable;
87
80
}
81
+ OSSpinLockUnlock (&_spinLock);
88
82
89
- void *existingDisposablePtr;
90
- // Keep trying while we're not disposed.
91
- while ((existingDisposablePtr = _disposablePtr) != NULL ) {
92
- if (!OSAtomicCompareAndSwapPtrBarrier (existingDisposablePtr, newDisposablePtr, &_disposablePtr)) {
93
- continue ;
94
- }
95
-
96
- // Return nil if _disposablePtr was set to self. Otherwise, release
97
- // the old value and return it as an object.
98
- if (existingDisposablePtr == selfPtr) {
99
- return nil ;
100
- } else {
101
- return CFBridgingRelease (existingDisposablePtr);
102
- }
83
+ if (alreadyDisposed) {
84
+ [newDisposable dispose ];
85
+ return nil ;
103
86
}
104
87
105
- // At this point, we've found out that we were already disposed.
106
- [newDisposable dispose ];
107
-
108
- // Failed to swap, clean up the ownership we took prior to the swap.
109
- if (newDisposable != nil ) {
110
- CFRelease (newDisposablePtr);
111
- }
112
-
113
- return nil ;
88
+ return existingDisposable;
114
89
}
115
90
116
91
#pragma mark Disposal
117
92
118
93
- (void )dispose {
119
- void *existingDisposablePtr;
120
-
121
- while ((existingDisposablePtr = _disposablePtr) != NULL ) {
122
- if (OSAtomicCompareAndSwapPtrBarrier (existingDisposablePtr, NULL , &_disposablePtr)) {
123
- if (existingDisposablePtr != (__bridge void *)self) {
124
- RACDisposable *existingDisposable = CFBridgingRelease (existingDisposablePtr);
125
- [existingDisposable dispose ];
126
- }
94
+ RACDisposable *existingDisposable;
127
95
128
- break ;
129
- }
96
+ OSSpinLockLock (&_spinLock);
97
+ if (!_disposed) {
98
+ existingDisposable = _disposable;
99
+ _disposed = YES ;
100
+ _disposable = nil ;
130
101
}
102
+ OSSpinLockUnlock (&_spinLock);
103
+
104
+ [existingDisposable dispose ];
131
105
}
132
106
133
107
@end
0 commit comments