/ Analytics / SFAnalyticsActivityTracker.m
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