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 critical execution path.
18   *
19   * @author Ceki Gülcü
20   */
21  public class DefaultInvocationGate implements InvocationGate {
22  
23      static final int MASK_DECREASE_RIGHT_SHIFT_COUNT = 2;
24  
25      // experiments indicate that even for the most CPU intensive applications with 200 or more threads MASK
26      // values in the order of 0xFFFF is appropriate
27      private static final int MAX_MASK = 0xFFFF;
28      static final int DEFAULT_MASK = 0xF;
29  
30      private volatile long mask = DEFAULT_MASK;
31      //private volatile long lastMaskCheck = System.currentTimeMillis();
32  
33      // IMPORTANT: This field can be updated by multiple threads. It follows that
34      // its values may *not* be incremented sequentially. However, we don't care
35      // about the actual value of the field except that from time to time the
36      // expression (invocationCounter++ & mask) == mask) should be true.
37      private long invocationCounter = 0;
38  
39      // if less than thresholdForMaskIncrease milliseconds elapse between invocations of updateMaskIfNecessary()
40      // method, then the mask should be increased
41      private static final long MASK_INCREASE_THRESHOLD = 100;
42  
43      // if more than thresholdForMaskDecrease milliseconds elapse between invocations of updateMaskIfNecessary() method,
44      // then the mask should be decreased
45      private static final long MASK_DECREASE_THRESHOLD = MASK_INCREASE_THRESHOLD * 8;
46  
47      
48      public DefaultInvocationGate() {
49          this(MASK_INCREASE_THRESHOLD, MASK_DECREASE_THRESHOLD, System.currentTimeMillis());
50      }
51      
52      public  DefaultInvocationGate(long minDelayThreshold, long maxDelayThreshold, long currentTime) {
53          this.minDelayThreshold = minDelayThreshold;
54          this.maxDelayThreshold = maxDelayThreshold; 
55          this.lowerLimitForMaskMatch = currentTime + minDelayThreshold;
56          this.upperLimitForNoMaskMatch = currentTime + maxDelayThreshold;
57      }
58      
59      private long minDelayThreshold;
60      private long maxDelayThreshold;
61  
62      long lowerLimitForMaskMatch;
63      long upperLimitForNoMaskMatch;
64  
65      /*
66       * (non-Javadoc)
67       * 
68       * @see ch.qos.logback.core.util.InvocationGate#skipFurtherWork()
69       */
70      @Override
71      final public boolean isTooSoon(long currentTime) {
72          boolean maskMatch = ((invocationCounter++) & mask) == mask;
73  
74          if (maskMatch) {
75              if (currentTime < this.lowerLimitForMaskMatch) {
76                  increaseMask();
77              }
78              updateLimits(currentTime);
79          } else {
80              if (currentTime > this.upperLimitForNoMaskMatch) {
81                  decreaseMask();
82                  updateLimits(currentTime);
83                  return false;
84              }
85          }
86          return !maskMatch;
87      }
88  
89      private void updateLimits(long currentTime) {
90          this.lowerLimitForMaskMatch = currentTime + minDelayThreshold;
91          this.upperLimitForNoMaskMatch = currentTime + maxDelayThreshold;
92      }
93  
94  
95      // package private, for testing purposes only
96      long getMask() {
97          return mask;
98      }
99      
100     private void increaseMask() {
101         if (mask >= MAX_MASK)
102             return;
103         mask = (mask << 1) | 1;
104     }
105 
106     private void decreaseMask() {
107         mask = mask >>> MASK_DECREASE_RIGHT_SHIFT_COUNT;
108     }
109 
110     public long getInvocationCounter() {
111         return invocationCounter;
112     }
113 }