Skip to content

Commit 49a6985

Browse files
authored
refactor(ios): performance API thread safety optimization (Tencent#4480)
1 parent ebef1fb commit 49a6985

File tree

2 files changed

+175
-21
lines changed

2 files changed

+175
-21
lines changed

framework/ios/base/bridge/HippyBridge+PerformanceAPI.h

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,15 @@
2424

2525
NS_ASSUME_NONNULL_BEGIN
2626

27+
/// Performance data dictionary keys
28+
HIPPY_EXTERN NSString *const HippyPerformanceKeyFP; // First Paint
29+
HIPPY_EXTERN NSString *const HippyPerformanceKeyFCP; // First Contentful Paint
30+
HIPPY_EXTERN NSString *const HippyPerformanceKeyInit; // Native Init
31+
HIPPY_EXTERN NSString *const HippyPerformanceKeyJSInit; // JS Engine Init
32+
HIPPY_EXTERN NSString *const HippyPerformanceKeyRunApp; // Run Application
33+
HIPPY_EXTERN NSString *const HippyPerformanceKeyDomCreate; // DOM Create
34+
HIPPY_EXTERN NSString *const HippyPerformanceKeyFirstFrame; // First Frame
35+
2736
/// Performance API Category of HippyBridge
2837
@interface HippyBridge (PerformanceAPI)
2938

@@ -34,11 +43,21 @@ NS_ASSUME_NONNULL_BEGIN
3443
/// Update FCP perf record.
3544
- (void)updatePerfRecordOnFirstContentfulPaintEnd;
3645

46+
/// Get all perf data (Thread-safe version with completion block)
47+
/// - Parameter completion: Completion block called with performance data on main thread
48+
- (void)getHippyInitPerformanceData:(void(^)(NSDictionary * _Nullable data))completion;
49+
50+
/// Get fcp perf data (Thread-safe version with completion block)
51+
/// - Parameter completion: Completion block called with performance data on main thread
52+
- (void)getFCPPerformanceData:(void(^)(NSDictionary * _Nullable data))completion;
53+
3754
/// Get all perf data
38-
- (NSDictionary *)getHippyInitPerformanceData;
55+
/// @warning This method is not thread-safe. Use getHippyInitPerformanceData: instead.
56+
- (NSDictionary *)getHippyInitPerformanceData __attribute__((deprecated("Use getHippyInitPerformanceData: instead")));
3957

4058
/// Get fcp perf data
41-
- (nullable NSDictionary *)getFCPPerformanceData;
59+
/// @warning This method is not thread-safe. Use getFCPPerformanceData: instead.
60+
- (NSDictionary *)getFCPPerformanceData __attribute__((deprecated("Use getFCPPerformanceData: instead")));
4261

4362
@end
4463

framework/ios/base/bridge/HippyBridge+PerformanceAPI.mm

Lines changed: 154 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,13 @@
2727
#import "footstone/string_view_utils.h"
2828

2929

30-
static NSString *const kHippyPerfKeyFP = @"FP";
31-
static NSString *const kHippyPerfKeyFCP = @"FCP";
32-
static NSString *const kHippyPerfKeyInit = @"NativeInit";
33-
static NSString *const kHippyPerfKeyJSInit = @"JsEngineInit";
34-
static NSString *const kHippyPerfKeyRunApp = @"RunApplication";
35-
static NSString *const kHippyPerfKeyDomCreate = @"DomCreate";
36-
static NSString *const kHippyPerfKeyFirstFrame = @"FirstFrame";
30+
NSString *const HippyPerformanceKeyFP = @"FP";
31+
NSString *const HippyPerformanceKeyFCP = @"FCP";
32+
NSString *const HippyPerformanceKeyInit = @"NativeInit";
33+
NSString *const HippyPerformanceKeyJSInit = @"JsEngineInit";
34+
NSString *const HippyPerformanceKeyRunApp = @"RunApplication";
35+
NSString *const HippyPerformanceKeyDomCreate = @"DomCreate";
36+
NSString *const HippyPerformanceKeyFirstFrame = @"FirstFrame";
3737

3838

3939
using namespace footstone;
@@ -107,6 +107,139 @@ - (void)updatePerfRecordOnFirstContentfulPaintEnd {
107107
}
108108
}
109109

110+
// MARK: - Thread-safe API with completion blocks
111+
112+
- (void)getHippyInitPerformanceData:(void(^)(NSDictionary * _Nullable data))completion {
113+
if (!completion) {
114+
return;
115+
}
116+
117+
std::shared_ptr<hippy::Scope> scope = self.javaScriptExecutor.pScope;
118+
if (!scope) {
119+
dispatch_async(dispatch_get_main_queue(), ^{
120+
completion(@{});
121+
});
122+
return;
123+
}
124+
125+
auto domManager = scope->GetDomManager().lock();
126+
auto performance = scope->GetPerformance();
127+
if (!domManager || !performance) {
128+
dispatch_async(dispatch_get_main_queue(), ^{
129+
completion(@{});
130+
});
131+
return;
132+
}
133+
134+
std::weak_ptr<hippy::DomManager> weak_domManager = domManager;
135+
std::weak_ptr<hippy::Performance> weak_performance = performance;
136+
std::vector<std::function<void()>> ops = {[weak_domManager, weak_performance, completion] {
137+
auto domManager = weak_domManager.lock();
138+
auto performance = weak_performance.lock();
139+
if (!domManager || !performance) {
140+
dispatch_async(dispatch_get_main_queue(), ^{
141+
completion(@{});
142+
});
143+
return;
144+
}
145+
146+
auto entry = performance->PerformanceNavigation(hippy::kPerfNavigationHippyInit);
147+
if (!entry) {
148+
dispatch_async(dispatch_get_main_queue(), ^{
149+
completion(@{});
150+
});
151+
return;
152+
}
153+
154+
NSMutableDictionary *dic = [NSMutableDictionary dictionary];
155+
int64_t totalFPTime = (entry->GetHippyFirstFrameEnd() - entry->GetHippyNativeInitStart()).ToMilliseconds();
156+
int64_t nativeInit = (entry->GetHippyNativeInitEnd() - entry->GetHippyNativeInitStart()).ToMilliseconds();
157+
int64_t jsEngineInit = (entry->GetHippyJsEngineInitEnd() - entry->GetHippyJsEngineInitStart()).ToMilliseconds();
158+
int64_t runApplication = (entry->GetHippyRunApplicationEnd() - entry->GetHippyRunApplicationStart()).ToMilliseconds();
159+
int64_t domCreate = (entry->GetHippyDomEnd() - entry->GetHippyDomStart()).ToMilliseconds();
160+
int64_t firstFrame = (entry->GetHippyFirstFrameEnd() - entry->GetHippyFirstFrameStart()).ToMilliseconds();
161+
dic[HippyPerformanceKeyFP] = @(totalFPTime);
162+
dic[HippyPerformanceKeyInit] = @(nativeInit);
163+
dic[HippyPerformanceKeyJSInit] = @(jsEngineInit);
164+
dic[HippyPerformanceKeyRunApp] = @(runApplication);
165+
dic[HippyPerformanceKeyDomCreate] = @(domCreate);
166+
dic[HippyPerformanceKeyFirstFrame] = @(firstFrame);
167+
168+
auto bundle_info_array = entry->GetBundleInfoArray();
169+
for (size_t i = 0; i < bundle_info_array.size(); ++i) {
170+
auto& info = bundle_info_array[i];
171+
auto url = StringViewUtils::ToStdString(StringViewUtils::ConvertEncoding(info.url_, string_view::Encoding::Utf8).utf8_value());
172+
NSString *urlStr = [NSString stringWithCString:url.c_str() encoding:[NSString defaultCStringEncoding]];
173+
if (urlStr) {
174+
int64_t exeTime = (info.execute_source_end_ - info.execute_source_start_).ToMilliseconds();
175+
[dic setObject:@(exeTime) forKey:urlStr.lastPathComponent];
176+
}
177+
}
178+
179+
NSDictionary *result = [dic copy];
180+
dispatch_async(dispatch_get_main_queue(), ^{
181+
completion(result);
182+
});
183+
}};
184+
185+
domManager->PostTask(hippy::Scene(std::move(ops)));
186+
}
187+
188+
- (void)getFCPPerformanceData:(void(^)(NSDictionary * _Nullable data))completion {
189+
if (!completion) {
190+
return;
191+
}
192+
193+
std::shared_ptr<hippy::Scope> scope = self.javaScriptExecutor.pScope;
194+
if (!scope) {
195+
dispatch_async(dispatch_get_main_queue(), ^{
196+
completion(@{});
197+
});
198+
return;
199+
}
200+
201+
auto domManager = scope->GetDomManager().lock();
202+
auto performance = scope->GetPerformance();
203+
if (!domManager || !performance) {
204+
dispatch_async(dispatch_get_main_queue(), ^{
205+
completion(@{});
206+
});
207+
return;
208+
}
209+
210+
std::weak_ptr<hippy::DomManager> weak_domManager = domManager;
211+
std::weak_ptr<hippy::Performance> weak_performance = performance;
212+
std::vector<std::function<void()>> ops = {[weak_domManager, weak_performance, completion] {
213+
auto domManager = weak_domManager.lock();
214+
auto performance = weak_performance.lock();
215+
if (!domManager || !performance) {
216+
dispatch_async(dispatch_get_main_queue(), ^{
217+
completion(@{});
218+
});
219+
return;
220+
}
221+
222+
auto entry = performance->PerformanceNavigation(hippy::kPerfNavigationHippyInit);
223+
if (!entry) {
224+
dispatch_async(dispatch_get_main_queue(), ^{
225+
completion(@{});
226+
});
227+
return;
228+
}
229+
230+
int64_t fcpTime = (entry->GetHippyFirstContentfulPaintEnd() - entry->GetHippyNativeInitStart()).ToMilliseconds();
231+
NSDictionary *result = @{ HippyPerformanceKeyFCP : @(fcpTime) };
232+
233+
dispatch_async(dispatch_get_main_queue(), ^{
234+
completion(result);
235+
});
236+
}};
237+
238+
domManager->PostTask(hippy::Scene(std::move(ops)));
239+
}
240+
241+
// MARK: - Deprecated non-thread-safe API
242+
110243
- (NSDictionary *)getHippyInitPerformanceData {
111244
std::shared_ptr<hippy::Scope> scope = self.javaScriptExecutor.pScope;
112245
if (!scope) {
@@ -127,20 +260,22 @@ - (NSDictionary *)getHippyInitPerformanceData {
127260
int64_t runApplication = (entry->GetHippyRunApplicationEnd() - entry->GetHippyRunApplicationStart()).ToMilliseconds();
128261
int64_t domCreate = (entry->GetHippyDomEnd() - entry->GetHippyDomStart()).ToMilliseconds();
129262
int64_t firstFrame = (entry->GetHippyFirstFrameEnd() - entry->GetHippyFirstFrameStart()).ToMilliseconds();
130-
dic[kHippyPerfKeyFP] = @(totalFPTime);
131-
dic[kHippyPerfKeyInit] = @(nativeInit);
132-
dic[kHippyPerfKeyJSInit] = @(jsEngineInit);
133-
dic[kHippyPerfKeyRunApp] = @(runApplication);
134-
dic[kHippyPerfKeyDomCreate] = @(domCreate);
135-
dic[kHippyPerfKeyFirstFrame] = @(firstFrame);
263+
dic[HippyPerformanceKeyFP] = @(totalFPTime);
264+
dic[HippyPerformanceKeyInit] = @(nativeInit);
265+
dic[HippyPerformanceKeyJSInit] = @(jsEngineInit);
266+
dic[HippyPerformanceKeyRunApp] = @(runApplication);
267+
dic[HippyPerformanceKeyDomCreate] = @(domCreate);
268+
dic[HippyPerformanceKeyFirstFrame] = @(firstFrame);
136269

137270
auto bundle_info_array = entry->GetBundleInfoArray();
138271
for (size_t i = 0; i < bundle_info_array.size(); ++i) {
139272
auto& info = bundle_info_array[i];
140273
auto url = StringViewUtils::ToStdString(StringViewUtils::ConvertEncoding(info.url_, string_view::Encoding::Utf8).utf8_value());
141274
NSString *urlStr = [NSString stringWithCString:url.c_str() encoding:[NSString defaultCStringEncoding]];
142-
int64_t exeTime = (info.execute_source_end_ - info.execute_source_start_).ToMilliseconds();
143-
[dic setObject:@(exeTime) forKey:urlStr.lastPathComponent];
275+
if (urlStr) {
276+
int64_t exeTime = (info.execute_source_end_ - info.execute_source_start_).ToMilliseconds();
277+
[dic setObject:@(exeTime) forKey:urlStr.lastPathComponent];
278+
}
144279
}
145280
return dic;
146281
}
@@ -150,19 +285,19 @@ - (NSDictionary *)getHippyInitPerformanceData {
150285
- (NSDictionary *)getFCPPerformanceData {
151286
std::shared_ptr<hippy::Scope> scope = self.javaScriptExecutor.pScope;
152287
if (!scope) {
153-
return nil;
288+
return @{};
154289
}
155290
auto domManager = scope->GetDomManager().lock();
156291
auto performance = scope->GetPerformance();
157292
if (domManager && performance) {
158293
auto entry = performance->PerformanceNavigation(hippy::kPerfNavigationHippyInit);
159294
if (!entry) {
160-
return nil;
295+
return @{};
161296
}
162297
int64_t fcpTime = (entry->GetHippyFirstContentfulPaintEnd() - entry->GetHippyNativeInitStart()).ToMilliseconds();
163-
return @{ kHippyPerfKeyFCP : @(fcpTime) };
298+
return @{ HippyPerformanceKeyFCP : @(fcpTime) };
164299
}
165-
return nil;
300+
return @{};
166301
}
167302

168303
@end

0 commit comments

Comments
 (0)