View Javadoc

    1   /*
    2    *  Licensed to the Apache Software Foundation (ASF) under one
    3    *  or more contributor license agreements.  See the NOTICE file
    4    *  distributed with this work for additional information
    5    *  regarding copyright ownership.  The ASF licenses this file
    6    *  to you under the Apache License, Version 2.0 (the
    7    *  "License"); you may not use this file except in compliance
    8    *  with the License.  You may obtain a copy of the License at
    9    *
    10   *    http://www.apache.org/licenses/LICENSE-2.0
    11   *
    12   *  Unless required by applicable law or agreed to in writing,
    13   *  software distributed under the License is distributed on an
    14   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    15   *  KIND, either express or implied.  See the License for the
    16   *  specific language governing permissions and limitations
    17   *  under the License.
    18   *
    19   */
    20  package org.apache.mina.filter.executor;
    21
    22  import java.util.concurrent.atomic.AtomicInteger;
    23
    24  import org.apache.mina.core.session.IoEvent;
    25  import org.slf4j.Logger;
    26  import org.slf4j.LoggerFactory;
    27
    28  /**
    29   * Throttles incoming or outgoing events.
    30   *
    31   * @author <a href="http://mina.apache.org">Apache MINA Project</a>
    32   */
    33  public class IoEventQueueThrottle implements IoEventQueueHandler {
    34      /** A logger for this class */
    35      private final static Logger LOGGER = LoggerFactory.getLogger(IoEventQueueThrottle.class);
    36
    37      /** The event size estimator instance */
    38      private final IoEventSizeEstimator eventSizeEstimator;
    39
    40      private volatile int threshold;
    41
    42      private final Object lock = new Object();
    43
    44      private final AtomicInteger counter = new AtomicInteger();
    45
    46      private int waiters;
    47
    48      public IoEventQueueThrottle() {
    49          this(new DefaultIoEventSizeEstimator(), 65536);
    50      }
    51
    52      public IoEventQueueThrottle(int threshold) {
    53          this(new DefaultIoEventSizeEstimator(), threshold);
    54      }
    55
    56      public IoEventQueueThrottle(IoEventSizeEstimator eventSizeEstimator, int threshold) {
    57          if (eventSizeEstimator == null) {
    58              throw new IllegalArgumentException("eventSizeEstimator");
    59          }
    60          this.eventSizeEstimator = eventSizeEstimator;
    61
    62          setThreshold(threshold);
    63      }
    64
    65      public IoEventSizeEstimator getEventSizeEstimator() {
    66          return eventSizeEstimator;
    67      }
    68
    69      public int getThreshold() {
    70          return threshold;
    71      }
    72
    73      public int getCounter() {
    74          return counter.get();
    75      }
    76
    77      public void setThreshold(int threshold) {
    78          if (threshold <= 0) {
    79              throw new IllegalArgumentException("threshold: " + threshold);
    80          }
    81          this.threshold = threshold;
    82      }
    83
    84      public boolean accept(Object source, IoEvent event) {
    85          return true;
    86      }
    87
    88      public void offered(Object source, IoEvent event) {
    89          int eventSize = estimateSize(event);
    90          int currentCounter = counter.addAndGet(eventSize);
    91          logState();
    92
    93          if (currentCounter >= threshold) {
    94              block();
    95          }
    96      }
    97
    98      public void polled(Object source, IoEvent event) {
    99          int eventSize = estimateSize(event);
    100         int currentCounter = counter.addAndGet(-eventSize);
    101
    102         logState();
    103
    104         if (currentCounter < threshold) {
    105             unblock();
    106         }
    107     }
    108
    109     private int estimateSize(IoEvent event) {
    110         int size = getEventSizeEstimator().estimateSize(event);
    111         if (size < 0) {
    112             throw new IllegalStateException(IoEventSizeEstimator.class.getSimpleName() + " returned "
    113                     + "a negative value (" + size + "): " + event);
    114         }
    115         return size;
    116     }
    117
    118     private void logState() {
    119         if (LOGGER.isDebugEnabled()) {
    120             LOGGER.debug(Thread.currentThread().getName() + " state: " + counter.get() + " / " + getThreshold());
    121         }
    122     }
    123
    124     protected void block() {
    125         if (LOGGER.isDebugEnabled()) {
    126             LOGGER.debug(Thread.currentThread().getName() + " blocked: " + counter.get() + " >= " + threshold);
    127         }
    128
    129         synchronized (lock) {
    130             while (counter.get() >= threshold) {
    131                 waiters++;
    132                 try {
    133                     lock.wait();
    134                 } catch (InterruptedException e) {
    135                     // Wait uninterruptably.
    136                 } finally {
    137                     waiters--;
    138                 }
    139             }
    140         }
    141
    142         if (LOGGER.isDebugEnabled()) {
    143             LOGGER.debug(Thread.currentThread().getName() + " unblocked: " + counter.get() + " < " + threshold);
    144         }
    145     }
    146
    147     protected void unblock() {
    148         synchronized (lock) {
    149             if (waiters > 0) {
    150                 lock.notify();
    151             }
    152         }
    153     }
    154 }