📄 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.13 2006/03/24 01:39:05 bondolo Exp $ * */package net.jxta.impl.util;import java.util.LinkedList;import org.apache.log4j.Logger;import org.apache.log4j.Level;import net.jxta.impl.util.ResourceAccount;/** This class does not in itself allocate anything; it just does accounting. Its role is to make sure that resource consumers ("accounts") are garaunteed 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. <p/><em>Note that this is all essentialy a limiter device. It assumes that the resources that are dispatched in that way are not otherwise in short supply.</em> <p/>The rules of the game are as follows: <p/>At initialization, an absolute maximum authorized number of items is computed. All item reservations and authorizations are done within this budget. <p/>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. <p/>The current strategy is as follows: The initialization parameters are:<ul> <li>minimum number of garanteed accounts: {@code minAccounts}</li> <li>minimum commitement to new accounts up to minAccounts: {@code minReserve}</li> <li>maximum commitement to new accounts: {@code maxReserve}</li> <li>extra number of dynamically allocatable items: {@code extraItems}</li> </ul> <p/>We infer the number of items dedicated to reservation: {@code reservedItems} That is {@code minReserve * minAccounts}. <p/>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. <p/>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. <p/>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. <p/>When resources are scarce, two modes of operations are available. <dl> <dt>Unfair</dt> <dd>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. <dd> <dt>RoundRobin<dt> <dd>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. </dd> </dl> <p/>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: <pre> a.releaseItem() == (a.needs ? a : null); </pre> */public class ResourceDispatcher { /** * Log4J Logger */ private final static transient Logger LOG = Logger.getLogger(ResourceDispatcher.class.getName()); private long extraItems; private long reservedItems; private final long maxReservedPerAccount; private final long minReservedPerAccount; private final long maxExtraPerAccount; private final long minExtraPoolSize; private int nbEligibles; private final String myName; 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 final long fromExtraItems; /** * The limit for extra items allocation. * When nbReserved is at or below that, extra items cannot be * granted. */ private final 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; } /** * {@inheritDoc} * * <p/>To accelerate return of resources to the global pool, one * may call close() explicitly. Otherwise it is called by * {@code finalize}. * * <p/>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 controller // without returning the resources ! if (LOG.isEnabledFor(Level.WARN)) { LOG.warn("An account was abandoned 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 = 0; } /** * {@inheritDoc} * * <p/>Will close the account. (close is idempotent). */ protected void finalize() throws Throwable { close(); super.finalize(); } /** * {@inheritDoc} */ 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(Level.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(); } /** * {@inheritDoc} */ public boolean obtainQuantity(long quantity) { if ((nbReserved - quantity) < extraLimit) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -