📄 resourcedispatcher.java
字号:
/*
* Copyright (c) 2002 Sun Microsystems, Inc. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Sun Microsystems, Inc. for Project JXTA."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Sun", "Sun Microsystems, Inc.", "JXTA" and "Project JXTA" must
* not be used to endorse or promote products derived from this
* software without prior written permission. For written
* permission, please contact Project JXTA at http://www.jxta.org.
*
* 5. Products derived from this software may not be called "JXTA",
* nor may "JXTA" appear in their name, without prior written
* permission of Sun.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL SUN MICROSYSTEMS OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of Project JXTA. For more
* information on Project JXTA, please see
* <http://www.jxta.org/>.
*
* This license is based on the BSD license adopted by the Apache Foundation.
*
* $Id: ResourceDispatcher.java,v 1.6 2002/06/20 19:33:12 jice Exp $
*
*/
package net.jxta.impl.util;
// This class does not in itself allocate anything; it just does accounting.
// Its role is to make sure that resource consumers ("accounts")
// are garanteed to be able to hold a pre-determined number of items,
// the extra being granted on a first-come-first-serve basis.
// It just replies yes/no to an account that wants to allocate an item.
// Synchronizarion is external.
// Note that this is all essentialy a limitter device. It assumes
// that the resources that are dispatched in that way are not otherwise
// in short supply.
//
// The rules of the game are as follows:
// At initialization, an absolute maximum authorized number of items
// is computed. All item reservations and authorizations are done
// within this budget.
// At any given point in time, out of this maximum, a number of items are
// permanently reserved for the minimum garanteed to each current account,
// a number of items are set aside for future accounts garantee reservation,
// and the rest is open for dynamic attribution on a first come first serve
// basis.
// The current strategy is as follows:
//
// The initialization parameters are:
// - minimum number of garanteed accounts: minAccounts
// - minimum commitement to new accounts up to minAccounts: minReserve
// - maximum commitement to new accounts: maxReserve
// - extra number of dynamically allocatable items: extraItems
//
// We infer the number of items dedicated to reservation: reservedItems
// That is minReserve * minAccounts.
//
// Accounts can ask for a commitment in excess of minReserve. Any reservation
// made by an account beyond the minimum is satified from extraItems
// limitted by what's available and maxReserve. When minAccounts have
// registered, it is possible that reservedItems is exhausted. New accounts
// are then accepted on a best effort basis using extra items exclusively. This
// may cause such new accounts to be given a commitement inferior to minReserve,
// including zero. It is up to the account to reject the offer and give up
// by closing, or to go along with the offer. At this time, we do not try
// to raise the commitement made to an account while it is registered.
//
// During the life of the account, items are allocated first from the set
// reserved by this account. If the account is out of reserved items, an attempt
// is made at getting the item from extraItems.
//
// For each account we count the number of items reserved from reservedItems,
// reserved from extraItems, allocated from the local reserved items
// and allocated from extraItems separately. When an item is released, it is
// accounted to extraItems if the account had anything allocated from
// extra items, or to the local reserved items.
// When an account goes away, the number of items that were reserved from
// reserveItems go back to reserveItems and likewise for those comming
// from extraItems. This is done rather than giving priority to reserve
// items so that the system does not favor reservation beyond its initial
// parameters when an account goes away under load.
//
// When resources are scarce, two modes of operations are available.
// Unfair: each account keeps its items as long it has a use for them. If
// the allocation of a new item is denied for an account, the account just has
// to live with it and try again the next time more items are desired.
// RoundRobin: each account releases each item after one use. When allocation
// of a new item is denied for an account by reason of item shortage, the
// account is placed on a list of eligible accounts. Every time an item is
// released, it is re-assigned to the oldest eligible account.
// From an API point of view the difference is not visible: account users
// are advised to release items after one use. Release returns the account to
// which the item has been re-assigned. If RoundRobin is not used, then
// the item is always re-assigned to the account that releases it unless it
// is not needed, in which case it returns to the available pool.
// So, with round-robin off the following is true:
// a.releaseItem() == (a.needs ? a : null);
import org.apache.log4j.Category;
import org.apache.log4j.Priority;
import net.jxta.impl.util.ResourceAccount;
import java.util.LinkedList;
public class ResourceDispatcher {
private final static Category LOG =
Category.getInstance(ResourceDispatcher.class.getName());
private long extraItems;
private long reservedItems;
private long maxReservedPerAccount;
private long minReservedPerAccount;
private long maxExtraPerAccount;
private int nbEligibles;
class ClientAccount extends Dlink implements ResourceAccount {
/**
* Tells whether this account has any use for extra resources
* or not. This feature is required to support roundRobin mode
* properly when resources are scarce.
*/
private boolean needs;
/**
* The number of items reserved for this account that may still be
* allocated. This decrements when we grant an item allocation. When
* it is <= 0, new items may be obtained from extraItems. If obtained
* we still decrement. When an item is released, if this counter is
* < 0 we return the item to extra items. This counter gets
* incremented either way.
*/
private long nbReserved;
/**
* The number out of nbReserved that is due back in reservedItems
* when this account disapears.
* The rest goes back to extraItems.
* NOTE: If we go away with items unaccounted for, we take the option
* of accouting them as allocated. In other words, that amount is
* not returned to its right full item account. That's why we do not
* need to keep track of allocated items. The leak is felt
* by this allocator. Alternatively we could pretend that the
* leaked resources are not ours; but that migh break the actual
* allocator of the resource if it relies on our accounting.
*/
private long fromReservedItems;
/**
* Same idea but they have been reserved by reducing the number
* of extra items available.
*/
private long fromExtraItems;
/**
* The limit for extra items allocation.
* When nbReserved is at or below that, extra items cannot be
* granted.
*/
private long extraLimit;
/**
* The external object for which this account manages items.
* This is an opaque cookie to us. Whatever code invokes
* releaseItem knows what to do with it and the re-assigned item, but
* it needs to be told which of its own object got an item assigned.
*/
private Object userObject;
/**
* Creates a client account with this resource manager.
* Not for external use.
* @param fromReservedItems
* @param fromExtraItems
* @param extraLimit
* @param userObject
*/
ClientAccount(long fromReservedItems, long fromExtraItems,
long extraLimit, Object userObject) {
this.nbReserved = fromReservedItems + fromExtraItems;
this.fromReservedItems = fromReservedItems;
this.fromExtraItems = fromExtraItems;
this.extraLimit = -extraLimit;
this.userObject = userObject;
this.needs = false;
}
/**
* Tear down this client account.
* Return reserved resources to the main pool.
* To accelerate return of resources to the global pool, one
* may call close() explicitly. Otherwise it is called by finalize.
* Calling close() or letting the account be GC'ed while some of the
* resources have not been returned is an error, may create a
* leak and may display a warning message.
*/
public void close() {
notEligible();
userObject = null;
if ((nbReserved == 0)
&& (fromReservedItems == 0)
&& (fromExtraItems == 0)) return;
if (nbReserved < (fromReservedItems + fromExtraItems)) {
// !!! someone just gave up on its resource controler
// without returning the resources !
if (LOG.isEnabledFor(Priority.WARN)) {
LOG.warn("An account was abandonned with resources"
+ " still allocated.");
}
// Release whatever we can.
if (nbReserved >= fromReservedItems) {
releaseExtra(nbReserved - fromReservedItems);
releaseReserved(fromReservedItems);
} else if (nbReserved > 0) {
releaseReserved(nbReserved);
}
} else {
releaseReserved(fromReservedItems);
releaseExtra(fromExtraItems);
}
nbReserved = fromReservedItems = fromExtraItems = 0;
}
/**
* Will close the account.
* (close is idempotent).
*/
public void finalize() {
close();
}
/**
* Tells if this account is idle (that is, none of the resources
* that it controls are currently in use). This means it can be closed
* safely.
*/
public boolean isIdle() {
return (nbReserved == fromExtraItems + fromReservedItems);
}
public boolean isEligible() {
return isLinked();
}
/**
* Put that account in the queue of accounts elligible to
* receive a resource when one becomes available.
*/
public void beEligible() {
if ((eligibles != null) && ! isEligible()) newEligible(this);
}
/**
* Remove that account from the queue of accounts elligible to
* receive a resource when one becomes available.
*/
public void notEligible() {
if ((eligibles != null) && isEligible()) unEligible(this);
}
// An extra item is being granted to this account (by being reassigned
// from another account upon release).
private void granted() {
// In theory, there cannot be an account that should NOT be granted
// an item in the eligibles list. For now, check whether the theory
// survives observations.
// It could happen that the need flag was turned off while this
// account was in the eligible list. That's not realy a problem.
// Either it will be released immediately, or we could filter
// it in mostEligible().
if (LOG.isEnabledFor(Priority.WARN)) {
if (nbReserved <= extraLimit) {
LOG.warn("An account that should not get an item" +
" was found in the eligibles list");
}
}
--nbReserved;
// We've been assigned an item. No-longer eligible.
notEligible();
}
/**
* Try and grant a certain quantity.
*
* It is usefull to manage the allocation of variable sized aggregates
* when what matters is the cummulated quantity rather than an item
* count. Quantity could be a number of bytes needed to store
* something for example. The advantage of using this method rather
* than obtainItem repeatedly is that it is obvisouly faster if
* quantity is more than one or two, and also that it is atomic;
* the entire quantity is either granted or denied.
* Using this routine is by definition incompatible with the round-robin
* mode, which could only re-assign quantities of 1.
*
* It is legal to use this routine along with round-robin mode if the
* same dispatcher is used to manage quantities of 1 in this manner,
* but an account that has failed to obtain its desired quantity is
* not queued for later re-assignment. And items released with
* releaseQuantity() are not re-assigned, so overall it is
* probably best to not mix the two.
*
* @param number The number of units wanted. The unit is harbitrary
* It is only meaningfull to the code that uses this dispatcher.
* @return boolean whether the requested quantity is authorized.
*/
public boolean obtainQuantity(long quantity) {
if ((nbReserved - quantity) < extraLimit) {
// That's asking too much. Denied.
return false;
}
if (quantity > nbReserved) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -