All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.lightbody.bmp.proxy.ActivityMonitor Maven / Gradle / Ivy

package net.lightbody.bmp.proxy;

import com.google.common.util.concurrent.Monitor;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;

/**
 * Tracks active and total requests on a proxy, and provides a mechanism to wait for active requests to finish.
 * See {@link net.lightbody.bmp.proxy.ActivityMonitor#waitForQuiescence(long, long, java.util.concurrent.TimeUnit)}.
 */
public class ActivityMonitor {
	private final AtomicInteger activeRequests = new AtomicInteger(0);
	private final AtomicInteger totalRequests = new AtomicInteger(0);

	private final AtomicLong lastRequestFinishedNanos = new AtomicLong(System.nanoTime());

	private final Monitor monitor = new Monitor();

	private final Monitor.Guard requestNotActive = new Monitor.Guard(monitor) {
		@Override
		public boolean isSatisfied() {
			return activeRequests.get() == 0;
		}
	};

	private final Monitor.Guard requestActive = new Monitor.Guard(monitor) {
		@Override
		public boolean isSatisfied() {
			return activeRequests.get() > 0;
		}
	};

	public void requestStarted() {
		int previousCount = activeRequests.getAndIncrement();
		totalRequests.incrementAndGet();
		if (previousCount == 0) {
			// previously there were no active requests, but now there are -- signal to any waitForQuiescence threads that they need to
			// begin waiting again
			monitor.enter();
			monitor.leave();
		}
	}

	public void requestFinished() {
		int newCount = activeRequests.decrementAndGet();
		lastRequestFinishedNanos.set(System.nanoTime());

		if (newCount == 0) {
			// there are no active requests, so signal to any waitForQuiescence threads that they can begin waiting for their quietPeriod
			monitor.enter();
			monitor.leave();
		}
	}

	public int getActiveRequests() {
		return activeRequests.get();
	}

	public int getTotalRequests() {
		return totalRequests.get();
	}

	public boolean waitForQuiescence(long quietPeriod, long timeout, TimeUnit timeUnit) {
		// the minRequestFinishTime is the earliest possible time the current or last request "could" finish. if there is no active
		// request, this is simply the lastRequestFinishedNanos time. if there is an active request, it is "now". this helps avoid waiting
		// for quiescence if there is an active request and the timeout is less than the quietPeriod.
		long minRequestFinishTime;
		if (activeRequests.get() == 0) {
			if (timeUnit.convert(System.nanoTime() - lastRequestFinishedNanos.get(), TimeUnit.NANOSECONDS) >= quietPeriod) {
				return true;
			} else {
				minRequestFinishTime = lastRequestFinishedNanos.get();
			}
		} else {
			minRequestFinishTime = System.nanoTime();
		}

		// record the maximum time we can wait until (the current time + the timeout), which will allow us to avoid waiting for
		// quiescence if it is not possible to satisfy the quietPeriod before the waitUntil time elapses
		long waitUntil = System.nanoTime() + TimeUnit.NANOSECONDS.convert(timeout, timeUnit);

		while (minRequestFinishTime + TimeUnit.NANOSECONDS.convert(quietPeriod, timeUnit) <= waitUntil) {
			// the maximum amount of time we can wait for active requests to finish that will still allow us to wait for quiescence
			// for the quietPeriod.
			long maxWaitTimeForActiveRequests = waitUntil - System.nanoTime() - TimeUnit.NANOSECONDS.convert(quietPeriod, timeUnit);

			// wait for active requests to finish
			boolean success = monitor.enterWhenUninterruptibly(requestNotActive, maxWaitTimeForActiveRequests, TimeUnit.NANOSECONDS);

			if (!success) {
				// timed out waiting for active requests to finish
				return false;
			}

			monitor.leave();

			// the time needed to monitor for new active requests is whenever the last request finished + the quiet period. this may be less
			// than the actual quiet period if no requests were active when entering waitForQuiescence, but the quietPeriod has not yet elapsed
			// since the last request.
			long waitForNewRequests = lastRequestFinishedNanos.get() - System.nanoTime() + TimeUnit.NANOSECONDS.convert(quietPeriod, timeUnit);

			// if the quietPeriod has already elapsed since the last request, no need to wait any longer
			if (waitForNewRequests < 0) {
				return true;
			}

			// wait for new requests to come in. if a new request comes in, the loop will restart, waiting for active requests to complete.
			boolean requestsActive = monitor.enterWhenUninterruptibly(requestActive, waitForNewRequests, TimeUnit.NANOSECONDS);

			if (requestsActive) {
				// a request became active, so we need to wait for all requests to finish again
				monitor.leave();

				continue;
			} else {
				return true;
			}
		}

		return false;
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy