Github user rmaucher commented on a diff in the pull request:
https://github.com/apache/tomcat/pull/126#discussion_r224803205
--- Diff: java/org/apache/tomcat/util/net/RateLimiter.java ---
@@ -0,0 +1,163 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.tomcat.util.net;
+
+import java.io.IOException;
+
+/** Abstract base class to rate limit IO. Typically implementations are
+ * shared across multiple IndexInputs or IndexOutputs (for example
+ * those involved all merging). Those IndexInputs and
+ * IndexOutputs would call {@link #pause} whenever the have read
+ * or written more than {@link #getMinPauseCheckBytes} bytes. */
+public abstract class RateLimiter {
+
+ /**
+ * Sets an updated MB per second rate limit.
+ */
+ public abstract void setMBPerSec(double mbPerSec);
+
+ /**
+ * The current MB per second rate limit.
+ */
+ public abstract double getMBPerSec();
+
+ /** Pauses, if necessary, to keep the instantaneous IO
+ * rate at or below the target.
+ *
+ * Note: the implementation is thread-safe
+ *
+ * @return the pause time in nano seconds
+ * */
+ public abstract long pause(long bytes);
+
+ /** How many bytes caller should add up itself before invoking {@link
#pause}. */
+ public abstract long getMinPauseCheckBytes();
+
+ /**
+ * Simple class to rate limit IO.
+ */
+ public static class SimpleRateLimiter extends RateLimiter {
+
+private final static int MIN_PAUSE_CHECK_MSEC = 5;
+
+private volatile double mbPerSec;
+private volatile long minPauseCheckBytes;
+private long lastNS;
+
+// TODO: we could also allow eg a sub class to dynamically
+// determine the allowed rate, eg if an app wants to
+// change the allowed rate over time or something
+
+/** mbPerSec is the MB/sec max IO rate */
+public SimpleRateLimiter(double mbPerSec) {
+ setMBPerSec(mbPerSec);
+ lastNS = System.nanoTime();
+}
+
+/**
+ * Sets an updated mb per second rate limit.
+ */
+@Override
+public void setMBPerSec(double mbPerSec) {
+ this.mbPerSec = mbPerSec;
+ minPauseCheckBytes = (long) ((MIN_PAUSE_CHECK_MSEC / 1000.0) *
mbPerSec * 1024 * 1024);
+}
+
+@Override
+public long getMinPauseCheckBytes() {
+ return minPauseCheckBytes;
+}
+
+/**
+ * The current mb per second rate limit.
+ */
+@Override
+public double getMBPerSec() {
+ return this.mbPerSec;
+}
+
+/** Pauses, if necessary, to keep the instantaneous IO
+ * rate at or below the target. Be sure to only call
+ * this method when bytes > {@link #getMinPauseCheckBytes},
+ * otherwise it will pause way too long!
+ *
+ * @return the pause time in nano seconds */
+@Override
+public long pause(long bytes) {
+
+ long startNS = System.nanoTime();
+
+ double secondsToPause = (bytes/1024./1024.) / mbPerSec;
+
+ long targetNS;
+
+ // Sync'd to read + write lastNS:
+ synchronized (this) {
+
+// Time we should sleep until; this is purely instantaneous
+// rate (just adds seconds onto the last time we had paused to);
+// maybe we should also offer decayed recent history one?
+targetNS = lastNS + (long) (10 * secondsToPause);
+
+if (startNS >= targetNS) {
+ // OK, current time is already beyond the target sleep time,
+ // no pausing to do.
+
+ // Set to startNS, not targetNS, to enforce the instant rate, not
+ // the "averaaged over all history" rate:
+ lastNS = startNS;
+ return 0;
+