blob: abd3a29f4f36091315515166a52b5017237a3351 [file] [log] [blame]
/* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.etch.util.core.nio;
/**
* History keeps track of pooled resource allocations, plus maintains historical
* information to better judge what a reasonable pool size for resources is.
*/
public class History
{
/**
* Constructs a History.
* @param min the minimum number of used items.
* @param limit the maximum number of used items.
* @param len the length of the history of used items over time.
*/
public History( int min, int limit, int len )
{
if (min < 0)
throw new IllegalArgumentException( "min < 0" );
if (limit < min)
throw new IllegalArgumentException( "limit < min" );
if (len < 0)
throw new IllegalArgumentException( "len < 0" );
this.min = min;
this.limit = limit;
history = new int[len];
init();
}
/** The minimum number of used items. */
private final int min;
/** The maximum number of used items. */
private final int limit;
/** The history of used items over time. */
private final int[] history;
/**
* @return the maximum number of used items.
*/
public int limit()
{
return limit;
}
/**
* @return the minimum number of used items.
*/
public int min()
{
return min;
}
/**
* @return the length of the history.
*/
public int length()
{
return history.length;
}
/**
* Adjusts the number of items being used. This is the number of items which
* have been allocated but not yet released.
*
* @param k positive if items are being allocated, negative if items are
* being released. Items might be released by being returned to some system
* facility, or they might be saved in a pool.
* @return true if the new value of used does not exceed the limit.
* @throws IllegalStateException if the new value of used would be < 0.
*/
public boolean used( int k )
{
if (k == 0)
throw new IllegalArgumentException( "k == 0" );
int newUsed = used + k;
if (newUsed > limit)
return false; // overflow
if (newUsed < 0)
throw new IllegalStateException( "newUsed < 0" );
used = newUsed;
bump( used );
return true;
}
/**
* @return the number of items being used.
*/
public int getUsed()
{
return used;
}
private int used;
/**
* Adjusts the number of items actually allocated or freed from some system
* facility rather than just from a pool. The number of used items
* can never exceed the number allocated. The number of allocated items
* equals used plus the current size of the pool.
*
* @param k positive if items are being allocated, negative if items are
* being released.
*/
public void alloc( int k )
{
if (k == 0)
throw new IllegalArgumentException( "k == 0" );
int newAlloc = alloc + k;
if (newAlloc > limit)
throw new IllegalStateException( "newAlloc > limit" );
if (newAlloc < 0)
throw new IllegalStateException( "newAlloc < 0" );
alloc = newAlloc;
}
/**
* @return the number of items allocated.
*/
public int getAlloc()
{
return alloc;
}
private int alloc;
/**
* Initialize (or re-initialize) the history.
*/
public void init()
{
synchronized (history)
{
int h = Math.max( min, used );
for (int i = 0; i < history.length; i++)
history[i] = h;
highwatermark = h;
suggested = h;
}
}
/**
* Tickles the history. The oldest value of history is discarded, and the
* others are all pushed down one, and the newest value is initialized to
* min. This should be called periodically on a regular schedule.
*/
public void tickle()
{
synchronized (history)
{
if (history.length == 0)
return;
System.arraycopy( history, 0, history, 1, history.length - 1 );
history[0] = highwatermark;
highwatermark = Math.max( min, used );
// recalculate suggested size
suggested = recalc( history );
}
}
/**
* Recalculates the suggested size for pool of allocated but unused items.
* Various algorithms might be used, including those that might use average
* history (the default) or something based on 1st or 2nd order derivitives
* of that.
*
* @return recalculated suggested size for pool of allocated but unused
* items.
*/
protected int recalc( int[] hstry )
{
int sum = 0;
for (int h : hstry)
sum += h;
return sum / hstry.length;
}
/**
* Updates the high water mark in light of a new value of used.
*
* @param value the new value of used.
*/
private void bump( int value )
{
synchronized (history)
{
if (value > highwatermark)
highwatermark = value;
}
}
private int highwatermark;
private int suggested;
/**
* @return the suggested size of a pool for quick allocation. This
* might be statically configured or based on recent activity.
*/
public int suggested()
{
return suggested;
}
}