java.lang.Object
net.i2p.router.transport.FIFOBandwidthLimiter

public class FIFOBandwidthLimiterextends Object
FIFO-based bandwidth limiter for managing inbound and outbound traffic.

This class provides bandwidth management using First-In-First-Out queues for both inbound and outbound traffic. It implements token bucket algorithm to control data rates and prevent burst traffic patterns.

Core Features:

  • FIFO request queuing for fair bandwidth allocation
  • Token bucket rate limiting
  • Separate inbound and outbound management
  • Configurable bandwidth limits and refill rates
  • Thread-safe operations with atomic counters
  • Request satisfaction and partial fulfillment handling

Concurrency Strategy:

  • Java 5: Used synchronized ArrayList with head/tail access
  • Java 6+: Uses LinkedBlockingDeque for lock-free operations
  • Request polling from queue head for efficiency
  • Partial request satisfaction with push-back mechanism

Algorithm:

  • Token refill at fixed intervals
  • Request processing when tokens available
  • Burst prevention through token depletion
  • Priority-based request handling
  • Field Details

    • _log

      private final Log _log
      Logger for the bandwidth limiter.
    • _context

      private final RouterContext _context
      The router context.
    • _pendingInboundRequests

      private final List<FIFOBandwidthLimiter.SimpleRequest> _pendingInboundRequests
      Inbound requests waiting for capacity.
    • _pendingOutboundRequests

      private final List<FIFOBandwidthLimiter.SimpleRequest> _pendingOutboundRequests
      Outbound requests waiting for capacity.
    • _availableInbound

      private final AtomicInteger _availableInbound
      How many bytes we can consume for inbound transmission immediately.
    • _availableOutbound

      private final AtomicInteger _availableOutbound
      How many bytes we can consume for outbound transmission immediately.
    • _unavailableInboundBurst

      private final AtomicInteger _unavailableInboundBurst
      How many bytes we can queue up for bursting.
    • _unavailableOutboundBurst

      private final AtomicInteger _unavailableOutboundBurst
      How many bytes we can queue up for bursting.
    • _maxInboundBurst

      private volatile int _maxInboundBurst
      How large _unavailableInbound can get.
    • _maxOutboundBurst

      private volatile int _maxOutboundBurst
      How large _unavailableInbound can get.
    • _maxInbound

      private volatile int _maxInbound
      How large _availableInbound can get - aka our inbound rate during a burst.
    • _maxOutbound

      private volatile int _maxOutbound
      How large _availableOutbound can get - aka our outbound rate during a burst.
    • _outboundUnlimited

      private volatile boolean _outboundUnlimited
      Shortcut of whether our outbound rate is unlimited - UNUSED, always false for now.
    • _inboundUnlimited

      private volatile boolean _inboundUnlimited
      Shortcut of whether our inbound rate is unlimited - UNUSED, always false for now.
    • _totalAllocatedInboundBytes

      private final AtomicLong _totalAllocatedInboundBytes
      Lifetime counter of bytes received.
    • _totalAllocatedOutboundBytes

      private final AtomicLong _totalAllocatedOutboundBytes
      Lifetime counter of bytes sent.
    • __requestId

      private static final AtomicLong __requestId
      Counter handing out request ids.
    • _refiller

      private final FIFOBandwidthRefiller _refiller
      Job that refills the capacity buckets.
    • _refillerThread

      private final Thread _refillerThread
      Thread running the refiller.
    • _lastTotalSent

      private volatile long _lastTotalSent
      Total sent when the stats were last updated.
    • _lastTotalReceived

      private volatile long _lastTotalReceived
      Total received when the stats were last updated.
    • _lastStatsUpdated

      private volatile long _lastStatsUpdated
      When the rate stats were last updated.
    • _sendBps

      private volatile float _sendBps
      Cached send rate, in bytes per second.
    • _recvBps

      private volatile float _recvBps
      Cached receive rate, in bytes per second.
    • _sendBps15s

      private volatile float _sendBps15s
      Cached send rate over 15 seconds.
    • _recvBps15s

      private volatile float _recvBps15s
      Cached receive rate over 15 seconds.
    • _noop

      private static final FIFOBandwidthLimiter.NoopRequest _noop
      The shared request that consumes no capacity.
  • Constructor Details

    • FIFOBandwidthLimiter

      public FIFOBandwidthLimiter(RouterContext context)
      Creates a limiter with empty queues and starts the background refiller.
      Parameters:
      context - the router context, used for the clock, logging, statistics and the refiller
  • Method Details

    • now

      public long now()
      Current time in milliseconds from the System clock.
      Returns:
      milliseconds since the epoch, from System.currentTimeMillis()
    • getTotalAllocatedInboundBytes

      public long getTotalAllocatedInboundBytes()
      Total bytes allocated for inbound messages since start.
      Returns:
      the total allocated inbound bytes
    • getTotalAllocatedOutboundBytes

      public long getTotalAllocatedOutboundBytes()
      Total bytes allocated for outbound messages since start.
      Returns:
      the total allocated outbound bytes
    • getSendBps

      public float getSendBps()
      Smoothed one-second send rate.
      Returns:
      smoothed one second rate
    • getReceiveBps

      public float getReceiveBps()
      Smoothed one-second receive rate.
      Returns:
      smoothed one second rate
    • getSendBps15s

      public float getSendBps15s()
      Smoothed 15-second send rate.
      Returns:
      smoothed 15 second rate
    • getReceiveBps15s

      public float getReceiveBps15s()
      Smoothed 15-second receive rate.
      Returns:
      smoothed 15 second rate
    • getOutboundKBytesPerSecond

      public int getOutboundKBytesPerSecond()
      The configured maximum, not the current rate. In binary K, i.e. rate / 1024.
      Returns:
      the outbound k bytes per second
    • getInboundKBytesPerSecond

      public int getInboundKBytesPerSecond()
      The configured maximum, not the current rate. In binary K, i.e. rate / 1024.
      Returns:
      the inbound k bytes per second
    • getOutboundBurstKBytesPerSecond

      public int getOutboundBurstKBytesPerSecond()
      The configured maximum, not the current rate. In binary K, i.e. rate / 1024.
      Returns:
      the outbound burst k bytes per second
    • getInboundBurstKBytesPerSecond

      public int getInboundBurstKBytesPerSecond()
      The configured maximum, not the current rate. In binary K, i.e. rate / 1024.
      Returns:
      the inbound burst k bytes per second
    • reinitialize

      public void reinitialize()
      Clear and reinitialize the refiller and queues.
    • shutdown

      public void shutdown()
      Shut down the bandwidth limiter.
    • clear

      private void clear()
      Clear all pending requests and reset counters.
    • sentParticipatingMessage

      public boolean sentParticipatingMessage(int size, float factor)
      We intend to send traffic for a participating tunnel with the given size and adjustment factor. Returns true if the message can be sent within the current share bandwidth limits, or false if it should be dropped.
      Parameters:
      size - the size of the participating message in bytes
      factor - multiplier of size for the drop calculation, 1 for no adjustment
      Returns:
      true for accepted, false for drop
    • receivedParticipatingMessage

      public boolean receivedParticipatingMessage(int size, float factor)
      Check if we should accept an inbound participating message.
      Parameters:
      size - the size of the participating message in bytes
      factor - multiplier of size for the drop calculation, 1 for no adjustment
      Returns:
      true for accepted, false for drop
    • getCurrentParticipatingBandwidth

      public int getCurrentParticipatingBandwidth()
      Out bandwidth. Actual bandwidth, not smoothed, not bucketed.
      Returns:
      Bps in recent period (a few seconds)
    • getCurrentParticipatingBandwidthIn

      public int getCurrentParticipatingBandwidthIn()
      In bandwidth. Actual bandwidth, not smoothed, not bucketed.
      Returns:
      Bps in recent period (a few seconds)
    • getMaxShareBandwidth

      public int getMaxShareBandwidth()
      In Bytes per second.
      Returns:
      the max share bandwidth
    • requestInbound

      public FIFOBandwidthLimiter.Request requestInbound(int bytesIn, String purpose)
      Request some bytes. Does not block.
      Parameters:
      bytesIn - the number of bytes requested
      purpose - the purpose of the request, for logging
      Returns:
      the request to use; never null
    • requestInbound

      public FIFOBandwidthLimiter.Request requestInbound(FIFOBandwidthLimiter.Request candidate, int bytesIn, String purpose)
      Request some bytes, reusing the given request if possible. Does not block.

      If the candidate is a SimpleRequest that is complete (fully allocated and not aborted), it is reset and re-queued, avoiding a new allocation. If the candidate is the shared no-op request and the shortcut still applies, it is returned as-is.

      The reuse check, reset, and re-queue are serialized on the pending inbound queue so the SimpleRequest is never doubled up by concurrent allocators or concurrent reuse of the same candidate.

      Parameters:
      candidate - the request from a previous allocation for this consumer, or null for a new request
      bytesIn - the number of bytes requested
      purpose - the purpose of the request, for logging
      Returns:
      the request to use; never null
    • resetIfReusable

      private boolean resetIfReusable(FIFOBandwidthLimiter.SimpleRequest sr, int bytesIn, int priority)
      Reset a previously allocated SimpleRequest for reuse if it is complete (fully allocated) and not aborted. The check and the reset are performed together under the pending inbound queue lock, so the request cannot be observed by an allocator, reset twice, or re-queued by two threads.

      This deliberately acquires the pending queue lock before the request lock, matching the ordering used by the satisfy/refill paths. Acquiring them in the opposite order (request lock then queue lock) can deadlock against a refill pass, so callers of this method must never hold a SimpleRequest lock while taking the inbound queue lock.

      Parameters:
      sr - the SimpleRequest to consider for reuse; never the shared no-op
      bytesIn - the number of bytes for the reused request
      priority - 0 for now
      Returns:
      true if the request was reset and should be re-queued
    • requestInbound

      private void requestInbound(FIFOBandwidthLimiter.SimpleRequest req, int bytesIn, String purpose)
      The transports don't use this any more, so make it private and a SimpleRequest instead of a Request So there's no more casting.
      Parameters:
      req - the request to enqueue, satisfied when bytes are available
      bytesIn - the number of inbound bytes wanted
      purpose - label naming the requester, currently unused
    • requestOutbound

      public FIFOBandwidthLimiter.Request requestOutbound(int bytesOut, int priority, String purpose)
      Request some bytes. Does not block.
      Parameters:
      bytesOut - the number of bytes requested
      priority - 0 for now
      purpose - the purpose of the request, for logging
      Returns:
      the request to use; never null
    • requestOutbound

      public FIFOBandwidthLimiter.Request requestOutbound(FIFOBandwidthLimiter.Request candidate, int bytesOut, int priority, String purpose)
      Request some bytes, reusing the given request if possible. Does not block.

      If the candidate is a SimpleRequest that is complete (fully allocated and not aborted), it is reset and re-queued, avoiding a new allocation. If the candidate is the shared no-op request and the shortcut still applies, it is returned as-is.

      Parameters:
      candidate - the request from a previous allocation for this consumer, or null for a new request
      bytesOut - the number of bytes requested
      priority - 0 for now
      purpose - the purpose of the request, for logging
      Returns:
      the request to use; never null
    • resetIfReusableOutbound

      private boolean resetIfReusableOutbound(FIFOBandwidthLimiter.SimpleRequest sr, int bytesOut, int priority)
      Reset a previously allocated SimpleRequest for reuse if it is complete (fully allocated) and not aborted. The check and the reset are performed together under the pending outbound queue lock, so the request cannot be observed by an allocator, reset twice, or re-queued by two threads.

      This deliberately acquires the pending queue lock before the request lock, matching the ordering used by the satisfy/refill paths. Acquiring them in the opposite order (request lock then queue lock) can deadlock against a refill pass, so callers of this method must never hold a SimpleRequest lock while taking the outbound queue lock.

      Parameters:
      sr - the SimpleRequest to consider for reuse; never the shared no-op
      bytesOut - the number of bytes for the reused request
      priority - 0 for now
      Returns:
      true if the request was reset and should be re-queued
    • requestOutbound

      private void requestOutbound(FIFOBandwidthLimiter.SimpleRequest req, int bytesOut, String purpose)
      Queues an outbound request for capacity.
      Parameters:
      req - the request to queue
      bytesOut - bytes of capacity wanted
      purpose - the request purpose for the log
    • setInboundBurstKBps

      void setInboundBurstKBps(int kbytesPerSecond)
      Inbound burst rate in KBps.
      Parameters:
      kbytesPerSecond - the new inbound burst rate in KBps, stored internally in bytes
    • setOutboundBurstKBps

      void setOutboundBurstKBps(int kbytesPerSecond)
      Outbound burst rate in KBps.
      Parameters:
      kbytesPerSecond - the new outbound burst rate in KBps, stored internally in bytes
    • getInboundBurstBytes

      public int getInboundBurstBytes()
      The max inbound burst, in bytes.
      Returns:
      the inbound burst bytes
    • getOutboundBurstBytes

      public int getOutboundBurstBytes()
      The max outbound burst, in bytes.
      Returns:
      the outbound burst bytes
    • setInboundBurstBytes

      void setInboundBurstBytes(int bytes)
      Inbound burst maximum, in bytes.
      Parameters:
      bytes - the new inbound burst maximum, in bytes per second
    • setOutboundBurstBytes

      void setOutboundBurstBytes(int bytes)
      Outbound burst maximum, in bytes.
      Parameters:
      bytes - the new outbound burst maximum, in bytes per second
    • getStatus

      StringBuilder getStatus()
      The current status string.
      Returns:
      the available, max, burst and burst-max rates for both directions
    • getInboundStatus

      private StringBuilder getInboundStatus()
      The inbound bandwidth status.
      Returns:
      the inbound status
    • getOutboundStatus

      private StringBuilder getOutboundStatus()
      The outbound bandwidth status.
      Returns:
      the outbound status
    • refillBandwidthQueues

      final void refillBandwidthQueues(List<FIFOBandwidthLimiter.Request> buf, long bytesInbound, long bytesOutbound, long maxBurstIn, long maxBurstOut)
      More bytes are available - add them to the queue and satisfy any requests we can.
      Parameters:
      buf - contains satisfied outbound requests, really just to avoid object thrash, not really used
      bytesInbound - allowance to add to the inbound queue, in bytes, for the elapsed period
      bytesOutbound - allowance to add to the outbound queue, in bytes, for the elapsed period
      maxBurstIn - allow up to this many bytes in from the burst section for this time period (may be negative)
      maxBurstOut - allow up to this many bytes in from the burst section for this time period (may be negative)
    • updateStats

      private void updateStats()
      Recomputes the cached rate statistics.
    • satisfyRequests

      private final void satisfyRequests(List<FIFOBandwidthLimiter.Request> buffer)
      Go through the queue, satisfying as many requests as possible (notifying each one satisfied that the request has been granted).
      Parameters:
      buffer - Out parameter, returned with the satisfied outbound requests only
    • satisfyInboundRequests

      private final void satisfyInboundRequests(List<FIFOBandwidthLimiter.Request> satisfied)
      Satisfy the pending inbound requests with currently available bandwidth.
      Parameters:
      satisfied - Out parameter, returned with the satisfied requests added
    • locked_getLongestInboundWait

      private long locked_getLongestInboundWait()
      Called from debug logging only.
      Returns:
      how long the longest-waiting inbound request has waited in ms, 0 if none pending
    • locked_getLongestOutboundWait

      private long locked_getLongestOutboundWait()
      Called from debug logging only.
      Returns:
      how long the longest-waiting outbound request has waited in ms, 0 if none pending
    • locked_satisfyInboundUnlimited

      private final void locked_satisfyInboundUnlimited(List<FIFOBandwidthLimiter.Request> satisfied)
      There are no limits, so just give every inbound request whatever they want.
      Parameters:
      satisfied - out param, list of requests that were completely satisfied
    • locked_satisfyInboundAvailable

      private final void locked_satisfyInboundAvailable(List<FIFOBandwidthLimiter.Request> satisfied)
      We have limits, so iterate through the requests, allocating as much bandwidth as we can to those who have used what we have given them and are waiting for more (giving priority to the first ones who requested it).
      Parameters:
      satisfied - out param, list of requests that were completely satisfied
    • satisfyOutboundRequests

      private final void satisfyOutboundRequests(List<FIFOBandwidthLimiter.Request> satisfied)
      Satisfy the pending outbound requests with currently available bandwidth.
      Parameters:
      satisfied - Out parameter, returned with the satisfied requests added
    • locked_satisfyOutboundUnlimited

      private final void locked_satisfyOutboundUnlimited(List<FIFOBandwidthLimiter.Request> satisfied)
      There are no limits, so just give every outbound request whatever they want.
      Parameters:
      satisfied - out param, list of requests that were completely satisfied
    • locked_satisfyOutboundAvailable

      private final void locked_satisfyOutboundAvailable(List<FIFOBandwidthLimiter.Request> satisfied)
      We have limits, so iterate through the requests, allocating as much bandwidth as we can to those who have used what we have given them and are waiting for more (giving priority to the first ones who requested it).
      Parameters:
      satisfied - out param, list of requests that were completely satisfied
    • shortcutSatisfyInboundRequest

      private boolean shortcutSatisfyInboundRequest(int requested)
      Lockless total satisfaction, at some minor risk of exceeding the limits and driving the available counter below zero.
      Parameters:
      requested - number of bytes
      Returns:
      true if the request was satisfied outright, false if it was queued
    • shortcutSatisfyOutboundRequest

      private boolean shortcutSatisfyOutboundRequest(int requested)
      Lockless total satisfaction, at some minor risk of exceeding the limits and driving the available counter below zero.
      Parameters:
      requested - number of bytes
      Returns:
      true if the request was satisfied outright, false if it was queued