Skip to content

Commit

Permalink
RUMM-1615 Allow launch time earlier than `UIApplicationDidBecomeActiv…
Browse files Browse the repository at this point in the history
…eNotification`
  • Loading branch information
maxep committed Oct 18, 2021
1 parent a88103e commit 30b4afc
Show file tree
Hide file tree
Showing 6 changed files with 24 additions and 17 deletions.
6 changes: 3 additions & 3 deletions Sources/Datadog/Core/System/LaunchTimeProvider.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,15 @@ import _Datadog_Private
internal protocol LaunchTimeProviderType {
/// The app process launch duration (in seconds) measured as the time from loading the first SDK object into memory
/// to receiving `UIApplication.didBecomeActiveNotification` notification.
var launchTime: TimeInterval? { get }
var launchTime: TimeInterval { get }
}

internal class LaunchTimeProvider: LaunchTimeProviderType {
var launchTime: TimeInterval? {
var launchTime: TimeInterval {
// Even if __dd_private_AppLaunchTime() is using a lock behind the scenes, TSAN will report a data race if there are no synchronizations at this level.
objc_sync_enter(self)
let time = __dd_private_AppLaunchTime()
objc_sync_exit(self)
return time > 0 ? time : nil
return time
}
}
2 changes: 1 addition & 1 deletion Sources/Datadog/RUM/RUMMonitor/Scopes/RUMViewScope.swift
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,7 @@ internal class RUMViewScope: RUMScope, RUMContextProvider {
crash: nil,
error: nil,
id: dependencies.rumUUIDGenerator.generateUnique().toRUMDataFormat,
loadingTime: dependencies.launchTimeProvider.launchTime?.toInt64Nanoseconds,
loadingTime: dependencies.launchTimeProvider.launchTime.toInt64Nanoseconds,
longTask: nil,
resource: nil,
target: nil,
Expand Down
24 changes: 13 additions & 11 deletions Sources/_Datadog_Private/ObjcAppLaunchHandler.m
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@

// A Read-Write lock to allow concurrent reads of TimeToApplicationDidBecomeActive, unless the initial (and only) write is locking it.
static pthread_rwlock_t rwLock;
// The framework load time in seconds relative to the absolute reference date of Jan 1 2001 00:00:00 GMT.
static NSTimeInterval FrameworkLoadTime = 0.0;
// The time interval between the application starts and it's responsive and accepts touch events.
static NSTimeInterval TimeToApplicationDidBecomeActive = 0.0;

NS_INLINE NSTimeInterval QueryProcessStartTimeWithFallback(NSTimeInterval fallbackTime) {
Expand Down Expand Up @@ -41,14 +44,10 @@ NS_INLINE NSTimeInterval QueryProcessStartTimeWithFallback(NSTimeInterval fallba
return processStartTime;
}

NS_INLINE void ComputeTimeToApplicationDidBecomeActiveWithFallback(NSTimeInterval fallbackTime) {
if (TimeToApplicationDidBecomeActive > 0) {
return;
}

NS_INLINE NSTimeInterval ComputeProcessTimeFromStart() {
NSTimeInterval now = CFAbsoluteTimeGetCurrent();
NSTimeInterval processStartTime = QueryProcessStartTimeWithFallback(fallbackTime);
TimeToApplicationDidBecomeActive = now - processStartTime;
NSTimeInterval processStartTime = QueryProcessStartTimeWithFallback(FrameworkLoadTime);
return now - processStartTime;
}

@interface AppLaunchHandler : NSObject
Expand All @@ -58,19 +57,21 @@ @implementation AppLaunchHandler

+ (void)load {
// This is called at the `_Datadog_Private` load time, keep the work minimal
NSTimeInterval frameworkLoadTime = CFAbsoluteTimeGetCurrent();
id __block token = [NSNotificationCenter.defaultCenter
FrameworkLoadTime = CFAbsoluteTimeGetCurrent();

NSNotificationCenter * __weak center = NSNotificationCenter.defaultCenter;
id __block token = [center
addObserverForName:UIApplicationDidBecomeActiveNotification
object:nil
queue:NSOperationQueue.mainQueue
usingBlock:^(NSNotification *_){

pthread_rwlock_init(&rwLock, NULL);
pthread_rwlock_wrlock(&rwLock);
ComputeTimeToApplicationDidBecomeActiveWithFallback(frameworkLoadTime);
TimeToApplicationDidBecomeActive = ComputeProcessTimeFromStart();
pthread_rwlock_unlock(&rwLock);

[NSNotificationCenter.defaultCenter removeObserver:token];
[center removeObserver:token];
}];
}

Expand All @@ -79,6 +80,7 @@ + (void)load {
CFTimeInterval __dd_private_AppLaunchTime() {
pthread_rwlock_rdlock(&rwLock);
CFTimeInterval time = TimeToApplicationDidBecomeActive;
if (time == 0) time = ComputeProcessTimeFromStart();
pthread_rwlock_unlock(&rwLock);
return time;
}
5 changes: 5 additions & 0 deletions Sources/_Datadog_Private/include/ObjcAppLaunchHandler.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,9 @@

#import <CoreFoundation/CFDate.h>

/// Returns the time interval between startup of the application process and the
/// `UIApplicationDidBecomeActiveNotification`.
///
/// If the `UIApplicationDidBecomeActiveNotification` has not been reached yet,
/// it returns time interval between startup of the application process and now.
CFTimeInterval __dd_private_AppLaunchTime(void);
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ class LaunchTimeProviderTests: XCTestCase {
var values: [TimeInterval] = []
(0..<10).forEach { _ in
Thread.sleep(forTimeInterval: 0.01)
values.append(provider.launchTime!)
values.append(provider.launchTime)
}

// Then
Expand Down
2 changes: 1 addition & 1 deletion Tests/DatadogTests/Datadog/Mocks/CoreMocks.swift
Original file line number Diff line number Diff line change
Expand Up @@ -625,7 +625,7 @@ class DateCorrectorMock: DateCorrectorType {
}

struct LaunchTimeProviderMock: LaunchTimeProviderType {
var launchTime: TimeInterval? = nil
var launchTime: TimeInterval = 0
}

extension UserInfo: AnyMockable, RandomMockable {
Expand Down

0 comments on commit 30b4afc

Please sign in to comment.