private IMetricSeriesAggregator GetOrCreateAggregator(MetricAggregationCycleKind aggregationCycleKind, ref WeakReference <IMetricSeriesAggregator> aggregatorWeakRef)
        {
            while (true)
            {
                // Local cache for the reference in case of concurrnet updates:
                WeakReference <IMetricSeriesAggregator> currentAggregatorWeakRef = aggregatorWeakRef;

                // Try to dereference the weak reference:
                IMetricSeriesAggregator aggregatorHardRef = UnwrapAggregator(currentAggregatorWeakRef);
                if (aggregatorHardRef != null)
                {
                    return(aggregatorHardRef);
                }

                // END OF FAST PATH. Could not dereference aggregator. Will attempt to create it...

                // So aggretator is NULL. For non-default cycle kinds, see if the there is even a cycle hooked up:

                if (aggregationCycleKind != CycleKind.Default)
                {
                    IMetricSeriesFilter dataSeriesFilter;
                    if (false == this.aggregationManager.IsCycleActive(aggregationCycleKind, out dataSeriesFilter))
                    {
                        return(null);
                    }

                    // Ok, a cycle is, indeed, acgive up. See if the cycle's filter is interested in this series:

                    // Respect the filter. Note: Filter may be user code. If user code is broken, assume we accept the series.
                    IMetricValueFilter valuesFilter;
                    if (false == Util.FilterWillConsume(dataSeriesFilter, this, out valuesFilter))
                    {
                        return(null);
                    }
                }

                // Ok, they want to consume us. Create new aggregator and a weak reference to it:

                IMetricSeriesAggregator newAggregator = this.GetNewOrRecycledAggregatorInstance(aggregationCycleKind);
                WeakReference <IMetricSeriesAggregator> newAggregatorWeakRef = new WeakReference <IMetricSeriesAggregator>(newAggregator, trackResurrection: false);

                // Store the weak reference to the aggregator. However, there is a race on doing it, so check for it:
                WeakReference <IMetricSeriesAggregator> prevAggregatorWeakRef = Interlocked.CompareExchange(ref aggregatorWeakRef, newAggregatorWeakRef, currentAggregatorWeakRef);
                if (prevAggregatorWeakRef == currentAggregatorWeakRef)
                {
                    // We won the race and stored the aggregator. Now tell the manager about it and go on using it.
                    // Note that the status of IsCycleActive and the dataSeriesFilter may have changed concurrently.
                    // So it is essential that we do this after the above interlocked assignment of aggregator.
                    // It ensures that only objects are added to the aggregator collection that are also referenced by the data series.
                    // In addition, AddAggregator(..) always uses the current value of the aggregator-collection in a thread-safe manner.
                    // Becasue the aggregator collection reference is always updated before telling the aggregators to cycle,
                    // we know that any aggregator added will be eventually cycled.
                    // If adding succeeds, AddAggregator(..) will have set the correct filter.
                    bool added = this.aggregationManager.AddAggregator(newAggregator, aggregationCycleKind);

                    // If the manager does not accept the addition, it means that the aggregationCycleKind is disabled or that the filter is not interested in this data series.
                    // Either way we need to give up.
                    if (added)
                    {
                        return(newAggregator);
                    }
                    else
                    {
                        // We could have accepted some values into this aggregator in the short time it was set in this series. We will loose those values.
                        Interlocked.CompareExchange(ref aggregatorWeakRef, null, newAggregatorWeakRef);
                        return(null);
                    }
                }

                // We lost the race and a different aggregator was used. Loop again. Doing so will attempt to dereference the latest aggregator reference.
            }
        }
        internal MetricSeries(
            MetricAggregationManager aggregationManager,
            MetricIdentifier metricIdentifier,
            IEnumerable <KeyValuePair <string, string> > dimensionNamesAndValues,
            IMetricSeriesConfiguration configuration)
        {
            // Validate and store aggregationManager:
            Util.ValidateNotNull(aggregationManager, nameof(aggregationManager));
            this.aggregationManager = aggregationManager;

            // Validate and store metricIdentifier:
            Util.ValidateNotNull(metricIdentifier, nameof(metricIdentifier));
            this.MetricIdentifier = metricIdentifier;

            // Copy dimensionNamesAndValues, validate values (keys are implicitly validated as they need to match the keys in the identifier):
            var dimNameVals = new Dictionary <string, string>();

            if (dimensionNamesAndValues != null)
            {
                int dimIndex = 0;
                foreach (KeyValuePair <string, string> dimNameVal in dimensionNamesAndValues)
                {
                    if (dimNameVal.Value == null)
                    {
                        throw new ArgumentNullException(Invariant($"The value for dimension '{dimNameVal.Key}' number is null."));
                    }

                    if (String.IsNullOrWhiteSpace(dimNameVal.Value))
                    {
                        throw new ArgumentNullException(Invariant($"The value for dimension '{dimNameVal.Key}' is empty or white-space."));
                    }

                    dimNameVals[dimNameVal.Key] = dimNameVal.Value;
                    dimIndex++;
                }
            }

            // Validate that metricIdentifier and dimensionNamesAndValues contain consistent dimension names:
            if (metricIdentifier.DimensionsCount != dimNameVals.Count)
            {
                throw new ArgumentException(Invariant($"The specified {nameof(metricIdentifier)} contains {metricIdentifier.DimensionsCount} dimensions,")
                                            + Invariant($" however the specified {nameof(dimensionNamesAndValues)} contains {dimNameVals.Count} name-value pairs with unique names."));
            }

            foreach (string dimName in metricIdentifier.GetDimensionNames())
            {
                if (false == dimNameVals.ContainsKey(dimName))
                {
                    throw new ArgumentException(Invariant($"The specified {nameof(metricIdentifier)} contains a dimension named \"{dimName}\",")
                                                + Invariant($" however the specified {nameof(dimensionNamesAndValues)} does not contain an entry for that name."));
                }
            }

            // Store copied dimensionNamesAndValues:
            this.dimensionNamesAndValues = dimNameVals;

            // Validate and store configuration:
            Util.ValidateNotNull(configuration, nameof(configuration));
            this.configuration = configuration;
            this.requiresPersistentAggregator = configuration.RequiresPersistentAggregation;

            // Init other instance vars:
            this.aggregatorPersistent = null;
            this.aggregatorDefault    = null;
            this.aggregatorQuickPulse = null;
            this.aggregatorCustom     = null;
        }