001/** 002 * Logback: the reliable, generic, fast and flexible logging framework. 003 * Copyright (C) 1999-2015, QOS.ch. All rights reserved. 004 * 005 * This program and the accompanying materials are dual-licensed under 006 * either the terms of the Eclipse Public License v1.0 as published by 007 * the Eclipse Foundation 008 * 009 * or (per the licensee's choosing) 010 * 011 * under the terms of the GNU Lesser General Public License version 2.1 012 * as published by the Free Software Foundation. 013 */ 014package ch.qos.logback.core.util; 015 016/** 017 * This class serves as a gateway for invocations of a "costly" operation on a 018 * critical execution path. 019 * 020 * @author Ceki Gülcü 021 */ 022public class DefaultInvocationGate implements InvocationGate { 023 024 static final int MASK_DECREASE_RIGHT_SHIFT_COUNT = 2; 025 026 // experiments indicate that even for the most CPU intensive applications with 027 // 200 or more threads MASK 028 // values in the order of 0xFFFF is appropriate 029 private static final int MAX_MASK = 0xFFFF; 030 static final int DEFAULT_MASK = 0xF; 031 032 private volatile long mask = DEFAULT_MASK; 033 // private volatile long lastMaskCheck = System.currentTimeMillis(); 034 035 // IMPORTANT: This field can be updated by multiple threads. It follows that 036 // its values may *not* be incremented sequentially. However, we don't care 037 // about the actual value of the field except that from time to time the 038 // expression (invocationCounter++ & mask) == mask) should be true. 039 private long invocationCounter = 0; 040 041 // if less than thresholdForMaskIncrease milliseconds elapse between invocations 042 // of updateMaskIfNecessary() 043 // method, then the mask should be increased 044 private static final long MASK_INCREASE_THRESHOLD = 100; 045 046 // if more than thresholdForMaskDecrease milliseconds elapse between invocations 047 // of updateMaskIfNecessary() method, 048 // then the mask should be decreased 049 private static final long MASK_DECREASE_THRESHOLD = MASK_INCREASE_THRESHOLD * 8; 050 051 public DefaultInvocationGate() { 052 this(MASK_INCREASE_THRESHOLD, MASK_DECREASE_THRESHOLD, System.currentTimeMillis()); 053 } 054 055 public DefaultInvocationGate(long minDelayThreshold, long maxDelayThreshold, long currentTime) { 056 this.minDelayThreshold = minDelayThreshold; 057 this.maxDelayThreshold = maxDelayThreshold; 058 this.lowerLimitForMaskMatch = currentTime + minDelayThreshold; 059 this.upperLimitForNoMaskMatch = currentTime + maxDelayThreshold; 060 } 061 062 private long minDelayThreshold; 063 private long maxDelayThreshold; 064 065 long lowerLimitForMaskMatch; 066 long upperLimitForNoMaskMatch; 067 068 /* 069 * (non-Javadoc) 070 * 071 * @see ch.qos.logback.core.util.InvocationGate#skipFurtherWork() 072 */ 073 @Override 074 final public boolean isTooSoon(long currentTime) { 075 boolean maskMatch = ((invocationCounter++) & mask) == mask; 076 077 if (maskMatch) { 078 if (currentTime < this.lowerLimitForMaskMatch) { 079 increaseMask(); 080 } 081 updateLimits(currentTime); 082 } else { 083 if (currentTime > this.upperLimitForNoMaskMatch) { 084 decreaseMask(); 085 updateLimits(currentTime); 086 return false; 087 } 088 } 089 return !maskMatch; 090 } 091 092 private void updateLimits(long currentTime) { 093 this.lowerLimitForMaskMatch = currentTime + minDelayThreshold; 094 this.upperLimitForNoMaskMatch = currentTime + maxDelayThreshold; 095 } 096 097 // package private, for testing purposes only 098 long getMask() { 099 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}