View Javadoc
1   /**
2    * Logback: the reliable, generic, fast and flexible logging framework.
3    * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
4    *
5    * This program and the accompanying materials are dual-licensed under
6    * either the terms of the Eclipse Public License v1.0 as published by
7    * the Eclipse Foundation
8    *
9    *   or (per the licensee's choosing)
10   *
11   * under the terms of the GNU Lesser General Public License version 2.1
12   * as published by the Free Software Foundation.
13   */
14  package ch.qos.logback.core.util;
15  
16  /**
17   * This class serves as a gateway for invocations of a "costly" operation on a
18   * critical execution path.
19   *
20   * @author Ceki Gülcü
21   */
22  public class DefaultInvocationGate implements InvocationGate {
23  
24      static final int MASK_DECREASE_RIGHT_SHIFT_COUNT = 2;
25  
26      // experiments indicate that even for the most CPU intensive applications with
27      // 200 or more threads MASK
28      // values in the order of 0xFFFF is appropriate
29      private static final int MAX_MASK = 0xFFFF;
30      static final int DEFAULT_MASK = 0xF;
31  
32      private volatile long mask = DEFAULT_MASK;
33      // private volatile long lastMaskCheck = System.currentTimeMillis();
34  
35      // IMPORTANT: This field can be updated by multiple threads. It follows that
36      // its values may *not* be incremented sequentially. However, we don't care
37      // about the actual value of the field except that from time to time the
38      // expression (invocationCounter++ & mask) == mask) should be true.
39      private long invocationCounter = 0;
40  
41      // if less than thresholdForMaskIncrease milliseconds elapse between invocations
42      // of updateMaskIfNecessary()
43      // method, then the mask should be increased
44      private static final long MASK_INCREASE_THRESHOLD = 100;
45  
46      // if more than thresholdForMaskDecrease milliseconds elapse between invocations
47      // of updateMaskIfNecessary() method,
48      // then the mask should be decreased
49      private static final long MASK_DECREASE_THRESHOLD = MASK_INCREASE_THRESHOLD * 8;
50  
51      public DefaultInvocationGate() {
52          this(MASK_INCREASE_THRESHOLD, MASK_DECREASE_THRESHOLD, System.currentTimeMillis());
53      }
54  
55      public DefaultInvocationGate(long minDelayThreshold, long maxDelayThreshold, long currentTime) {
56          this.minDelayThreshold = minDelayThreshold;
57          this.maxDelayThreshold = maxDelayThreshold;
58          this.lowerLimitForMaskMatch = currentTime + minDelayThreshold;
59          this.upperLimitForNoMaskMatch = currentTime + maxDelayThreshold;
60      }
61  
62      private long minDelayThreshold;
63      private long maxDelayThreshold;
64  
65      long lowerLimitForMaskMatch;
66      long upperLimitForNoMaskMatch;
67  
68      /*
69       * (non-Javadoc)
70       * 
71       * @see ch.qos.logback.core.util.InvocationGate#skipFurtherWork()
72       */
73      @Override
74      final public boolean isTooSoon(long currentTime) {
75          boolean maskMatch = ((invocationCounter++) & mask) == mask;
76  
77          if (maskMatch) {
78              if (currentTime < this.lowerLimitForMaskMatch) {
79                  increaseMask();
80              }
81              updateLimits(currentTime);
82          } else {
83              if (currentTime > this.upperLimitForNoMaskMatch) {
84                  decreaseMask();
85                  updateLimits(currentTime);
86                  return false;
87              }
88          }
89          return !maskMatch;
90      }
91  
92      private void updateLimits(long currentTime) {
93          this.lowerLimitForMaskMatch = currentTime + minDelayThreshold;
94          this.upperLimitForNoMaskMatch = currentTime + maxDelayThreshold;
95      }
96  
97      // package private, for testing purposes only
98      long getMask() {
99          return mask;
100     }
101 
102     private void increaseMask() {
103         if (mask >= MAX_MASK)
104             return;
105         mask = (mask << 1) | 1;
106     }
107 
108     private void decreaseMask() {
109         mask = mask >>> MASK_DECREASE_RIGHT_SHIFT_COUNT;
110     }
111 
112     public long getInvocationCounter() {
113         return invocationCounter;
114     }
115 }