Create a new dealloc queue that is more efficient (#931)

* Fork dealloc queue in an experiment

* Fix and put back

* Use the right selector

* Go simpler

* Clarify name

* Type inference

* Use CFTypeRefs like a boss

* Improve comments

* License header
This commit is contained in:
Adlai Holler 2018-05-22 09:06:24 -07:00 committed by GitHub
parent b2e5f9ec64
commit 8b890f07be
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 107 additions and 25 deletions

View File

@ -54,6 +54,7 @@
- [ASCollectionNode/ASTableNode] Fix a crash occurs while remeasuring cell nodes. [Huy Nguyen](https://github.com/nguyenhuy) [#917](https://github.com/TextureGroup/Texture/pull/917) - [ASCollectionNode/ASTableNode] Fix a crash occurs while remeasuring cell nodes. [Huy Nguyen](https://github.com/nguyenhuy) [#917](https://github.com/TextureGroup/Texture/pull/917)
- Fix an issue where ASConfigurationDelegate would not call out for "control" users. If set, it now receives events whenever an experimental feature decision point occurs, whether it's enabled or not. [Adlai Holler](https://github.com/Adlai-Holler) - Fix an issue where ASConfigurationDelegate would not call out for "control" users. If set, it now receives events whenever an experimental feature decision point occurs, whether it's enabled or not. [Adlai Holler](https://github.com/Adlai-Holler)
- [ASDisplayNode] Fix an issue that causes a node to sometimes return an outdated calculated size or size range. [Huy Nguyen](https://github.com/nguyenhuy) [#808](https://github.com/TextureGroup/Texture/pull/808) - [ASDisplayNode] Fix an issue that causes a node to sometimes return an outdated calculated size or size range. [Huy Nguyen](https://github.com/nguyenhuy) [#808](https://github.com/TextureGroup/Texture/pull/808)
- Add an experimental deallocation queue implementation that's more efficient. [Adlai Holler](https://github.com/Adlai-Holler)
## 2.6 ## 2.6
- [Xcode 9] Updated to require Xcode 9 (to fix warnings) [Garrett Moon](https://github.com/garrettmoon) - [Xcode 9] Updated to require Xcode 9 (to fix warnings) [Garrett Moon](https://github.com/garrettmoon)

View File

@ -18,7 +18,8 @@
"exp_interface_state_coalesce", "exp_interface_state_coalesce",
"exp_unfair_lock", "exp_unfair_lock",
"exp_infer_layer_defaults", "exp_infer_layer_defaults",
"exp_network_image_queue" "exp_network_image_queue",
"exp_dealloc_queue_v2"
] ]
} }
} }

View File

@ -26,6 +26,7 @@ typedef NS_OPTIONS(NSUInteger, ASExperimentalFeatures) {
ASExperimentalUnfairLock = 1 << 3, // exp_unfair_lock ASExperimentalUnfairLock = 1 << 3, // exp_unfair_lock
ASExperimentalLayerDefaults = 1 << 4, // exp_infer_layer_defaults ASExperimentalLayerDefaults = 1 << 4, // exp_infer_layer_defaults
ASExperimentalNetworkImageQueue = 1 << 5, // exp_network_image_queue ASExperimentalNetworkImageQueue = 1 << 5, // exp_network_image_queue
ASExperimentalDeallocQueue = 1 << 6, // exp_dealloc_queue_v2
ASExperimentalFeatureAll = 0xFFFFFFFF ASExperimentalFeatureAll = 0xFFFFFFFF
}; };

View File

@ -19,7 +19,8 @@ NSArray<NSString *> *ASExperimentalFeaturesGetNames(ASExperimentalFeatures flags
@"exp_interface_state_coalesce", @"exp_interface_state_coalesce",
@"exp_unfair_lock", @"exp_unfair_lock",
@"exp_infer_layer_defaults", @"exp_infer_layer_defaults",
@"exp_network_image_queue"])); @"exp_network_image_queue",
@"exp_dealloc_queue_v2"]));
if (flags == ASExperimentalFeatureAll) { if (flags == ASExperimentalFeatureAll) {
return allNames; return allNames;

View File

@ -76,14 +76,12 @@ AS_SUBCLASSING_RESTRICTED
@end @end
AS_SUBCLASSING_RESTRICTED
@interface ASDeallocQueue : NSObject @interface ASDeallocQueue : NSObject
@property (class, atomic, readonly) ASDeallocQueue *sharedDeallocationQueue; @property (class, atomic, readonly) ASDeallocQueue *sharedDeallocationQueue;
+ (ASDeallocQueue *)sharedDeallocationQueue NS_RETURNS_RETAINED; + (ASDeallocQueue *)sharedDeallocationQueue NS_RETURNS_RETAINED;
- (void)test_drain; - (void)drain;
- (void)releaseObjectInBackground:(id __strong _Nullable * _Nonnull)objectPtr; - (void)releaseObjectInBackground:(id __strong _Nullable * _Nonnull)objectPtr;

View File

@ -39,23 +39,41 @@ static void runLoopSourceCallback(void *info) {
#pragma mark - ASDeallocQueue #pragma mark - ASDeallocQueue
@implementation ASDeallocQueue { @interface ASDeallocQueueV1 : ASDeallocQueue
NSThread *_thread; @end
NSCondition *_condition; @interface ASDeallocQueueV2 : ASDeallocQueue
std::deque<id> _queue; @end
ASDN::RecursiveMutex _queueLock;
} @implementation ASDeallocQueue
+ (ASDeallocQueue *)sharedDeallocationQueue NS_RETURNS_RETAINED + (ASDeallocQueue *)sharedDeallocationQueue NS_RETURNS_RETAINED
{ {
static ASDeallocQueue *deallocQueue = nil; static ASDeallocQueue *deallocQueue = nil;
static dispatch_once_t onceToken; static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{ dispatch_once(&onceToken, ^{
deallocQueue = [[ASDeallocQueue alloc] init]; if (ASActivateExperimentalFeature(ASExperimentalDeallocQueue)) {
deallocQueue = [[ASDeallocQueueV2 alloc] init];
} else {
deallocQueue = [[ASDeallocQueueV1 alloc] init];
}
}); });
return deallocQueue; return deallocQueue;
} }
- (void)releaseObjectInBackground:(id _Nullable __strong *)objectPtr
{
ASDisplayNodeFailAssert(@"Abstract method.");
}
@end
@implementation ASDeallocQueueV1 {
NSThread *_thread;
NSCondition *_condition;
std::deque<id> _queue;
ASDN::RecursiveMutex _queueLock;
}
- (void)releaseObjectInBackground:(id _Nullable __strong *)objectPtr - (void)releaseObjectInBackground:(id _Nullable __strong *)objectPtr
{ {
if (objectPtr != NULL && *objectPtr != nil) { if (objectPtr != NULL && *objectPtr != nil) {
@ -147,12 +165,12 @@ static void runLoopSourceCallback(void *info) {
_thread = nil; _thread = nil;
} }
- (void)test_drain - (void)drain
{ {
[self performSelector:@selector(_test_drain) onThread:_thread withObject:nil waitUntilDone:YES]; [self performSelector:@selector(_drain) onThread:_thread withObject:nil waitUntilDone:YES];
} }
- (void)_test_drain - (void)_drain
{ {
while (true) { while (true) {
@autoreleasepool { @autoreleasepool {
@ -182,6 +200,57 @@ static void runLoopSourceCallback(void *info) {
@end @end
@implementation ASDeallocQueueV2 {
std::vector<CFTypeRef> _queue;
ASDN::Mutex _lock;
}
- (void)dealloc
{
ASDisplayNodeFailAssert(@"Singleton should not dealloc.");
}
- (void)releaseObjectInBackground:(id _Nullable __strong *)objectPtr
{
NSParameterAssert(objectPtr != NULL);
// Cast to CFType so we can manipulate retain count manually.
auto cfPtr = (CFTypeRef *)(void *)objectPtr;
if (!cfPtr || !*cfPtr) {
return;
}
_lock.lock();
auto isFirstEntry = _queue.empty();
// Push the pointer into our queue and clear their pointer.
// This "steals" the +1 from ARC and nils their pointer so they can't
// access or release the object.
_queue.push_back(*cfPtr);
*cfPtr = NULL;
_lock.unlock();
if (isFirstEntry) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.100 * NSEC_PER_SEC)), dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0), ^{
[self drain];
});
}
}
- (void)drain
{
@autoreleasepool {
_lock.lock();
auto q = std::move(_queue);
_lock.unlock();
for (auto ref : q) {
// NOTE: Could check that retain count is 1 and retry later if not.
CFRelease(ref);
}
}
}
@end
#if AS_KDEBUG_ENABLE #if AS_KDEBUG_ENABLE
/** /**
* This is real, private CA API. Valid as of iOS 10. * This is real, private CA API. Valid as of iOS 10.

View File

@ -95,7 +95,7 @@ static __weak ASTestCase *currentTestCase;
} }
// Now that the autorelease pool is drained, drain the dealloc queue also. // Now that the autorelease pool is drained, drain the dealloc queue also.
[[ASDeallocQueue sharedDeallocationQueue] test_drain]; [[ASDeallocQueue sharedDeallocationQueue] drain];
} }
+ (ASTestCase *)currentTestCase + (ASTestCase *)currentTestCase

View File

@ -1,18 +1,18 @@
// //
// AppDelegate.m // AppDelegate.m
// Sample // Texture
// //
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
// This source code is licensed under the BSD-style license found in the // This source code is licensed under the BSD-style license found in the
// LICENSE file in the root directory of this source tree. An additional grant // LICENSE file in the /ASDK-Licenses directory of this source tree. An additional
// of patent rights can be found in the PATENTS file in the same directory. // grant of patent rights can be found in the PATENTS file in the same directory.
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // Modifications to this file made after 4/13/2017 are: Copyright (c) through the present,
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License");
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL // you may not use this file except in compliance with the License.
// FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN // You may obtain a copy of the License at
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN //
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // http://www.apache.org/licenses/LICENSE-2.0
// //
#import "AppDelegate.h" #import "AppDelegate.h"
@ -55,3 +55,14 @@
} }
@end @end
@implementation ASConfiguration (UserProvided)
+ (ASConfiguration *)textureConfiguration
{
ASConfiguration *cfg = [[ASConfiguration alloc] init];
cfg.experimentalFeatures = ASExperimentalDeallocQueue;
return cfg;
}
@end