@@ -31,177 +31,21 @@ static void runLoopSourceCallback(void *info) {
3131
3232#pragma mark - ASDeallocQueue
3333
34- @interface ASDeallocQueueV1 : ASDeallocQueue
35- @end
36- @interface ASDeallocQueueV2 : ASDeallocQueue
37- @end
38-
39- @implementation ASDeallocQueue
34+ @implementation ASDeallocQueue {
35+ std::vector<CFTypeRef> _queue;
36+ ASDN::Mutex _lock;
37+ }
4038
4139+ (ASDeallocQueue *)sharedDeallocationQueue NS_RETURNS_RETAINED
4240{
4341 static ASDeallocQueue *deallocQueue = nil ;
4442 static dispatch_once_t onceToken;
4543 dispatch_once (&onceToken, ^{
46- if (ASActivateExperimentalFeature (ASExperimentalDeallocQueue)) {
47- deallocQueue = [[ASDeallocQueueV2 alloc ] init ];
48- } else {
49- deallocQueue = [[ASDeallocQueueV1 alloc ] init ];
50- }
44+ deallocQueue = [[ASDeallocQueue alloc ] init ];
5145 });
5246 return deallocQueue;
5347}
5448
55- - (void )releaseObjectInBackground : (id _Nullable __strong *)objectPtr
56- {
57- ASDisplayNodeFailAssert (@" Abstract method." );
58- }
59-
60- - (void )drain
61- {
62- ASDisplayNodeFailAssert (@" Abstract method." );
63- }
64-
65- @end
66-
67- @implementation ASDeallocQueueV1 {
68- NSThread *_thread;
69- NSCondition *_condition;
70- std::deque<id > _queue;
71- ASDN::RecursiveMutex _queueLock;
72- }
73-
74- - (void )releaseObjectInBackground : (id _Nullable __strong *)objectPtr
75- {
76- if (objectPtr != NULL && *objectPtr != nil ) {
77- ASDN::MutexLocker l (_queueLock);
78- _queue.push_back (*objectPtr);
79- *objectPtr = nil ;
80- }
81- }
82-
83- - (void )threadMain
84- {
85- @autoreleasepool {
86- __unsafe_unretained __typeof__ (self) weakSelf = self;
87- // 100ms timer. No resources are wasted in between, as the thread sleeps, and each check is fast.
88- // This time is fast enough for most use cases without excessive churn.
89- CFRunLoopTimerRef timer = CFRunLoopTimerCreateWithHandler (NULL , -1 , 0.1 , 0 , 0 , ^(CFRunLoopTimerRef timer) {
90- weakSelf->_queueLock .lock ();
91- if (weakSelf->_queue .size () == 0 ) {
92- weakSelf->_queueLock .unlock ();
93- return ;
94- }
95- // The scope below is entered while already locked. @autorelease is crucial here; see PR 2890.
96- __unused NSInteger count; // Prevent static analyzer warning if release build
97- @autoreleasepool {
98- #if ASRunLoopQueueLoggingEnabled
99- NSLog (@" ASDeallocQueue Processing: %lu objects destroyed" , weakSelf->_queue.size());
100- #endif
101- // Sometimes we release 10,000 objects at a time. Don't hold the lock while releasing.
102- std::deque<id > currentQueue = weakSelf->_queue ;
103- count = currentQueue.size ();
104- ASSignpostStartCustom (ASSignpostDeallocQueueDrain, self, count);
105- weakSelf->_queue = std::deque<id >();
106- weakSelf->_queueLock .unlock ();
107- currentQueue.clear ();
108- }
109- ASSignpostEndCustom (ASSignpostDeallocQueueDrain, self, count, ASSignpostColorDefault);
110- });
111-
112- CFRunLoopRef runloop = CFRunLoopGetCurrent ();
113- CFRunLoopAddTimer (runloop, timer, kCFRunLoopCommonModes );
114-
115- [_condition lock ];
116- [_condition signal ];
117- // At this moment, -init is signalled that the thread is guaranteed to be finished starting.
118- [_condition unlock ];
119-
120- // Keep processing events until the runloop is stopped.
121- CFRunLoopRun ();
122-
123- CFRunLoopTimerInvalidate (timer);
124- CFRunLoopRemoveTimer (runloop, timer, kCFRunLoopCommonModes );
125- CFRelease (timer);
126-
127- [_condition lock ];
128- [_condition signal ];
129- // At this moment, -stop is signalled that the thread is guaranteed to be finished exiting.
130- [_condition unlock ];
131- }
132- }
133-
134- - (instancetype )init
135- {
136- if ((self = [super init ])) {
137- _condition = [[NSCondition alloc ] init ];
138-
139- _thread = [[NSThread alloc ] initWithTarget: self selector: @selector (threadMain ) object: nil ];
140- _thread.name = @" ASDeallocQueue" ;
141-
142- // Use condition to ensure NSThread has finished starting.
143- [_condition lock ];
144- [_thread start ];
145- [_condition wait ];
146- [_condition unlock ];
147- }
148- return self;
149- }
150-
151- - (void )stop
152- {
153- if (!_thread) {
154- return ;
155- }
156-
157- [_condition lock ];
158- [self performSelector: @selector (_stop ) onThread: _thread withObject: nil waitUntilDone: NO ];
159- [_condition wait ];
160- // At this moment, the thread is guaranteed to be finished running.
161- [_condition unlock ];
162- _thread = nil ;
163- }
164-
165- - (void )drain
166- {
167- [self performSelector: @selector (_drain ) onThread: _thread withObject: nil waitUntilDone: YES ];
168- }
169-
170- - (void )_drain
171- {
172- while (true ) {
173- @autoreleasepool {
174- _queueLock.lock ();
175- std::deque<id > currentQueue = _queue;
176- _queue = std::deque<id >();
177- _queueLock.unlock ();
178-
179- if (currentQueue.empty ()) {
180- return ;
181- } else {
182- currentQueue.clear ();
183- }
184- }
185- }
186- }
187-
188- - (void )_stop
189- {
190- CFRunLoopStop (CFRunLoopGetCurrent ());
191- }
192-
193- - (void )dealloc
194- {
195- [self stop ];
196- }
197-
198- @end
199-
200- @implementation ASDeallocQueueV2 {
201- std::vector<CFTypeRef> _queue;
202- ASDN::Mutex _lock;
203- }
204-
20549- (void )dealloc
20650{
20751 ASDisplayNodeFailAssert (@" Singleton should not dealloc." );
0 commit comments