diff --git a/spectator-api/src/main/java/com/netflix/spectator/impl/AtomicDouble.java b/spectator-api/src/main/java/com/netflix/spectator/impl/AtomicDouble.java index 0be70db75..06fb4e4e6 100644 --- a/spectator-api/src/main/java/com/netflix/spectator/impl/AtomicDouble.java +++ b/spectator-api/src/main/java/com/netflix/spectator/impl/AtomicDouble.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2019 Netflix, Inc. + * Copyright 2014-2024 Netflix, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -28,8 +28,8 @@ public class AtomicDouble extends Number { private volatile long value; - private static final AtomicLongFieldUpdater VALUE_UPDATER = AtomicLongFieldUpdater.newUpdater( - AtomicDouble.class, "value"); + private static final AtomicLongFieldUpdater VALUE_UPDATER = + AtomicLongFieldUpdater.newUpdater(AtomicDouble.class, "value"); /** Create an instance with an initial value of 0. */ public AtomicDouble() { @@ -98,6 +98,20 @@ public void set(double amount) { value = Double.doubleToLongBits(amount); } + private static boolean isLessThan(double v1, double v2) { + return v1 < v2 || Double.isNaN(v2); + } + + /** Set the current value to the maximum of the current value or the provided value. */ + public void min(double v) { + if (Double.isFinite(v)) { + double min = get(); + while (isLessThan(v, min) && !compareAndSet(min, v)) { + min = get(); + } + } + } + private static boolean isGreaterThan(double v1, double v2) { return v1 > v2 || Double.isNaN(v2); } @@ -128,8 +142,7 @@ public void max(double v) { return get(); } - @Override - public String toString() { + @Override public String toString() { return Double.toString(get()); } } diff --git a/spectator-api/src/main/java/com/netflix/spectator/impl/StepDouble.java b/spectator-api/src/main/java/com/netflix/spectator/impl/StepDouble.java index cc59857e7..10c7afca1 100644 --- a/spectator-api/src/main/java/com/netflix/spectator/impl/StepDouble.java +++ b/spectator-api/src/main/java/com/netflix/spectator/impl/StepDouble.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2019 Netflix, Inc. + * Copyright 2014-2024 Netflix, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -35,12 +35,15 @@ public class StepDouble implements StepValue { private final long step; private volatile double previous; - private final AtomicDouble current; + private volatile long current; + + private static final AtomicLongFieldUpdater CURRENT_UPDATER = + AtomicLongFieldUpdater.newUpdater(StepDouble.class, "current"); private volatile long lastInitPos; - private static final AtomicLongFieldUpdater LAST_INIT_POS_UPDATER = AtomicLongFieldUpdater.newUpdater( - StepDouble.class, "lastInitPos"); + private static final AtomicLongFieldUpdater LAST_INIT_POS_UPDATER = + AtomicLongFieldUpdater.newUpdater(StepDouble.class, "lastInitPos"); /** Create a new instance. */ public StepDouble(double init, Clock clock, long step) { @@ -48,7 +51,7 @@ public StepDouble(double init, Clock clock, long step) { this.clock = clock; this.step = step; previous = init; - current = new AtomicDouble(init); + current = Double.doubleToLongBits(init); lastInitPos = clock.wallTime() / step; } @@ -56,7 +59,8 @@ private void rollCount(long now) { final long stepTime = now / step; final long lastInit = lastInitPos; if (lastInit < stepTime && LAST_INIT_POS_UPDATER.compareAndSet(this, lastInit, stepTime)) { - final double v = current.getAndSet(init); + final double v = Double.longBitsToDouble( + CURRENT_UPDATER.getAndSet(this, Double.doubleToLongBits(init))); // Need to check if there was any activity during the previous step interval. If there was // then the init position will move forward by 1, otherwise it will be older. No activity // means the previous interval should be set to the `init` value. @@ -64,15 +68,102 @@ private void rollCount(long now) { } } - /** Get the AtomicDouble for the current bucket. */ - public AtomicDouble getCurrent() { + /** Get the value for the current bucket. */ + public double getCurrent() { return getCurrent(clock.wallTime()); } - /** Get the AtomicDouble for the current bucket. */ - public AtomicDouble getCurrent(long now) { + /** Get the value for the current bucket. */ + public double getCurrent(long now) { + rollCount(now); + return Double.longBitsToDouble(current); + } + + /** Set the value for the current bucket. */ + public void setCurrent(long now, double value) { + rollCount(now); + current = Double.doubleToLongBits(value); + } + + /** Increment the current value and return the result. */ + public double addAndGet(long now, double amount) { + rollCount(now); + long v; + double d; + double n; + long next; + do { + v = current; + d = Double.longBitsToDouble(v); + n = d + amount; + next = Double.doubleToLongBits(n); + } while (!CURRENT_UPDATER.compareAndSet(this, v, next)); + return n; + } + + /** Increment the current value and return the value before incrementing. */ + public double getAndAdd(long now, double amount) { + rollCount(now); + long v; + double d; + double n; + long next; + do { + v = current; + d = Double.longBitsToDouble(v); + n = d + amount; + next = Double.doubleToLongBits(n); + } while (!CURRENT_UPDATER.compareAndSet(this, v, next)); + return d; + } + + /** Set the current value and return the previous value. */ + public double getAndSet(long now, double value) { + rollCount(now); + long v = CURRENT_UPDATER.getAndSet(this, Double.doubleToLongBits(value)); + return Double.longBitsToDouble(v); + } + + private boolean compareAndSet(double expect, double update) { + long e = Double.doubleToLongBits(expect); + long u = Double.doubleToLongBits(update); + return CURRENT_UPDATER.compareAndSet(this, e, u); + } + + /** Set the current value and return the previous value. */ + public boolean compareAndSet(long now, double expect, double update) { rollCount(now); - return current; + return compareAndSet(expect, update); + } + + private static boolean isLessThan(double v1, double v2) { + return v1 < v2 || Double.isNaN(v2); + } + + /** Set the current value to the minimum of the current value or the provided value. */ + public void min(long now, double value) { + if (Double.isFinite(value)) { + rollCount(now); + double min = Double.longBitsToDouble(current); + while (isLessThan(value, min) && !compareAndSet(min, value)) { + min = Double.longBitsToDouble(current); + } + } + } + + private static boolean isGreaterThan(double v1, double v2) { + return v1 > v2 || Double.isNaN(v2); + } + + /** Set the current value to the maximum of the current value or the provided value. */ + public void max(long now, double value) { + if (Double.isFinite(value)) { + rollCount(now); + double max = Double.longBitsToDouble(current); + while (isGreaterThan(value, max) && !compareAndSet(max, value)) { + max = Double.longBitsToDouble(current); + } + } } /** Get the value for the last completed interval. */ @@ -106,7 +197,7 @@ public double poll(long now) { @Override public String toString() { return "StepDouble{init=" + init + ", previous=" + previous - + ", current=" + current.get() + + ", current=" + Double.longBitsToDouble(current) + ", lastInitPos=" + lastInitPos + '}'; } } diff --git a/spectator-api/src/main/java/com/netflix/spectator/impl/StepLong.java b/spectator-api/src/main/java/com/netflix/spectator/impl/StepLong.java index c957121b7..5d595a4e6 100644 --- a/spectator-api/src/main/java/com/netflix/spectator/impl/StepLong.java +++ b/spectator-api/src/main/java/com/netflix/spectator/impl/StepLong.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2019 Netflix, Inc. + * Copyright 2014-2024 Netflix, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,7 +17,6 @@ import com.netflix.spectator.api.Clock; -import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLongFieldUpdater; /** @@ -36,12 +35,15 @@ public class StepLong implements StepValue { private final long step; private volatile long previous; - private final AtomicLong current; + private volatile long current; + + private static final AtomicLongFieldUpdater CURRENT_UPDATER = + AtomicLongFieldUpdater.newUpdater(StepLong.class, "current"); private volatile long lastInitPos; - private static final AtomicLongFieldUpdater LAST_INIT_POS_UPDATER = AtomicLongFieldUpdater.newUpdater( - StepLong.class, "lastInitPos"); + private static final AtomicLongFieldUpdater LAST_INIT_POS_UPDATER = + AtomicLongFieldUpdater.newUpdater(StepLong.class, "lastInitPos"); /** Create a new instance. */ public StepLong(long init, Clock clock, long step) { @@ -49,7 +51,7 @@ public StepLong(long init, Clock clock, long step) { this.clock = clock; this.step = step; previous = init; - current = new AtomicLong(init); + current = init; lastInitPos = clock.wallTime() / step; } @@ -57,7 +59,7 @@ private void rollCount(long now) { final long stepTime = now / step; final long lastInit = lastInitPos; if (lastInit < stepTime && LAST_INIT_POS_UPDATER.compareAndSet(this, lastInit, stepTime)) { - final long v = current.getAndSet(init); + final long v = CURRENT_UPDATER.getAndSet(this, init); // Need to check if there was any activity during the previous step interval. If there was // then the init position will move forward by 1, otherwise it will be older. No activity // means the previous interval should be set to the `init` value. @@ -65,17 +67,77 @@ private void rollCount(long now) { } } - /** Get the AtomicLong for the current bucket. */ - public AtomicLong getCurrent() { + /** Get the value for the current bucket. */ + public long getCurrent() { return getCurrent(clock.wallTime()); } - /** Get the AtomicLong for the current bucket. */ - public AtomicLong getCurrent(long now) { + /** Get the value for the current bucket. */ + public long getCurrent(long now) { rollCount(now); return current; } + /** Set the value for the current bucket. */ + public void setCurrent(long now, long value) { + rollCount(now); + current = value; + } + + /** Increment the current value and return the result. */ + public long incrementAndGet(long now) { + rollCount(now); + return CURRENT_UPDATER.incrementAndGet(this); + } + + /** Increment the current value and return the value before incrementing. */ + public long getAndIncrement(long now) { + rollCount(now); + return CURRENT_UPDATER.getAndIncrement(this); + } + + /** Increment the current value and return the result. */ + public long addAndGet(long now, long value) { + rollCount(now); + return CURRENT_UPDATER.addAndGet(this, value); + } + + /** Increment the current value and return the value before incrementing. */ + public long getAndAdd(long now, long value) { + rollCount(now); + return CURRENT_UPDATER.getAndAdd(this, value); + } + + /** Set the current value and return the previous value. */ + public long getAndSet(long now, long value) { + rollCount(now); + return CURRENT_UPDATER.getAndSet(this, value); + } + + /** Set the current value and return the previous value. */ + public boolean compareAndSet(long now, long expect, long update) { + rollCount(now); + return CURRENT_UPDATER.compareAndSet(this, expect, update); + } + + /** Set the current value to the minimum of the current value or the provided value. */ + public void min(long now, long value) { + rollCount(now); + long min = current; + while (value < min && !CURRENT_UPDATER.compareAndSet(this, min, value)) { + min = current; + } + } + + /** Set the current value to the maximum of the current value or the provided value. */ + public void max(long now, long value) { + rollCount(now); + long max = current; + while (value > max && !CURRENT_UPDATER.compareAndSet(this, max, value)) { + max = current; + } + } + /** Get the value for the last completed interval. */ public long poll() { return poll(clock.wallTime()); @@ -107,7 +169,7 @@ public long poll(long now) { @Override public String toString() { return "StepLong{init=" + init + ", previous=" + previous - + ", current=" + current.get() + + ", current=" + current + ", lastInitPos=" + lastInitPos + '}'; } } diff --git a/spectator-api/src/test/java/com/netflix/spectator/impl/AtomicDoubleTest.java b/spectator-api/src/test/java/com/netflix/spectator/impl/AtomicDoubleTest.java index 09b36b0f3..aba11fb46 100644 --- a/spectator-api/src/test/java/com/netflix/spectator/impl/AtomicDoubleTest.java +++ b/spectator-api/src/test/java/com/netflix/spectator/impl/AtomicDoubleTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2019 Netflix, Inc. + * Copyright 2014-2024 Netflix, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -74,6 +74,55 @@ public void getAndAdd() { Assertions.assertEquals(55.0, v.get(), 1e-12); } + @Test + public void minGt() { + AtomicDouble v = new AtomicDouble(0.0); + v.min(2.0); + Assertions.assertEquals(0.0, v.get(), 1e-12); + } + + @Test + public void minLt() { + AtomicDouble v = new AtomicDouble(2.0); + v.min(0.0); + Assertions.assertEquals(0.0, v.get(), 1e-12); + } + + @Test + public void minNegative() { + AtomicDouble v = new AtomicDouble(-42.0); + v.min(-41.0); + Assertions.assertEquals(-42.0, v.get(), 1e-12); + } + + @Test + public void minNaN() { + AtomicDouble v = new AtomicDouble(Double.NaN); + v.min(0.0); + Assertions.assertEquals(0.0, v.get(), 1e-12); + } + + @Test + public void minValueNaN() { + AtomicDouble v = new AtomicDouble(0.0); + v.min(Double.NaN); + Assertions.assertEquals(0.0, v.get(), 1e-12); + } + + @Test + public void minNegativeNaN() { + AtomicDouble v = new AtomicDouble(Double.NaN); + v.min(-42.0); + Assertions.assertEquals(-42.0, v.get(), 1e-12); + } + + @Test + public void minValueInfinity() { + AtomicDouble v = new AtomicDouble(0.0); + v.min(Double.NEGATIVE_INFINITY); + Assertions.assertEquals(0.0, v.get(), 1e-12); + } + @Test public void maxGt() { AtomicDouble v = new AtomicDouble(0.0); diff --git a/spectator-api/src/test/java/com/netflix/spectator/impl/StepDoubleTest.java b/spectator-api/src/test/java/com/netflix/spectator/impl/StepDoubleTest.java index b179fd867..606b6aa10 100644 --- a/spectator-api/src/test/java/com/netflix/spectator/impl/StepDoubleTest.java +++ b/spectator-api/src/test/java/com/netflix/spectator/impl/StepDoubleTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2019 Netflix, Inc. + * Copyright 2014-2024 Netflix, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -32,33 +32,204 @@ public void init() { @Test public void empty() { StepDouble v = new StepDouble(0.0, clock, 10L); - Assertions.assertEquals(0.0, v.getCurrent().get(), 1e-12); + Assertions.assertEquals(0.0, v.getCurrent(), 1e-12); Assertions.assertEquals(0.0, v.poll(), 1e-12); } @Test public void increment() { StepDouble v = new StepDouble(0.0, clock, 10L); - v.getCurrent().addAndGet(1.0); - Assertions.assertEquals(1.0, v.getCurrent().get(), 1e-12); + v.addAndGet(clock.wallTime(), 1.0); + Assertions.assertEquals(1.0, v.getCurrent(), 1e-12); Assertions.assertEquals(0.0, v.poll(), 1e-12); } @Test public void incrementAndCrossStepBoundary() { StepDouble v = new StepDouble(0.0, clock, 10L); - v.getCurrent().addAndGet(1.0); + v.addAndGet(clock.wallTime(), 1.0); clock.setWallTime(10L); - Assertions.assertEquals(0.0, v.getCurrent().get(), 1e-12); + Assertions.assertEquals(0.0, v.getCurrent(), 1e-12); Assertions.assertEquals(1.0, v.poll(), 1e-12); } @Test public void missedRead() { StepDouble v = new StepDouble(0.0, clock, 10L); - v.getCurrent().addAndGet(1.0); + v.addAndGet(clock.wallTime(), 1.0); clock.setWallTime(20L); - Assertions.assertEquals(0.0, v.getCurrent().get(), 1e-12); + Assertions.assertEquals(0.0, v.getCurrent(), 1e-12); Assertions.assertEquals(0.0, v.poll(), 1e-12); } + + @Test + public void initDefault() { + StepDouble v = new StepDouble(0.0, clock, 10L); + Assertions.assertEquals(0.0, v.getCurrent(), 1e-12); + } + + @Test + public void initWithValue() { + StepDouble v = new StepDouble(42.0, clock, 10L); + Assertions.assertEquals(42.0, v.getCurrent(), 1e-12); + } + + @Test + public void set() { + StepDouble v = new StepDouble(13.0, clock, 10L); + v.setCurrent(clock.wallTime(), 42.0); + Assertions.assertEquals(42.0, v.getCurrent(clock.wallTime()), 1e-12); + } + + @Test + public void getAndSet() { + final long now = clock.wallTime(); + StepDouble v = new StepDouble(13.0, clock, 10L); + Assertions.assertEquals(13.0, v.getAndSet(now, 42.0), 1e-12); + Assertions.assertEquals(42.0, v.getCurrent(now), 1e-12); + } + + @Test + public void compareAndSet() { + final long now = clock.wallTime(); + StepDouble v = new StepDouble(13.0, clock, 10L); + Assertions.assertTrue(v.compareAndSet(now, 13.0, 42.0)); + Assertions.assertEquals(42.0, v.getCurrent(now), 1e-12); + } + + @Test + public void compareAndSetFail() { + final long now = clock.wallTime(); + StepDouble v = new StepDouble(13.0, clock, 10L); + Assertions.assertFalse(v.compareAndSet(now, 12.0, 42.0)); + Assertions.assertEquals(13.0, v.getCurrent(now), 1e-12); + } + + @Test + public void addAndGet() { + final long now = clock.wallTime(); + StepDouble v = new StepDouble(13.0, clock, 10L); + Assertions.assertEquals(55.0, v.addAndGet(now, 42.0), 1e-12); + Assertions.assertEquals(55.0, v.getCurrent(now), 1e-12); + } + + @Test + public void getAndAdd() { + final long now = clock.wallTime(); + StepDouble v = new StepDouble(13.0, clock, 10L); + Assertions.assertEquals(13.0, v.getAndAdd(now, 42.0), 1e-12); + Assertions.assertEquals(55.0, v.getCurrent(now), 1e-12); + } + + @Test + public void minGt() { + final long now = clock.wallTime(); + StepDouble v = new StepDouble(0.0, clock, 10L); + v.min(now, 2.0); + Assertions.assertEquals(0.0, v.getCurrent(now), 1e-12); + } + + @Test + public void minLt() { + final long now = clock.wallTime(); + StepDouble v = new StepDouble(2.0, clock, 10L); + v.min(now, 0.0); + Assertions.assertEquals(0.0, v.getCurrent(now), 1e-12); + } + + @Test + public void minNegative() { + final long now = clock.wallTime(); + StepDouble v = new StepDouble(-42.0, clock, 10L); + v.min(now, -41.0); + Assertions.assertEquals(-42.0, v.getCurrent(now), 1e-12); + } + + @Test + public void minNaN() { + final long now = clock.wallTime(); + StepDouble v = new StepDouble(Double.NaN, clock, 10L); + v.min(now, 0.0); + Assertions.assertEquals(0.0, v.getCurrent(now), 1e-12); + } + + @Test + public void minValueNaN() { + final long now = clock.wallTime(); + StepDouble v = new StepDouble(0.0, clock, 10L); + v.min(now, Double.NaN); + Assertions.assertEquals(0.0, v.getCurrent(now), 1e-12); + } + + @Test + public void minNegativeNaN() { + final long now = clock.wallTime(); + StepDouble v = new StepDouble(Double.NaN, clock, 10L); + v.min(now, -42.0); + Assertions.assertEquals(-42.0, v.getCurrent(now), 1e-12); + } + + @Test + public void minValueInfinity() { + final long now = clock.wallTime(); + StepDouble v = new StepDouble(0.0, clock, 10L); + v.min(now, Double.NEGATIVE_INFINITY); + Assertions.assertEquals(0.0, v.getCurrent(now), 1e-12); + } + + @Test + public void maxGt() { + final long now = clock.wallTime(); + StepDouble v = new StepDouble(0.0, clock, 10L); + v.max(now, 2.0); + Assertions.assertEquals(2.0, v.getCurrent(now), 1e-12); + } + + @Test + public void maxLt() { + final long now = clock.wallTime(); + StepDouble v = new StepDouble(2.0, clock, 10L); + v.max(now, 0.0); + Assertions.assertEquals(2.0, v.getCurrent(now), 1e-12); + } + + @Test + public void maxNegative() { + final long now = clock.wallTime(); + StepDouble v = new StepDouble(-42.0, clock, 10L); + v.max(now, -41.0); + Assertions.assertEquals(-41.0, v.getCurrent(now), 1e-12); + } + + @Test + public void maxNaN() { + final long now = clock.wallTime(); + StepDouble v = new StepDouble(Double.NaN, clock, 10L); + v.max(now, 0.0); + Assertions.assertEquals(0.0, v.getCurrent(now), 1e-12); + } + + @Test + public void maxValueNaN() { + final long now = clock.wallTime(); + StepDouble v = new StepDouble(0.0, clock, 10L); + v.max(now, Double.NaN); + Assertions.assertEquals(0.0, v.getCurrent(now), 1e-12); + } + + @Test + public void maxNegativeNaN() { + final long now = clock.wallTime(); + StepDouble v = new StepDouble(Double.NaN, clock, 10L); + v.max(now, -42.0); + Assertions.assertEquals(-42.0, v.getCurrent(now), 1e-12); + } + + @Test + public void maxValueInfinity() { + final long now = clock.wallTime(); + StepDouble v = new StepDouble(0.0, clock, 10L); + v.max(now, Double.POSITIVE_INFINITY); + Assertions.assertEquals(0.0, v.getCurrent(now), 1e-12); + } } diff --git a/spectator-api/src/test/java/com/netflix/spectator/impl/StepLongTest.java b/spectator-api/src/test/java/com/netflix/spectator/impl/StepLongTest.java index 70091dc2b..194da22e5 100644 --- a/spectator-api/src/test/java/com/netflix/spectator/impl/StepLongTest.java +++ b/spectator-api/src/test/java/com/netflix/spectator/impl/StepLongTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2019 Netflix, Inc. + * Copyright 2014-2024 Netflix, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -32,33 +32,156 @@ public void init() { @Test public void empty() { StepLong v = new StepLong(0L, clock, 10L); - Assertions.assertEquals(0L, v.getCurrent().get()); + Assertions.assertEquals(0L, v.getCurrent()); Assertions.assertEquals(0L, v.poll()); } @Test public void increment() { StepLong v = new StepLong(0L, clock, 10L); - v.getCurrent().incrementAndGet(); - Assertions.assertEquals(1L, v.getCurrent().get()); + v.incrementAndGet(clock.wallTime()); + Assertions.assertEquals(1L, v.getCurrent()); Assertions.assertEquals(0L, v.poll()); } @Test public void incrementAndCrossStepBoundary() { StepLong v = new StepLong(0L, clock, 10L); - v.getCurrent().incrementAndGet(); + v.incrementAndGet(clock.wallTime()); clock.setWallTime(10L); - Assertions.assertEquals(0L, v.getCurrent().get()); + Assertions.assertEquals(0L, v.getCurrent()); Assertions.assertEquals(1L, v.poll()); } @Test public void missedRead() { StepLong v = new StepLong(0L, clock, 10L); - v.getCurrent().incrementAndGet(); + v.incrementAndGet(clock.wallTime()); clock.setWallTime(20L); - Assertions.assertEquals(0L, v.getCurrent().get()); + Assertions.assertEquals(0L, v.getCurrent()); Assertions.assertEquals(0L, v.poll()); } + + @Test + public void initDefault() { + StepLong v = new StepLong(0L, clock, 10L); + Assertions.assertEquals(0L, v.getCurrent()); + } + + @Test + public void initWithValue() { + StepLong v = new StepLong(42L, clock, 10L); + Assertions.assertEquals(42L, v.getCurrent()); + } + + @Test + public void set() { + StepLong v = new StepLong(13L, clock, 10L); + v.setCurrent(clock.wallTime(), 42L); + Assertions.assertEquals(42L, v.getCurrent(clock.wallTime())); + } + + @Test + public void getAndSet() { + final long now = clock.wallTime(); + StepLong v = new StepLong(13L, clock, 10L); + Assertions.assertEquals(13L, v.getAndSet(now, 42L)); + Assertions.assertEquals(42L, v.getCurrent(now)); + } + + @Test + public void compareAndSet() { + final long now = clock.wallTime(); + StepLong v = new StepLong(13L, clock, 10L); + Assertions.assertTrue(v.compareAndSet(now, 13L, 42L)); + Assertions.assertEquals(42L, v.getCurrent(now)); + } + + @Test + public void compareAndSetFail() { + final long now = clock.wallTime(); + StepLong v = new StepLong(13L, clock, 10L); + Assertions.assertFalse(v.compareAndSet(now, 12L, 42L)); + Assertions.assertEquals(13L, v.getCurrent(now)); + } + + @Test + public void incrementAndGet() { + final long now = clock.wallTime(); + StepLong v = new StepLong(13L, clock, 10L); + Assertions.assertEquals(14L, v.incrementAndGet(now)); + Assertions.assertEquals(14L, v.getCurrent(now)); + } + + @Test + public void getAndIncrement() { + final long now = clock.wallTime(); + StepLong v = new StepLong(13L, clock, 10L); + Assertions.assertEquals(13L, v.getAndIncrement(now)); + Assertions.assertEquals(14L, v.getCurrent(now)); + } + + @Test + public void addAndGet() { + final long now = clock.wallTime(); + StepLong v = new StepLong(13L, clock, 10L); + Assertions.assertEquals(55L, v.addAndGet(now, 42L)); + Assertions.assertEquals(55L, v.getCurrent(now)); + } + + @Test + public void getAndAdd() { + final long now = clock.wallTime(); + StepLong v = new StepLong(13L, clock, 10L); + Assertions.assertEquals(13L, v.getAndAdd(now, 42L)); + Assertions.assertEquals(55L, v.getCurrent(now)); + } + + @Test + public void minGt() { + final long now = clock.wallTime(); + StepLong v = new StepLong(0L, clock, 10L); + v.min(now, 2L); + Assertions.assertEquals(0L, v.getCurrent(now)); + } + + @Test + public void minLt() { + final long now = clock.wallTime(); + StepLong v = new StepLong(2L, clock, 10L); + v.min(now, 0L); + Assertions.assertEquals(0L, v.getCurrent(now)); + } + + @Test + public void minNegative() { + final long now = clock.wallTime(); + StepLong v = new StepLong(-42L, clock, 10L); + v.min(now, -41L); + Assertions.assertEquals(-42L, v.getCurrent(now)); + } + + @Test + public void maxGt() { + final long now = clock.wallTime(); + StepLong v = new StepLong(0L, clock, 10L); + v.max(now, 2L); + Assertions.assertEquals(2L, v.getCurrent(now)); + } + + @Test + public void maxLt() { + final long now = clock.wallTime(); + StepLong v = new StepLong(2L, clock, 10L); + v.max(now, 0L); + Assertions.assertEquals(2L, v.getCurrent(now)); + } + + @Test + public void maxNegative() { + final long now = clock.wallTime(); + StepLong v = new StepLong(-42L, clock, 10L); + v.max(now, -41L); + Assertions.assertEquals(-41L, v.getCurrent(now)); + } } diff --git a/spectator-reg-atlas/src/main/java/com/netflix/spectator/atlas/AtlasCounter.java b/spectator-reg-atlas/src/main/java/com/netflix/spectator/atlas/AtlasCounter.java index b09ce4c4e..8f645b873 100644 --- a/spectator-reg-atlas/src/main/java/com/netflix/spectator/atlas/AtlasCounter.java +++ b/spectator-reg-atlas/src/main/java/com/netflix/spectator/atlas/AtlasCounter.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2020 Netflix, Inc. + * Copyright 2014-2024 Netflix, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -47,8 +47,8 @@ class AtlasCounter extends AtlasMeter implements Counter { @Override public void add(double amount) { if (Double.isFinite(amount) && amount > 0.0) { - long now = clock.wallTime(); - value.getCurrent(now).addAndGet(amount); + final long now = clock.wallTime(); + value.addAndGet(now, amount); updateLastModTime(now); } } diff --git a/spectator-reg-atlas/src/main/java/com/netflix/spectator/atlas/AtlasDistributionSummary.java b/spectator-reg-atlas/src/main/java/com/netflix/spectator/atlas/AtlasDistributionSummary.java index b67ae54ad..5b916023b 100644 --- a/spectator-reg-atlas/src/main/java/com/netflix/spectator/atlas/AtlasDistributionSummary.java +++ b/spectator-reg-atlas/src/main/java/com/netflix/spectator/atlas/AtlasDistributionSummary.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2022 Netflix, Inc. + * Copyright 2014-2024 Netflix, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,8 +23,6 @@ import com.netflix.spectator.impl.StepLong; import com.netflix.spectator.impl.StepValue; -import java.util.concurrent.atomic.AtomicLong; - /** * Distribution summary that reports four measurements to Atlas: * @@ -94,11 +92,11 @@ private void reportMaxMeasurement(long now, MeasurementConsumer consumer, Id mid @Override public void record(long amount) { long now = clock.wallTime(); - count.getCurrent(now).incrementAndGet(); + count.incrementAndGet(now); if (amount > 0) { - total.getCurrent(now).addAndGet(amount); - totalOfSquares.getCurrent(now).addAndGet((double) amount * amount); - updateMax(max.getCurrent(now), amount); + total.addAndGet(now, amount); + totalOfSquares.addAndGet(now, (double) amount * amount); + max.max(now, amount); } updateLastModTime(now); } @@ -121,20 +119,13 @@ private void reportMaxMeasurement(long now, MeasurementConsumer consumer, Id mid // issue updates as a batch final long now = clock.wallTime(); - count.getCurrent(now).addAndGet(limit); - total.getCurrent(now).addAndGet(accumulatedTotal); - totalOfSquares.getCurrent(now).addAndGet(accumulatedTotalOfSquares); - updateMax(max.getCurrent(now), accumulatedMax); + count.addAndGet(now, limit); + total.addAndGet(now, accumulatedTotal); + totalOfSquares.addAndGet(now, accumulatedTotalOfSquares); + max.max(now, accumulatedMax); updateLastModTime(now); } - private void updateMax(AtomicLong maxValue, long v) { - long p = maxValue.get(); - while (v > p && !maxValue.compareAndSet(p, v)) { - p = maxValue.get(); - } - } - @Override public long count() { return count.poll(); } @@ -154,9 +145,9 @@ private void updateMax(AtomicLong maxValue, long v) { */ void update(long count, long total, double totalOfSquares, long max) { long now = clock.wallTime(); - this.count.getCurrent(now).addAndGet(count); - this.total.getCurrent(now).addAndGet(total); - this.totalOfSquares.getCurrent(now).addAndGet(totalOfSquares); - updateMax(this.max.getCurrent(now), max); + this.count.addAndGet(now, count); + this.total.addAndGet(now, total); + this.totalOfSquares.addAndGet(now, totalOfSquares); + this.max.max(now, max); } } diff --git a/spectator-reg-atlas/src/main/java/com/netflix/spectator/atlas/AtlasMaxGauge.java b/spectator-reg-atlas/src/main/java/com/netflix/spectator/atlas/AtlasMaxGauge.java index 41ed24f4e..8f0dda28b 100644 --- a/spectator-reg-atlas/src/main/java/com/netflix/spectator/atlas/AtlasMaxGauge.java +++ b/spectator-reg-atlas/src/main/java/com/netflix/spectator/atlas/AtlasMaxGauge.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2020 Netflix, Inc. + * Copyright 2014-2024 Netflix, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -53,7 +53,7 @@ class AtlasMaxGauge extends AtlasMeter implements Gauge { @Override public void set(double v) { long now = clock.wallTime(); - value.getCurrent(now).max(v); + value.max(now, v); updateLastModTime(now); } diff --git a/spectator-reg-atlas/src/main/java/com/netflix/spectator/atlas/AtlasTimer.java b/spectator-reg-atlas/src/main/java/com/netflix/spectator/atlas/AtlasTimer.java index 9c75bd84c..9baf49c5f 100644 --- a/spectator-reg-atlas/src/main/java/com/netflix/spectator/atlas/AtlasTimer.java +++ b/spectator-reg-atlas/src/main/java/com/netflix/spectator/atlas/AtlasTimer.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2023 Netflix, Inc. + * Copyright 2014-2024 Netflix, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,7 +25,6 @@ import java.time.Duration; import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicLong; /** * Timer that reports four measurements to Atlas: @@ -100,12 +99,12 @@ private void reportMaxMeasurement(long now, MeasurementConsumer consumer, Id mid @Override public void record(long amount, TimeUnit unit) { long now = clock.wallTime(); - count.getCurrent(now).incrementAndGet(); + count.incrementAndGet(now); if (amount > 0) { final long nanos = unit.toNanos(amount); - total.getCurrent(now).addAndGet(nanos); - totalOfSquares.getCurrent(now).addAndGet((double) nanos * nanos); - updateMax(max.getCurrent(now), nanos); + total.addAndGet(now, nanos); + totalOfSquares.addAndGet(now, (double) nanos * nanos); + max.max(now, nanos); } updateLastModTime(now); } @@ -129,10 +128,10 @@ private void reportMaxMeasurement(long now, MeasurementConsumer consumer, Id mid // issue updates as a batch final long now = clock.wallTime(); - count.getCurrent(now).addAndGet(limit); - total.getCurrent(now).addAndGet(accumulatedTotal); - totalOfSquares.getCurrent(now).addAndGet(accumulatedTotalOfSquares); - updateMax(max.getCurrent(now), accumulatedMax); + count.addAndGet(now, limit); + total.addAndGet(now, accumulatedTotal); + totalOfSquares.addAndGet(now, accumulatedTotalOfSquares); + max.max(now, accumulatedMax); updateLastModTime(now); } @@ -155,20 +154,13 @@ private void reportMaxMeasurement(long now, MeasurementConsumer consumer, Id mid // issue updates as a batch final long now = clock.wallTime(); - count.getCurrent(now).addAndGet(limit); - total.getCurrent(now).addAndGet(accumulatedTotal); - totalOfSquares.getCurrent(now).addAndGet(accumulatedTotalOfSquares); - updateMax(max.getCurrent(now), accumulatedMax); + count.addAndGet(now, limit); + total.addAndGet(now, accumulatedTotal); + totalOfSquares.addAndGet(now, accumulatedTotalOfSquares); + max.max(now, accumulatedMax); updateLastModTime(now); } - private void updateMax(AtomicLong maxValue, long v) { - long p = maxValue.get(); - while (v > p && !maxValue.compareAndSet(p, v)) { - p = maxValue.get(); - } - } - @Override public long count() { return count.poll(); } @@ -191,9 +183,9 @@ private void updateMax(AtomicLong maxValue, long v) { */ void update(long count, double total, double totalOfSquares, long max) { long now = clock.wallTime(); - this.count.getCurrent(now).addAndGet(count); - this.total.getCurrent(now).addAndGet(total); - this.totalOfSquares.getCurrent(now).addAndGet(totalOfSquares); - updateMax(this.max.getCurrent(now), max); + this.count.addAndGet(now, count); + this.total.addAndGet(now, total); + this.totalOfSquares.addAndGet(now, totalOfSquares); + this.max.max(now, max); } } diff --git a/spectator-reg-micrometer/src/main/java/com/netflix/spectator/micrometer/MicrometerRegistry.java b/spectator-reg-micrometer/src/main/java/com/netflix/spectator/micrometer/MicrometerRegistry.java index 289b13f9b..80e09f266 100644 --- a/spectator-reg-micrometer/src/main/java/com/netflix/spectator/micrometer/MicrometerRegistry.java +++ b/spectator-reg-micrometer/src/main/java/com/netflix/spectator/micrometer/MicrometerRegistry.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2019 Netflix, Inc. + * Copyright 2014-2024 Netflix, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -133,12 +133,13 @@ private Meter convert(io.micrometer.core.instrument.Meter meter) { @Override public Gauge maxGauge(Id id) { // Note: micrometer doesn't support this type directly so it uses an arbitrary // window of 1m - StepDouble value = new StepDouble(Double.NaN, clock(), 60000L); + final Clock clk = clock(); + StepDouble value = new StepDouble(Double.NaN, clk, 60000L); io.micrometer.core.instrument.Gauge gauge = io.micrometer.core.instrument.Gauge .builder(id.name(), value, StepDouble::poll) .tags(convert(id.tags())) .register(impl); - return new MicrometerGauge(id, v -> value.getCurrent().max(v), gauge); + return new MicrometerGauge(id, v -> value.max(clk.wallTime(), v), gauge); } @Override public Meter get(Id id) {