2  * Copyright (c) 2017 Apple Inc. All Rights Reserved.
 
   4  * @APPLE_LICENSE_HEADER_START@
 
   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
 
  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.
 
  21  * @APPLE_LICENSE_HEADER_END@
 
  26 #import "SFAnalyticsMultiSampler+Internal.h"
 
  27 #import "SFAnalytics+Internal.h"
 
  28 #import "SFAnalyticsDefines.h"
 
  29 #import "utilities/debugging.h"
 
  31 #include <dispatch/dispatch.h>
 
  33 @implementation SFAnalyticsMultiSampler {
 
  34     NSTimeInterval _samplingInterval;
 
  35     dispatch_source_t _timer;
 
  37     MultiSamplerDictionary (^_block)(void);
 
  38     int _notificationToken;
 
  44 @synthesize name = _name;
 
  45 @synthesize samplingInterval = _samplingInterval;
 
  47 - (instancetype)initWithName:(NSString*)name interval:(NSTimeInterval)interval block:(MultiSamplerDictionary (^)(void))block clientClass:(Class)clientClass
 
  49     if (self = [super init]) {
 
  50         if (![clientClass isSubclassOfClass:[SFAnalytics class]]) {
 
  51             secerror("SFAnalyticsSampler created without valid client class (%@)", clientClass);
 
  55         if (!name || (interval < 1.0f && interval != SFAnalyticsSamplerIntervalOncePerReport) || !block) {
 
  56             secerror("SFAnalyticsSampler created without proper data");
 
  60         _clientClass = clientClass;
 
  63         _samplingInterval = interval;
 
  75     _oncePerReport = (_samplingInterval == SFAnalyticsSamplerIntervalOncePerReport);
 
  77         [self setupOnceTimer];
 
  79         [self setupPeriodicTimer];
 
  83 - (void)setupOnceTimer
 
  85     __weak __typeof(self) weakSelf = self;
 
  86     notify_register_dispatch(SFAnalyticsFireSamplersNotification, &_notificationToken, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(int token) {
 
  87         __strong __typeof(self) strongSelf = weakSelf;
 
  89             secnotice("SFAnalyticsSampler", "sampler went away before we could run its once-per-report block");
 
  94         MultiSamplerDictionary data = strongSelf->_block();
 
  95         [data enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, NSNumber * _Nonnull obj, BOOL * _Nonnull stop) {
 
  96             [[strongSelf->_clientClass logger] logMetric:obj withName:key oncePerReport:strongSelf->_oncePerReport];
 
 102 - (void)setupPeriodicTimer
 
 104     _timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0));
 
 105     dispatch_source_set_timer(_timer, dispatch_walltime(0, _samplingInterval * NSEC_PER_SEC), _samplingInterval * NSEC_PER_SEC, _samplingInterval * NSEC_PER_SEC / 50.0);    // give 2% leeway on timer
 
 107     __weak __typeof(self) weakSelf = self;
 
 108     dispatch_source_set_event_handler(_timer, ^{
 
 109         __strong __typeof(self) strongSelf = weakSelf;
 
 111             secnotice("SFAnalyticsSampler", "sampler went away before we could run its once-per-report block");
 
 115         MultiSamplerDictionary data = strongSelf->_block();
 
 116         [data enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, NSNumber * _Nonnull obj, BOOL * _Nonnull stop) {
 
 117             [[strongSelf->_clientClass logger] logMetric:obj withName:key oncePerReport:strongSelf->_oncePerReport];
 
 120     dispatch_resume(_timer);
 
 125 - (void)setSamplingInterval:(NSTimeInterval)interval
 
 127     if (interval < 1.0f && !(interval == SFAnalyticsSamplerIntervalOncePerReport)) {
 
 128         secerror("SFAnalyticsSampler: interval %f is not supported", interval);
 
 132     _samplingInterval = interval;
 
 136 - (NSTimeInterval)samplingInterval {
 
 137     return _samplingInterval;
 
 140 - (MultiSamplerDictionary)sampleNow
 
 142     MultiSamplerDictionary data = _block();
 
 143     [data enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, NSNumber * _Nonnull obj, BOOL * _Nonnull stop) {
 
 144         [[self->_clientClass logger] logMetric:obj withName:key oncePerReport:self->_oncePerReport];
 
 149 - (void)pauseSampling
 
 155     if (_oncePerReport) {
 
 156         notify_cancel(_notificationToken);
 
 157         _notificationToken = 0;
 
 159         dispatch_source_cancel(_timer);
 
 164 - (void)resumeSampling
 
 171     [self pauseSampling];