SFAnalyticsActivityTracker.m
1 /* 2 * Copyright (c) 2017 Apple Inc. All Rights Reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23 24 #if __OBJC2__ 25 26 #import "SFAnalyticsActivityTracker.h" 27 #import "SFAnalyticsActivityTracker+Internal.h" 28 #import "SFAnalytics.h" 29 #import <mach/mach_time.h> 30 #import "utilities/debugging.h" 31 32 @interface SFAnalyticsActivityTracker () 33 @property (readwrite) NSNumber * measurement; 34 @end 35 36 @implementation SFAnalyticsActivityTracker { 37 dispatch_queue_t _queue; 38 NSString* _name; 39 Class _clientClass; 40 uint64_t _start; 41 BOOL _canceled; 42 } 43 44 @synthesize measurement = _measurement; 45 46 - (instancetype)initWithName:(NSString*)name clientClass:(Class)className { 47 if (![name isKindOfClass:[NSString class]] || ![className isSubclassOfClass:[SFAnalytics class]] ) { 48 secerror("Cannot instantiate SFActivityTracker without name and client class"); 49 return nil; 50 } 51 52 if (self = [super init]) { 53 _queue = dispatch_queue_create("SFAnalyticsActivityTracker queue", DISPATCH_QUEUE_SERIAL_WITH_AUTORELEASE_POOL); 54 _name = name; 55 _clientClass = className; 56 _measurement = nil; 57 _canceled = NO; 58 _start = 0; 59 } 60 return self; 61 } 62 63 - (void)performAction:(void (^)(void))action 64 { 65 [self start]; 66 dispatch_sync(_queue, ^{ 67 action(); 68 }); 69 [self stop]; 70 } 71 72 - (void)start 73 { 74 if (_canceled) { 75 return; 76 } 77 NSAssert(_start == 0, @"SFAnalyticsActivityTracker user called start twice"); 78 _start = mach_absolute_time(); 79 } 80 81 - (void)stop 82 { 83 uint64_t end = mach_absolute_time(); 84 85 if (_canceled) { 86 _start = 0; 87 return; 88 } 89 NSAssert(_start != 0, @"SFAnalyticsActivityTracker user called stop w/o calling start"); 90 91 static mach_timebase_info_data_t sTimebaseInfo; 92 if ( sTimebaseInfo.denom == 0 ) { 93 (void)mach_timebase_info(&sTimebaseInfo); 94 } 95 96 _measurement = @([_measurement doubleValue] + (1.0f * (end - _start) * (1.0f * sTimebaseInfo.numer / sTimebaseInfo.denom))); 97 _start = 0; 98 } 99 100 - (void)stopWithEvent:(NSString*)eventName 101 result:(NSError* _Nullable)eventResultError 102 { 103 [self stop]; 104 105 [[_clientClass logger] logResultForEvent:eventName hardFailure:false result:eventResultError]; 106 } 107 108 - (void)cancel 109 { 110 _canceled = YES; 111 } 112 113 - (void)dealloc 114 { 115 if (_start != 0) { 116 [self stop]; 117 } 118 if (!_canceled && _measurement != nil) { 119 [[_clientClass logger] logMetric:_measurement withName:_name]; 120 } 121 } 122 123 @end 124 125 #endif