📄 resourcedispatcher.java
字号:
// We need to get some or all of it from the extra items.
long toAsk = nbReserved > 0 ? quantity - nbReserved : quantity;
long res = holdExtra(toAsk);
if (res != toAsk) {
// Could not get enough. We got nothing.
releaseExtra(res);
return false;
}
}
// Now record it.
nbReserved -= quantity;
return true;
}
/**
* Try and grant a new item to this account. If it cannot be done,
* the account may be eligible for the next available extra item.
* That there is need is assumed (otherwise, why is this called ?).
* @return boolean true if an item was granted, false otherwise.
*/
public boolean obtainItem() {
// Set it for consistency. It will get cleared when
// the item is used to satisfy the need.
needs = true;
if (nbReserved > 0) {
notEligible();
--nbReserved;
return true; // Its pre-reserved.
}
// This account may deliberately limit the number of extra
// items it uses. this translates into a lower limit for
// nbReserved when <= 0.
if (nbReserved <= extraLimit) {
notEligible();
return false;
}
if (holdExtra(1) == 1) { // Need authorization.
notEligible();
--nbReserved;
return true;
}
// We are out of luck but eligible.
beEligible();
return false;
}
/**
* This will release a number of items at once rather than
* once. To be used in conjunctino with obtainItems(). See that
* method.
* @param quantity the number of items to be released.
*/
public void releaseQuantity(long quantity) {
if (nbReserved < 0) {
releaseExtra(quantity < -nbReserved ? quantity : -nbReserved);
}
nbReserved += quantity;
}
/**
* This will release an item and return the most eligible account to
* re-use this item for. The account that is returned has been granted
* the item and thus the invoker is expected to do with this account
* whatever an invoker of obtainItem() would do in case of success.
* If the items that are managed are threads, the invoker is
* likely to be one these threads and it should therefore process
* the returned account as it did the one for which it was calling
* releaseItem, however be very carefull not to process the new account
* in the context of the old one; that would rapidly lead to stack
* overflow. In other words, be carefull of not making a construct
* equivalent to:
* process() {
* doStuff();
* myAccount.releaseItem().getUserObject().process();
* }
*
* That won't work.
* Instead do:
* work() {
* while (myAccount != null) {
* myAccount.getUserObject().doStuff();
* myAccount = myAccount.releaseItem();
* }
* }
* Or similar; always go back to base stack level.
* It is mandatory to handle accounts returned by releaseItem().
* If handling leads to releaseItem, then it has to be done in a
* forever loop. No choice. That's typical if the items are threads.
* That's normally not happening if the items are memory.
*
* If this account is not using any extra
* item, it is the only eligible account to reuse this item.
* In that case, if this account needs the item, "this" is returned.
* Else the item is accounted as released and null is returned.
* If this account is using extra items, the item is accounted as
* released for this account, and granted to the most eligible account,
* which is returned. If roundRobin mode is OFF, the most eligible
* account can only be this.
* If no account has any need for the item, the item is counted
* as released for this item and globaly, and null is returned.
* If RoundRobin is ON, this account may not always be eligible
* for an extra item.
*
* @return ResourceAccount the account to which the released item
* has been re-assigned. null if the released item was not re-assigned.
*/
public ResourceAccount releaseItem() {
if (nbReserved < 0) {
if (eligibles == null) {
// RoundRobin is OFF either we reuse it or we let
// it go.
if (needs) return this;
++nbReserved;
releaseExtra(1);
return null;
}
// RoundRobin is ON, we compete with others for this item.
++nbReserved;
// Update our eligibility which depends on extraLimit and
// whether we have a use for the item or not.
if ((nbReserved > extraLimit) && needs) {
beEligible();
}
ClientAccount next = mostEligible();
if (next == null) {
releaseExtra(1); // noone wants it. return to main pool
} else {
next.granted();
}
return next;
}
// Since we are (back) in our reserved range, we can't be eligible
// for extra.
notEligible();
// In reserved range; we keep using the item if we need it.
if (needs) return this;
++nbReserved;
return null;
}
/**
* Call this with true as soon as account needs a new item.
* Call this with false as soon as account has all it needs.
* Accounts are taken out of the eligible list as soon as one
* item is obtained. Calling inNeed() is a way to get the item
* back in list (at the end) if the need is still present.
* @param needs Whether the account needs a new item or not.
*/
public void inNeed(boolean needs) {
this.needs = needs;
if ((nbReserved < 0) && (nbReserved > extraLimit) && needs) {
beEligible();
} else {
notEligible();
}
}
/**
* @return Object The userObject that was supplied when creating the
* account.
*/
public Object getUserObject() {
return userObject;
}
/**
* Set the userObject associated with that account.
*/
public void setUserObject(Object obj) {
userObject = obj;
}
/**
* Returns the number of reserved items that can still be obtained by
* this account.
* If that number is negative it means that all reserved items are
* currently in use, and the number is the opposite of the number
* of extra items that are also currently in use by that account.
* @return long The number of reserved items.
*/
public long getNbReserved() {
return nbReserved;
}
}
/**
* The list of eligible accounts.
*/
private Dlist eligibles;
/**
* Construct a Fair Resource Allocator with the given parameters:
* @param minAccounts The minimum number of client accounts that we want to
* garantee we can handle. <0 means 0
* @param minReservedPerAccount The minimum reservation request that we will
* always grant to accounts as long as we have less than minAccounts <0 means
* 0.
* @param maxReservedPerAccount The maximum reservation request that we ever
* will grant to any given account. <minReservedPerAccount means ==
* @param extraItems The total number of items that we will authorize
* beyond what has been reserved. <0 means 0.
* @param maxExtraPerAccount The maximum number of extra items we will ever
* let any given account occupy. <0 or >extraItems means ==extraItems.
* @param roundRobin If true, when there is no items available, all
* eligible accounts are put in a FIFO. Accounts release items often, and the
* oldest account in the FIFO will get it. If false, accounts always keep
* items for as long as they can use them, and there is no FIFO of eligible
* accounts. Accounts can obtain new resources only if available at the time
* they try to aquire it. RoundRobin is more fair but has more overhead.
* Neither mode will cause starvation as long as accounts reserve at least
* one item each. RoundRobin is most usefull when allocating threads.
*/
public ResourceDispatcher(long minAccounts, long minReservedPerAccount,
long maxReservedPerAccount, long extraItems,
long maxExtraPerAccount,
boolean roundRobin)
{
if (minAccounts < 0) minAccounts = 0;
if (minReservedPerAccount < 0) minReservedPerAccount = 0;
if (maxReservedPerAccount < minReservedPerAccount) {
maxReservedPerAccount = minReservedPerAccount;
}
if (extraItems < 0) extraItems = 0;
if ((maxExtraPerAccount < 0) || (maxExtraPerAccount > extraItems)) {
maxExtraPerAccount = extraItems;
}
this.extraItems = extraItems;
this.maxReservedPerAccount = maxReservedPerAccount;
this.minReservedPerAccount = minReservedPerAccount;
this.reservedItems = minAccounts * minReservedPerAccount;
this.maxExtraPerAccount = maxExtraPerAccount;
nbEligibles = 0;
if (roundRobin) eligibles = new Dlist();
}
private long holdReserved(long req) {
if (req > reservedItems) req = reservedItems;
reservedItems -= req;
return req;
}
private void releaseReserved(long nb) {
reservedItems += nb;
}
private long holdExtra(long req) {
if (req > extraItems) req = extraItems;
extraItems -= req;
return req;
}
private void releaseExtra(long nb) {
extraItems += nb;
}
private void newEligible(ClientAccount account) {
++nbEligibles;
eligibles.putLast(account);
}
private ClientAccount mostEligible() {
if (nbEligibles == 0) return null;
return (ClientAccount) eligibles.getFirst();
}
private void unEligible(ClientAccount account) {
--nbEligibles;
account.unlink();
}
// Not synch; it's just a snapshot for trace purposes.
public int getNbEligibles() {
return nbEligibles;
}
/**
* Creates and returns a new client account.
* @param nbReq the number of reserved items requested (may not be
* always granted in full). A negative value is taken to mean 0.
* @param maxExtra the number of additional items that this account
* authorizes to be allocated in addition to the reserved ones. This
* is typically usefull if the items are threads and if some accounts
* are not re-entrant. Then nbReq would be 1 and maxExtra would be 0.
* It is also permitted to have some accounts receive no items at all
* ever by setting nbReq and maxExtra both to zero. A negative maxExtra
* is taken as meaning no specified limit, in which case an actual limit
* may be set silently.
* @param userObject An opaque cookie that the account object will return
* when requested. This is usefull to relate an account returned by
* ClientAccount.releaseItem() to an invoking code relevant object.
* @return ResourceAccount An account with this allocator.
*/
public ResourceAccount newAccount(long nbReq, long maxExtra,
Object userObject) {
long extra = 0; // reserved from extra pool
long reserved = 0; // reserved from reserved pool
if (nbReq > maxReservedPerAccount) nbReq = maxReservedPerAccount;
// Anything beyond the minimum omes from extra items if there's
// enough.
if (nbReq > minReservedPerAccount) {
extra = holdExtra(nbReq - minReservedPerAccount);
nbReq = minReservedPerAccount;
}
// Then the minimum comes from reserved items, if we can.
reserved = holdReserved(nbReq);
nbReq -= reserved;
// If there's some letf to be had, it means that we're getting
// short on reserved items, we'll try to compensate by getting
// more items from extra, but the app should start getting rid
// of stale accounts if it can.
if (nbReq > 0) {
if (LOG.isEnabledFor(Priority.WARN)) {
LOG.warn("Accepting extra account on a best effort basis.");
}
extra += holdExtra(nbReq);
if (extra + reserved < minReservedPerAccount) {
// Even that was not enough to reach our minimal commitment.
// The app should realy consider some cleanup.
if (LOG.isEnabledFor(Priority.WARN)) {
LOG.warn("Accepting extra account with below minimal commitement.");
}
}
}
if ((maxExtra > maxExtraPerAccount) || (maxExtra < 0)) {
maxExtra = maxExtraPerAccount;
}
return new ClientAccount(reserved, extra, maxExtra, userObject);
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -