| /* |
| * Copyright 2015-present Open Networking Foundation |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.onlab.util; |
| |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.List; |
| import java.util.concurrent.ScheduledExecutorService; |
| import java.util.concurrent.TimeUnit; |
| import java.util.concurrent.atomic.AtomicLong; |
| import java.util.stream.Collectors; |
| |
| import static com.google.common.base.Preconditions.checkArgument; |
| import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor; |
| import static org.onlab.util.Tools.groupedThreads; |
| |
| /** |
| * Maintains a sliding window of value counts. The sliding window counter is |
| * initialized with a number of window slots. Calls to #incrementCount() will |
| * increment the value in the current window slot. Periodically the window |
| * slides and the oldest value count is dropped. Calls to #get() will get the |
| * total count for the last N window slots. |
| */ |
| public final class SlidingWindowCounter { |
| |
| private volatile int headSlot; |
| private final int windowSlots; |
| |
| private final List<AtomicLong> counters; |
| |
| private final ScheduledExecutorService background; |
| |
| private static final int SLIDE_WINDOW_PERIOD_SECONDS = 1; |
| |
| /** |
| * Creates a new sliding window counter with the given total number of |
| * window slots. |
| * |
| * @param windowSlots total number of window slots |
| */ |
| public SlidingWindowCounter(int windowSlots) { |
| checkArgument(windowSlots > 0, "Window size must be a positive integer"); |
| |
| this.windowSlots = windowSlots; |
| this.headSlot = 0; |
| |
| // Initialize each item in the list to an AtomicLong of 0 |
| this.counters = Collections.nCopies(windowSlots, 0) |
| .stream() |
| .map(AtomicLong::new) |
| .collect(Collectors.toCollection(ArrayList::new)); |
| |
| background = newSingleThreadScheduledExecutor(groupedThreads("SlidingWindowCounter", "bg-%d")); |
| background.scheduleWithFixedDelay(this::advanceHead, 0, |
| SLIDE_WINDOW_PERIOD_SECONDS, TimeUnit.SECONDS); |
| } |
| |
| /** |
| * Releases resources used by the SlidingWindowCounter. |
| */ |
| public void destroy() { |
| background.shutdownNow(); |
| } |
| |
| /** |
| * Increments the count of the current window slot by 1. |
| */ |
| public void incrementCount() { |
| incrementCount(headSlot, 1); |
| } |
| |
| /** |
| * Increments the count of the current window slot by the given value. |
| * |
| * @param value value to increment by |
| */ |
| public void incrementCount(long value) { |
| incrementCount(headSlot, value); |
| } |
| |
| private void incrementCount(int slot, long value) { |
| counters.get(slot).addAndGet(value); |
| } |
| |
| /** |
| * Gets the total count for the last N window slots. |
| * |
| * @param slots number of slots to include in the count |
| * @return total count for last N slots |
| */ |
| public long get(int slots) { |
| checkArgument(slots <= windowSlots, |
| "Requested window must be less than the total window slots"); |
| |
| long sum = 0; |
| |
| for (int i = 0; i < slots; i++) { |
| int currentIndex = headSlot - i; |
| if (currentIndex < 0) { |
| currentIndex = counters.size() + currentIndex; |
| } |
| sum += counters.get(currentIndex).get(); |
| } |
| |
| return sum; |
| } |
| |
| void advanceHead() { |
| counters.get(slotAfter(headSlot)).set(0); |
| headSlot = slotAfter(headSlot); |
| } |
| |
| private int slotAfter(int slot) { |
| return (slot + 1) % windowSlots; |
| } |
| |
| } |