Skip to content

Commit

Permalink
Synchronize on a lock object
Browse files Browse the repository at this point in the history
  • Loading branch information
twcrone committed Feb 25, 2022
1 parent 8f0708c commit ef15db0
Show file tree
Hide file tree
Showing 2 changed files with 140 additions and 94 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ public class ResponseTimeStatsImpl extends AbstractStats implements ResponseTime
private static final long NANOSECONDS_PER_SECOND_SQUARED = TimeConversion.NANOSECONDS_PER_SECOND
* TimeConversion.NANOSECONDS_PER_SECOND;

private final Object lock = new Object();

private long total;
private long totalExclusive;
private long minValue;
Expand All @@ -36,12 +38,14 @@ protected ResponseTimeStatsImpl() {
@Override
public Object clone() throws CloneNotSupportedException {
ResponseTimeStatsImpl newStats = new ResponseTimeStatsImpl();
newStats.count = count;
newStats.total = total;
newStats.totalExclusive = totalExclusive;
newStats.minValue = minValue;
newStats.maxValue = maxValue;
newStats.sumOfSquares = sumOfSquares;
synchronized (lock) {
newStats.count = count;
newStats.total = total;
newStats.totalExclusive = totalExclusive;
newStats.minValue = minValue;
newStats.maxValue = maxValue;
newStats.sumOfSquares = sumOfSquares;
}
return newStats;
}

Expand All @@ -67,94 +71,116 @@ public void recordResponseTimeInNanos(long responseTime) {
public void recordResponseTimeInNanos(long responseTime, long exclusiveTime) {
double responseTimeAsDouble = responseTime;
responseTimeAsDouble *= responseTimeAsDouble;
sumOfSquares += responseTimeAsDouble;
if (count > 0) {
minValue = Math.min(responseTime, minValue);
} else {
minValue = responseTime;
}
count++;
total += responseTime;
maxValue = Math.max(responseTime, maxValue);
totalExclusive += exclusiveTime;
if (NewRelic.getAgent().getConfig().getValue(AgentConfigImpl.METRIC_DEBUG, AgentConfigImpl.DEFAULT_METRIC_DEBUG)) {
if (count < 0 || total < 0 || totalExclusive < 0 || sumOfSquares < 0) {
NewRelic.incrementCounter("Supportability/ResponseTimeStatsImpl/NegativeValue");
Agent.LOG.log(Level.INFO, "Invalid count {0}, total {1}, totalExclusive {2}, or sum of squares {3}",
count, total, totalExclusive, sumOfSquares);
synchronized (lock) {
sumOfSquares += responseTimeAsDouble;
if (count > 0) {
minValue = Math.min(responseTime, minValue);
} else {
minValue = responseTime;
}
count++;
total += responseTime;
maxValue = Math.max(responseTime, maxValue);
totalExclusive += exclusiveTime;
if (NewRelic.getAgent().getConfig().getValue(AgentConfigImpl.METRIC_DEBUG, AgentConfigImpl.DEFAULT_METRIC_DEBUG)) {
if (count < 0 || total < 0 || totalExclusive < 0 || sumOfSquares < 0) {
NewRelic.incrementCounter("Supportability/ResponseTimeStatsImpl/NegativeValue");
Agent.LOG.log(Level.INFO, "Invalid count {0}, total {1}, totalExclusive {2}, or sum of squares {3}",
count, total, totalExclusive, sumOfSquares);
}
}
}

}

@Override
public boolean hasData() {
return count > 0 || total > 0 || totalExclusive > 0;
boolean hasData;
synchronized (lock) {
hasData = count > 0 || total > 0 || totalExclusive > 0;
}
return hasData;
}

@Override
public void reset() {
count = 0;
total = totalExclusive = minValue = maxValue = 0;
sumOfSquares = 0;
synchronized (lock) {
count = 0;
total = totalExclusive = minValue = maxValue = 0;
sumOfSquares = 0;
}
}

@Override
public float getTotal() {
return (float) total / TimeConversion.NANOSECONDS_PER_SECOND;
synchronized (lock) {
return (float) total / TimeConversion.NANOSECONDS_PER_SECOND;
}
}

@Override
public float getTotalExclusiveTime() {
return (float) totalExclusive / TimeConversion.NANOSECONDS_PER_SECOND;
synchronized (lock) {
return (float) totalExclusive / TimeConversion.NANOSECONDS_PER_SECOND;
}
}


@Override
public float getMaxCallTime() {
return (float) maxValue / TimeConversion.NANOSECONDS_PER_SECOND;
synchronized (lock) {
return (float) maxValue / TimeConversion.NANOSECONDS_PER_SECOND;
}
}

@Override
public float getMinCallTime() {
return (float) minValue / TimeConversion.NANOSECONDS_PER_SECOND;
synchronized (lock) {
return (float) minValue / TimeConversion.NANOSECONDS_PER_SECOND;
}
}

@Override
public double getSumOfSquares() {
return sumOfSquares / NANOSECONDS_PER_SECOND_SQUARED;
synchronized (lock) {
return sumOfSquares / NANOSECONDS_PER_SECOND_SQUARED;
}
}

@Override
public final void merge(StatsBase statsObj) {
if (statsObj instanceof ResponseTimeStatsImpl) {
ResponseTimeStatsImpl stats = (ResponseTimeStatsImpl) statsObj;
if (stats.count > 0) {
if (count > 0) {
minValue = Math.min(minValue, stats.minValue);
} else {
minValue = stats.minValue;
synchronized (lock) {
if (stats.count > 0) {
if (count > 0) {
minValue = Math.min(minValue, stats.minValue);
} else {
minValue = stats.minValue;
}
}
}
count += stats.count;
total += stats.total;
totalExclusive += stats.totalExclusive;
count += stats.count;
total += stats.total;
totalExclusive += stats.totalExclusive;

maxValue = Math.max(maxValue, stats.maxValue);
sumOfSquares += stats.sumOfSquares;
maxValue = Math.max(maxValue, stats.maxValue);
sumOfSquares += stats.sumOfSquares;
}
}
}

@Override
public void recordResponseTime(int count, long totalTime, long minTime, long maxTime, TimeUnit unit) {
long totalTimeInNanos = TimeUnit.NANOSECONDS.convert(totalTime, unit);
this.count = count;
this.total = totalTimeInNanos;
this.totalExclusive = totalTimeInNanos;
this.minValue = TimeUnit.NANOSECONDS.convert(minTime, unit);
this.maxValue = TimeUnit.NANOSECONDS.convert(maxTime, unit);
double totalTimeInNanosAsDouble = totalTimeInNanos;
totalTimeInNanosAsDouble *= totalTimeInNanosAsDouble;
sumOfSquares += totalTimeInNanosAsDouble;
synchronized (lock) {
long totalTimeInNanos = TimeUnit.NANOSECONDS.convert(totalTime, unit);
this.count = count;
this.total = totalTimeInNanos;
this.totalExclusive = totalTimeInNanos;
this.minValue = TimeUnit.NANOSECONDS.convert(minTime, unit);
this.maxValue = TimeUnit.NANOSECONDS.convert(maxTime, unit);
double totalTimeInNanosAsDouble = totalTimeInNanos;
totalTimeInNanosAsDouble *= totalTimeInNanosAsDouble;
sumOfSquares += totalTimeInNanosAsDouble;
}
}

@Override
Expand Down
106 changes: 63 additions & 43 deletions newrelic-agent/src/main/java/com/newrelic/agent/stats/StatsImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
*/
public class StatsImpl extends AbstractStats implements Stats {

private final Object lock = new Object();

private float total;
private float minValue;
private float maxValue;
Expand All @@ -39,11 +41,13 @@ public StatsImpl(int count, float total, float minValue, float maxValue, double
@Override
public Object clone() throws CloneNotSupportedException {
StatsImpl newStats = new StatsImpl();
newStats.count = count;
newStats.total = total;
newStats.minValue = minValue;
newStats.maxValue = maxValue;
newStats.sumOfSquares = sumOfSquares;
synchronized (lock) {
newStats.count = count;
newStats.total = total;
newStats.minValue = minValue;
newStats.maxValue = maxValue;
newStats.sumOfSquares = sumOfSquares;
}
return newStats;
}

Expand All @@ -58,84 +62,100 @@ public void recordDataPoint(float value) {
if (Float.isNaN(value) || Float.isInfinite(value)) {
throw new IllegalArgumentException("Data points must be numbers");
}
double sos = sumOfSquares + (value * value);
if (sos < sumOfSquares) {
throw new IllegalArgumentException("Data value " + value + " caused sum of squares to roll over");
}
if (count > 0) {
minValue = Math.min(value, minValue);
} else {
minValue = value;
}
count++;
total += value;
maxValue = Math.max(value, maxValue);
sumOfSquares = sos;
synchronized (lock) {
double sos = sumOfSquares + (value * value);
if (sos < sumOfSquares) {
throw new IllegalArgumentException("Data value " + value + " caused sum of squares to roll over");
}
if (count > 0) {
minValue = Math.min(value, minValue);
} else {
minValue = value;
}
count++;
total += value;
maxValue = Math.max(value, maxValue);
sumOfSquares = sos;

if (NewRelic.getAgent().getConfig().getValue(AgentConfigImpl.METRIC_DEBUG, AgentConfigImpl.DEFAULT_METRIC_DEBUG)) {
if (count < 0 || total < 0) {
NewRelic.incrementCounter("Supportability/StatsImpl/NegativeValue");
Agent.LOG.log(Level.INFO, "Invalid count {0} or total {1}", count, total);
if (NewRelic.getAgent().getConfig().getValue(AgentConfigImpl.METRIC_DEBUG, AgentConfigImpl.DEFAULT_METRIC_DEBUG)) {
if (count < 0 || total < 0) {
NewRelic.incrementCounter("Supportability/StatsImpl/NegativeValue");
Agent.LOG.log(Level.INFO, "Invalid count {0} or total {1}", count, total);

}
}
}

}

@Override
public boolean hasData() {
return count > 0 || total > 0;
synchronized (lock) {
return count > 0 || total > 0;
}
}

@Override
public void reset() {
count = 0;
total = minValue = maxValue = 0;
sumOfSquares = 0;
synchronized (lock) {
count = 0;
total = minValue = maxValue = 0;
sumOfSquares = 0;
}
}

@Override
public float getTotal() {
return total;
synchronized (lock) {
return total;
}
}

@Override
public float getTotalExclusiveTime() {
return total;
synchronized (lock) {
return total;
}
}

@Override
public float getMinCallTime() {
return minValue;
synchronized (lock) {
return minValue;
}
}

@Override
public float getMaxCallTime() {
return maxValue;
synchronized (lock) {
return maxValue;
}
}

@Override
public double getSumOfSquares() {
return sumOfSquares;
synchronized (lock) {
return sumOfSquares;
}
}

@Override
public void merge(StatsBase statsObj) {
if (statsObj instanceof StatsImpl) {
StatsImpl stats = (StatsImpl) statsObj;
if (stats.count > 0) {
if (count > 0) {
minValue = Math.min(minValue, stats.minValue);
} else {
minValue = stats.minValue;
synchronized (lock) {
if (stats.count > 0) {
if (count > 0) {
minValue = Math.min(minValue, stats.minValue);
} else {
minValue = stats.minValue;
}
}
}
count += stats.count;
total += stats.total;
count += stats.count;
total += stats.total;

maxValue = Math.max(maxValue, stats.maxValue);
sumOfSquares += stats.sumOfSquares;
maxValue = Math.max(maxValue, stats.maxValue);
sumOfSquares += stats.sumOfSquares;
}
}
}

}

0 comments on commit ef15db0

Please sign in to comment.