internal void Update(long number) { switch (this.aggType) { case AggregationType.LongSumIncomingDelta: { Interlocked.Add(ref this.runningValue.AsLong, number); break; } case AggregationType.LongSumIncomingCumulative: { Interlocked.Exchange(ref this.runningValue.AsLong, number); break; } case AggregationType.LongGauge: { Interlocked.Exchange(ref this.runningValue.AsLong, number); break; } case AggregationType.Histogram: case AggregationType.HistogramSumCount: { this.Update((double)number); break; } } // There is a race with Snapshot: // Update() updates the value // Snapshot snapshots the value // Snapshot sets status to NoCollectPending // Update sets status to CollectPending -- this is not right as the Snapshot // already included the updated value. // In the absence of any new Update call until next Snapshot, // this results in exporting an Update even though // it had no update. // TODO: For Delta, this can be mitigated // by ignoring Zero points this.MetricPointStatus = MetricPointStatus.CollectPending; }
internal void TakeSnapshot(bool outputDelta) { switch (this.aggType) { case AggregationType.LongSumIncomingDelta: case AggregationType.LongSumIncomingCumulative: { if (outputDelta) { long initValue = Interlocked.Read(ref this.runningValue.AsLong); this.snapshotValue.AsLong = initValue - this.deltaLastValue.AsLong; this.deltaLastValue.AsLong = initValue; this.MetricPointStatus = MetricPointStatus.NoCollectPending; // Check again if value got updated, if yes reset status. // This ensures no Updates get Lost. if (initValue != Interlocked.Read(ref this.runningValue.AsLong)) { this.MetricPointStatus = MetricPointStatus.CollectPending; } } else { this.snapshotValue.AsLong = Interlocked.Read(ref this.runningValue.AsLong); } break; } case AggregationType.DoubleSumIncomingDelta: case AggregationType.DoubleSumIncomingCumulative: { if (outputDelta) { // TODO: // Is this thread-safe way to read double? // As long as the value is not -ve infinity, // the exchange (to 0.0) will never occur, // but we get the original value atomically. double initValue = Interlocked.CompareExchange(ref this.runningValue.AsDouble, 0.0, double.NegativeInfinity); this.snapshotValue.AsDouble = initValue - this.deltaLastValue.AsDouble; this.deltaLastValue.AsDouble = initValue; this.MetricPointStatus = MetricPointStatus.NoCollectPending; // Check again if value got updated, if yes reset status. // This ensures no Updates get Lost. if (initValue != Interlocked.CompareExchange(ref this.runningValue.AsDouble, 0.0, double.NegativeInfinity)) { this.MetricPointStatus = MetricPointStatus.CollectPending; } } else { // TODO: // Is this thread-safe way to read double? // As long as the value is not -ve infinity, // the exchange (to 0.0) will never occur, // but we get the original value atomically. this.snapshotValue.AsDouble = Interlocked.CompareExchange(ref this.runningValue.AsDouble, 0.0, double.NegativeInfinity); } break; } case AggregationType.LongGauge: { this.snapshotValue.AsLong = Interlocked.Read(ref this.runningValue.AsLong); this.MetricPointStatus = MetricPointStatus.NoCollectPending; // Check again if value got updated, if yes reset status. // This ensures no Updates get Lost. if (this.snapshotValue.AsLong != Interlocked.Read(ref this.runningValue.AsLong)) { this.MetricPointStatus = MetricPointStatus.CollectPending; } break; } case AggregationType.DoubleGauge: { // TODO: // Is this thread-safe way to read double? // As long as the value is not -ve infinity, // the exchange (to 0.0) will never occur, // but we get the original value atomically. this.snapshotValue.AsDouble = Interlocked.CompareExchange(ref this.runningValue.AsDouble, 0.0, double.NegativeInfinity); this.MetricPointStatus = MetricPointStatus.NoCollectPending; // Check again if value got updated, if yes reset status. // This ensures no Updates get Lost. if (this.snapshotValue.AsDouble != Interlocked.CompareExchange(ref this.runningValue.AsDouble, 0.0, double.NegativeInfinity)) { this.MetricPointStatus = MetricPointStatus.CollectPending; } break; } case AggregationType.Histogram: { lock (this.histogramBuckets.LockObject) { this.snapshotValue.AsLong = this.runningValue.AsLong; this.histogramBuckets.SnapshotSum = this.histogramBuckets.RunningSum; if (outputDelta) { this.runningValue.AsLong = 0; this.histogramBuckets.RunningSum = 0; } for (int i = 0; i < this.histogramBuckets.RunningBucketCounts.Length; i++) { this.histogramBuckets.SnapshotBucketCounts[i] = this.histogramBuckets.RunningBucketCounts[i]; if (outputDelta) { this.histogramBuckets.RunningBucketCounts[i] = 0; } } this.MetricPointStatus = MetricPointStatus.NoCollectPending; } break; } case AggregationType.HistogramSumCount: { lock (this.histogramBuckets.LockObject) { this.snapshotValue.AsLong = this.runningValue.AsLong; this.histogramBuckets.SnapshotSum = this.histogramBuckets.RunningSum; if (outputDelta) { this.runningValue.AsLong = 0; this.histogramBuckets.RunningSum = 0; } this.MetricPointStatus = MetricPointStatus.NoCollectPending; } break; } } }
internal void Update(double number) { switch (this.aggType) { case AggregationType.DoubleSumIncomingDelta: { double initValue, newValue; do { initValue = this.runningValue.AsDouble; newValue = initValue + number; }while (initValue != Interlocked.CompareExchange(ref this.runningValue.AsDouble, newValue, initValue)); break; } case AggregationType.DoubleSumIncomingCumulative: { Interlocked.Exchange(ref this.runningValue.AsDouble, number); break; } case AggregationType.DoubleGauge: { Interlocked.Exchange(ref this.runningValue.AsDouble, number); break; } case AggregationType.Histogram: { int i; for (i = 0; i < this.histogramBuckets.ExplicitBounds.Length; i++) { // Upper bound is inclusive if (number <= this.histogramBuckets.ExplicitBounds[i]) { break; } } lock (this.histogramBuckets.LockObject) { this.runningValue.AsLong++; this.histogramBuckets.RunningSum += number; this.histogramBuckets.RunningBucketCounts[i]++; } break; } case AggregationType.HistogramSumCount: { lock (this.histogramBuckets.LockObject) { this.runningValue.AsLong++; this.histogramBuckets.RunningSum += number; } break; } } // There is a race with Snapshot: // Update() updates the value // Snapshot snapshots the value // Snapshot sets status to NoCollectPending // Update sets status to CollectPending -- this is not right as the Snapshot // already included the updated value. // In the absence of any new Update call until next Snapshot, // this results in exporting an Update even though // it had no update. // TODO: For Delta, this can be mitigated // by ignoring Zero points this.MetricPointStatus = MetricPointStatus.CollectPending; }