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 }