diff --git a/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin.h b/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin.h index 6cb5f6251..c27aaf46b 100644 --- a/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin.h +++ b/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin.h @@ -10,6 +10,7 @@ #import #import +#import #import "SKDescriptorMapper.h" #import "SKInvalidation.h" @@ -29,4 +30,7 @@ @end +/** Exposed for tests only. */ +SK_EXTERN_C dispatch_queue_t SKLayoutPluginSerialBackgroundQueue(void); + #endif diff --git a/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin.mm b/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin.mm index bb958fdc3..512738d52 100644 --- a/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin.mm +++ b/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin.mm @@ -258,7 +258,10 @@ [elements addObject:node]; } - [responder success:@{@"elements" : elements}]; + // Converting to folly::dynamic is expensive, do it on a bg queue: + dispatch_async(SKLayoutPluginSerialBackgroundQueue(), ^{ + [responder success:@{@"elements" : elements}]; + }); } - (void)onCallSetData:(NSString*)objectId @@ -560,4 +563,22 @@ @end +/** + Operations like converting NSDictionary to folly::dynamic can be expensive. + Do them on this serial background queue to avoid blocking the main thread. + (Of course, ideally we wouldn't bother with building NSDictionary objects + in the first place, in favor of just using folly::dynamic directly...) + */ +dispatch_queue_t SKLayoutPluginSerialBackgroundQueue(void) { + static dispatch_queue_t queue; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + queue = dispatch_queue_create("flipper.layout.bg", DISPATCH_QUEUE_SERIAL); + // This should be relatively high priority, to prevent Flipper lag. + dispatch_set_target_queue( + queue, dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0)); + }); + return queue; +} + #endif diff --git a/iOS/Plugins/FlipperKitLayoutPlugin/SonarKitLayoutPluginTests/SonarKitLayoutPluginTests.m b/iOS/Plugins/FlipperKitLayoutPlugin/SonarKitLayoutPluginTests/SonarKitLayoutPluginTests.m index 42ff5011c..41b375bdb 100644 --- a/iOS/Plugins/FlipperKitLayoutPlugin/SonarKitLayoutPluginTests/SonarKitLayoutPluginTests.m +++ b/iOS/Plugins/FlipperKitLayoutPlugin/SonarKitLayoutPluginTests/SonarKitLayoutPluginTests.m @@ -67,6 +67,11 @@ SonarReceiver receiver = connection.receivers[@"getNodes"]; receiver(@{@"ids" : @[]}, responder); + dispatch_barrier_sync( + SKLayoutPluginSerialBackgroundQueue(), + ^{ + }); + XCTAssertTrue(([responder.successes containsObject:@{@"elements" : @[]}])); } @@ -96,6 +101,11 @@ receiver( @{@"ids" : @[ @"testNode1", @"testNode2", @"testNode3" ]}, responder); + dispatch_barrier_sync( + SKLayoutPluginSerialBackgroundQueue(), + ^{ + }); + XCTAssertTrue(([responder.successes containsObject:@{ @"elements" : @[ @{ @@ -149,6 +159,11 @@ SonarReceiver getNodesCall = connection.receivers[@"getNodes"]; getNodesCall(@{@"ids" : @[ @"testNode1", @"testNode2" ]}, responder); + dispatch_barrier_sync( + SKLayoutPluginSerialBackgroundQueue(), + ^{ + }); + SonarReceiver setHighlighted = connection.receivers[@"setHighlighted"]; setHighlighted(@{@"id" : @"testNode2"}, responder); @@ -249,6 +264,11 @@ connection.receivers[@"getRoot"](@{}, responder); connection.receivers[@"getNodes"](@{@"ids" : @[ @"testNode2" ]}, responder); + dispatch_barrier_sync( + SKLayoutPluginSerialBackgroundQueue(), + ^{ + }); + // Modify the name of testNode3 connection.receivers[@"setData"]( @{