0001: /*
0002: * Distributed as part of c3p0 v.0.9.1.2
0003: *
0004: * Copyright (C) 2005 Machinery For Change, Inc.
0005: *
0006: * Author: Steve Waldman <swaldman@mchange.com>
0007: *
0008: * This library is free software; you can redistribute it and/or modify
0009: * it under the terms of the GNU Lesser General Public License version 2.1, as
0010: * published by the Free Software Foundation.
0011: *
0012: * This software is distributed in the hope that it will be useful,
0013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
0015: * GNU Lesser General Public License for more details.
0016: *
0017: * You should have received a copy of the GNU Lesser General Public License
0018: * along with this software; see the file LICENSE. If not, write to the
0019: * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
0020: * Boston, MA 02111-1307, USA.
0021: */
0022:
0023: package com.mchange.v2.resourcepool;
0024:
0025: import java.util.*;
0026: import com.mchange.v2.async.*;
0027: import com.mchange.v2.log.*;
0028: import com.mchange.v2.lang.ThreadUtils;
0029: import com.mchange.v2.util.ResourceClosedException;
0030:
0031: class BasicResourcePool implements ResourcePool {
0032: private final static MLogger logger = MLog
0033: .getLogger(BasicResourcePool.class);
0034:
0035: final static int AUTO_CULL_FREQUENCY_DIVISOR = 4;
0036: final static int AUTO_MAX_CULL_FREQUENCY = (15 * 60 * 1000); //15 mins
0037: final static int AUTO_MIN_CULL_FREQUENCY = (1 * 1000); //15 mins
0038:
0039: //XXX: temporary -- for selecting between AcquireTask types
0040: // remove soon, and use only ScatteredAcquireTask,
0041: // presuming no problems appear
0042: final static String USE_SCATTERED_ACQUIRE_TASK_KEY = "com.mchange.v2.resourcepool.experimental.useScatteredAcquireTask";
0043: final static boolean USE_SCATTERED_ACQUIRE_TASK;
0044: static {
0045: String checkScattered = com.mchange.v2.cfg.MultiPropertiesConfig
0046: .readVmConfig().getProperty(
0047: USE_SCATTERED_ACQUIRE_TASK_KEY);
0048: if (checkScattered != null
0049: && checkScattered.trim().toLowerCase().equals("true")) {
0050: USE_SCATTERED_ACQUIRE_TASK = true;
0051: if (logger.isLoggable(MLevel.INFO))
0052: logger.info(BasicResourcePool.class.getName()
0053: + " using experimental ScatteredAcquireTask.");
0054: } else
0055: USE_SCATTERED_ACQUIRE_TASK = false;
0056: }
0057: // end temporary switch between acquire task types
0058:
0059: //MT: unchanged post c'tor
0060: final Manager mgr;
0061:
0062: final int start;
0063: final int min;
0064: final int max;
0065: final int inc;
0066:
0067: final int num_acq_attempts;
0068: final int acq_attempt_delay;
0069:
0070: final long check_idle_resources_delay; //milliseconds
0071: final long max_resource_age; //milliseconds
0072: final long max_idle_time; //milliseconds
0073: final long excess_max_idle_time; //milliseconds
0074: final long destroy_unreturned_resc_time; //milliseconds
0075: final long expiration_enforcement_delay; //milliseconds
0076:
0077: final boolean break_on_acquisition_failure;
0078: final boolean debug_store_checkout_exceptions;
0079:
0080: final long pool_start_time = System.currentTimeMillis();
0081:
0082: //MT: not-reassigned, thread-safe, and independent
0083: final BasicResourcePoolFactory factory;
0084: final AsynchronousRunner taskRunner;
0085: final RunnableQueue asyncEventQueue;
0086: final ResourcePoolEventSupport rpes;
0087:
0088: //MT: protected by this' lock
0089: Timer cullAndIdleRefurbishTimer;
0090: TimerTask cullTask;
0091: TimerTask idleRefurbishTask;
0092: HashSet acquireWaiters = new HashSet();
0093: HashSet otherWaiters = new HashSet();
0094:
0095: int pending_acquires;
0096: int pending_removes;
0097:
0098: int target_pool_size;
0099:
0100: /* keys are all valid, managed resources, value is a PunchCard */
0101: HashMap managed = new HashMap();
0102:
0103: /* all valid, managed resources currently available for checkout */
0104: LinkedList unused = new LinkedList();
0105:
0106: /* resources which have been invalidated somehow, but which are */
0107: /* still checked out and in use. */
0108: HashSet excluded = new HashSet();
0109:
0110: Map formerResources = new WeakHashMap();
0111:
0112: Set idleCheckResources = new HashSet();
0113:
0114: boolean force_kill_acquires = false;
0115:
0116: boolean broken = false;
0117:
0118: // long total_acquired = 0;
0119:
0120: long failed_checkins = 0;
0121: long failed_checkouts = 0;
0122: long failed_idle_tests = 0;
0123:
0124: Throwable lastCheckinFailure = null;
0125: Throwable lastCheckoutFailure = null;
0126: Throwable lastIdleTestFailure = null;
0127: Throwable lastResourceTestFailure = null;
0128:
0129: Throwable lastAcquisitionFailiure = null;
0130:
0131: //DEBUG only!
0132: Object exampleResource;
0133:
0134: public long getStartTime() {
0135: return pool_start_time;
0136: }
0137:
0138: public long getUpTime() {
0139: return System.currentTimeMillis() - pool_start_time;
0140: }
0141:
0142: public synchronized long getNumFailedCheckins() {
0143: return failed_checkins;
0144: }
0145:
0146: public synchronized long getNumFailedCheckouts() {
0147: return failed_checkouts;
0148: }
0149:
0150: public synchronized long getNumFailedIdleTests() {
0151: return failed_idle_tests;
0152: }
0153:
0154: public synchronized Throwable getLastCheckinFailure() {
0155: return lastCheckinFailure;
0156: }
0157:
0158: //must be called from a pre-existing sync'ed block
0159: private void setLastCheckinFailure(Throwable t) {
0160: assert (Thread.holdsLock(this ));
0161:
0162: this .lastCheckinFailure = t;
0163: this .lastResourceTestFailure = t;
0164: }
0165:
0166: public synchronized Throwable getLastCheckoutFailure() {
0167: return lastCheckoutFailure;
0168: }
0169:
0170: //must be called from a pre-existing sync'ed block
0171: private void setLastCheckoutFailure(Throwable t) {
0172: assert (Thread.holdsLock(this ));
0173:
0174: this .lastCheckoutFailure = t;
0175: this .lastResourceTestFailure = t;
0176: }
0177:
0178: public synchronized Throwable getLastIdleCheckFailure() {
0179: return lastIdleTestFailure;
0180: }
0181:
0182: //must be called from a pre-existing sync'ed block
0183: private void setLastIdleCheckFailure(Throwable t) {
0184: assert (Thread.holdsLock(this ));
0185:
0186: this .lastIdleTestFailure = t;
0187: this .lastResourceTestFailure = t;
0188: }
0189:
0190: public synchronized Throwable getLastResourceTestFailure() {
0191: return lastResourceTestFailure;
0192: }
0193:
0194: public synchronized Throwable getLastAcquisitionFailure() {
0195: return lastAcquisitionFailiure;
0196: }
0197:
0198: // ought not be called while holding this' lock
0199: private synchronized void setLastAcquisitionFailure(Throwable t) {
0200: this .lastAcquisitionFailiure = t;
0201: }
0202:
0203: public synchronized int getNumCheckoutWaiters() {
0204: return acquireWaiters.size();
0205: }
0206:
0207: private void addToFormerResources(Object resc) {
0208: formerResources.put(resc, null);
0209: }
0210:
0211: private boolean isFormerResource(Object resc) {
0212: return formerResources.keySet().contains(resc);
0213: }
0214:
0215: /**
0216: * @param factory may be null
0217: */
0218: public BasicResourcePool(Manager mgr, int start, int min, int max,
0219: int inc, int num_acq_attempts, int acq_attempt_delay,
0220: long check_idle_resources_delay, long max_resource_age,
0221: long max_idle_time, long excess_max_idle_time,
0222: long destroy_unreturned_resc_time,
0223: long expiration_enforcement_delay,
0224: boolean break_on_acquisition_failure,
0225: boolean debug_store_checkout_exceptions,
0226: AsynchronousRunner taskRunner,
0227: RunnableQueue asyncEventQueue,
0228: Timer cullAndIdleRefurbishTimer,
0229: BasicResourcePoolFactory factory)
0230: throws ResourcePoolException {
0231: try {
0232: this .mgr = mgr;
0233: this .start = start;
0234: this .min = min;
0235: this .max = max;
0236: this .inc = inc;
0237: this .num_acq_attempts = num_acq_attempts;
0238: this .acq_attempt_delay = acq_attempt_delay;
0239: this .check_idle_resources_delay = check_idle_resources_delay;
0240: this .max_resource_age = max_resource_age;
0241: this .max_idle_time = max_idle_time;
0242: this .excess_max_idle_time = excess_max_idle_time;
0243: this .destroy_unreturned_resc_time = destroy_unreturned_resc_time;
0244: //this.expiration_enforcement_delay = expiration_enforcement_delay; -- set up below
0245: this .break_on_acquisition_failure = break_on_acquisition_failure;
0246: this .debug_store_checkout_exceptions = (debug_store_checkout_exceptions && destroy_unreturned_resc_time > 0);
0247: this .taskRunner = taskRunner;
0248: this .asyncEventQueue = asyncEventQueue;
0249: this .cullAndIdleRefurbishTimer = cullAndIdleRefurbishTimer;
0250: this .factory = factory;
0251:
0252: this .pending_acquires = 0;
0253: this .pending_removes = 0;
0254:
0255: this .target_pool_size = Math.max(start, min);
0256:
0257: if (asyncEventQueue != null)
0258: this .rpes = new ResourcePoolEventSupport(this );
0259: else
0260: this .rpes = null;
0261:
0262: //start acquiring our initial resources
0263: ensureStartResources();
0264:
0265: if (mustEnforceExpiration()) {
0266: if (expiration_enforcement_delay <= 0)
0267: this .expiration_enforcement_delay = automaticExpirationEnforcementDelay();
0268: else
0269: this .expiration_enforcement_delay = expiration_enforcement_delay;
0270:
0271: this .cullTask = new CullTask();
0272: //System.err.println("minExpirationTime(): " + minExpirationTime());
0273: //System.err.println("this.expiration_enforcement_delay: " + this.expiration_enforcement_delay);
0274: cullAndIdleRefurbishTimer.schedule(cullTask,
0275: minExpirationTime(),
0276: this .expiration_enforcement_delay);
0277: } else
0278: this .expiration_enforcement_delay = expiration_enforcement_delay;
0279:
0280: //System.err.println("this.check_idle_resources_delay: " + this.check_idle_resources_delay);
0281: if (check_idle_resources_delay > 0) {
0282: this .idleRefurbishTask = new CheckIdleResourcesTask();
0283: cullAndIdleRefurbishTimer.schedule(idleRefurbishTask,
0284: check_idle_resources_delay,
0285: check_idle_resources_delay);
0286: }
0287:
0288: if (logger.isLoggable(MLevel.FINER))
0289: logger.finer(this + " config: [start -> " + this .start
0290: + "; min -> " + this .min + "; max -> "
0291: + this .max + "; inc -> " + this .inc
0292: + "; num_acq_attempts -> "
0293: + this .num_acq_attempts
0294: + "; acq_attempt_delay -> "
0295: + this .acq_attempt_delay
0296: + "; check_idle_resources_delay -> "
0297: + this .check_idle_resources_delay
0298: + "; mox_resource_age -> "
0299: + this .max_resource_age + "; max_idle_time -> "
0300: + this .max_idle_time
0301: + "; excess_max_idle_time -> "
0302: + this .excess_max_idle_time
0303: + "; destroy_unreturned_resc_time -> "
0304: + this .destroy_unreturned_resc_time
0305: + "; expiration_enforcement_delay -> "
0306: + this .expiration_enforcement_delay
0307: + "; break_on_acquisition_failure -> "
0308: + this .break_on_acquisition_failure
0309: + "; debug_store_checkout_exceptions -> "
0310: + this .debug_store_checkout_exceptions + "]");
0311:
0312: } catch (Exception e) {
0313: // if ( logger.isLoggable( MLevel.WARNING) )
0314: // logger.log( MLevel.WARNING, "Could not create resource pool due to Exception!", e );
0315:
0316: throw ResourcePoolUtils.convertThrowable(e);
0317: }
0318: }
0319:
0320: // private boolean timerRequired()
0321: // { return mustEnforceExpiration() || mustTestIdleResources(); }
0322:
0323: // no need to sync
0324: private boolean mustTestIdleResources() {
0325: return check_idle_resources_delay > 0;
0326: }
0327:
0328: // no need to sync
0329: private boolean mustEnforceExpiration() {
0330: return max_resource_age > 0 || max_idle_time > 0
0331: || excess_max_idle_time > 0
0332: || destroy_unreturned_resc_time > 0;
0333: }
0334:
0335: // no need to sync
0336: private long minExpirationTime() {
0337: long out = Long.MAX_VALUE;
0338: if (max_resource_age > 0)
0339: out = Math.min(out, max_resource_age);
0340: if (max_idle_time > 0)
0341: out = Math.min(out, max_idle_time);
0342: if (excess_max_idle_time > 0)
0343: out = Math.min(out, excess_max_idle_time);
0344: if (destroy_unreturned_resc_time > 0)
0345: out = Math.min(out, destroy_unreturned_resc_time);
0346: return out;
0347: }
0348:
0349: private long automaticExpirationEnforcementDelay() {
0350: long out = minExpirationTime();
0351: out /= AUTO_CULL_FREQUENCY_DIVISOR;
0352: out = Math.min(out, AUTO_MAX_CULL_FREQUENCY);
0353: out = Math.max(out, AUTO_MIN_CULL_FREQUENCY);
0354: return out;
0355: }
0356:
0357: public long getEffectiveExpirationEnforcementDelay() {
0358: return expiration_enforcement_delay;
0359: }
0360:
0361: private synchronized boolean isBroken() {
0362: return broken;
0363: }
0364:
0365: // no need to sync
0366: private boolean supportsEvents() {
0367: return asyncEventQueue != null;
0368: }
0369:
0370: public Object checkoutResource() throws ResourcePoolException,
0371: InterruptedException {
0372: try {
0373: return checkoutResource(0);
0374: } catch (TimeoutException e) {
0375: //this should never happen
0376: //e.printStackTrace();
0377: if (logger.isLoggable(MLevel.WARNING))
0378: logger
0379: .log(
0380: MLevel.WARNING,
0381: "Huh??? TimeoutException with no timeout set!!!",
0382: e);
0383:
0384: throw new ResourcePoolException(
0385: "Huh??? TimeoutException with no timeout set!!!", e);
0386: }
0387: }
0388:
0389: // must be called from synchronized method, idempotent
0390: private void _recheckResizePool() {
0391: assert Thread.holdsLock(this );
0392:
0393: if (!broken) {
0394: int msz = managed.size();
0395: //int expected_size = msz + pending_acquires - pending_removes;
0396:
0397: // System.err.print("target: " + target_pool_size);
0398: // System.err.println(" (msz: " + msz + "; pending_acquires: " + pending_acquires + "; pending_removes: " + pending_removes + ')');
0399: //new Exception( "_recheckResizePool() STACK TRACE" ).printStackTrace();
0400:
0401: int shrink_count;
0402: int expand_count;
0403:
0404: if ((shrink_count = msz - pending_removes
0405: - target_pool_size) > 0)
0406: shrinkPool(shrink_count);
0407: else if ((expand_count = target_pool_size
0408: - (msz + pending_acquires)) > 0)
0409: expandPool(expand_count);
0410: }
0411: }
0412:
0413: private synchronized void incrementPendingAcquires() {
0414: ++pending_acquires;
0415:
0416: if (logger.isLoggable(MLevel.FINEST))
0417: logger.finest("incremented pending_acquires: "
0418: + pending_acquires);
0419: //new Exception("ACQUIRE SOURCE STACK TRACE").printStackTrace();
0420: }
0421:
0422: private synchronized void incrementPendingRemoves() {
0423: ++pending_removes;
0424:
0425: if (logger.isLoggable(MLevel.FINEST))
0426: logger.finest("incremented pending_removes: "
0427: + pending_removes);
0428: //new Exception("REMOVE SOURCE STACK TRACE").printStackTrace();
0429: }
0430:
0431: private synchronized void decrementPendingAcquires() {
0432: --pending_acquires;
0433:
0434: if (logger.isLoggable(MLevel.FINEST))
0435: logger.finest("decremented pending_acquires: "
0436: + pending_acquires);
0437: //new Exception("ACQUIRE SOURCE STACK TRACE").printStackTrace();
0438: }
0439:
0440: private synchronized void decrementPendingRemoves() {
0441: --pending_removes;
0442:
0443: if (logger.isLoggable(MLevel.FINEST))
0444: logger.finest("decremented pending_removes: "
0445: + pending_removes);
0446: //new Exception("ACQUIRE SOURCE STACK TRACE").printStackTrace();
0447: }
0448:
0449: // idempotent
0450: private synchronized void recheckResizePool() {
0451: _recheckResizePool();
0452: }
0453:
0454: // must be called from synchronized method
0455: private void expandPool(int count) {
0456: assert Thread.holdsLock(this );
0457:
0458: // XXX: temporary switch -- assuming no problems appear, we'll get rid of AcquireTask
0459: // in favor of ScatteredAcquireTask
0460: if (USE_SCATTERED_ACQUIRE_TASK) {
0461: for (int i = 0; i < count; ++i)
0462: taskRunner.postRunnable(new ScatteredAcquireTask());
0463: } else {
0464: for (int i = 0; i < count; ++i)
0465: taskRunner.postRunnable(new AcquireTask());
0466: }
0467: }
0468:
0469: // must be called from synchronized method
0470: private void shrinkPool(int count) {
0471: assert Thread.holdsLock(this );
0472:
0473: for (int i = 0; i < count; ++i)
0474: taskRunner.postRunnable(new RemoveTask());
0475: }
0476:
0477: /*
0478: * This function recursively calls itself... under nonpathological
0479: * situations, it shouldn't be a problem, but if resources can never
0480: * successfully check out for some reason, we might blow the stack...
0481: *
0482: * by the semantics of wait(), a timeout of zero means forever.
0483: */
0484: public Object checkoutResource(long timeout)
0485: throws TimeoutException, ResourcePoolException,
0486: InterruptedException {
0487: Object resc = prelimCheckoutResource(timeout);
0488:
0489: boolean refurb = attemptRefurbishResourceOnCheckout(resc);
0490:
0491: synchronized (this ) {
0492: if (!refurb) {
0493: removeResource(resc);
0494: ensureMinResources();
0495: resc = null;
0496: } else {
0497: asyncFireResourceCheckedOut(resc, managed.size(),
0498: unused.size(), excluded.size());
0499: if (Debug.DEBUG && Debug.TRACE == Debug.TRACE_MAX)
0500: trace();
0501:
0502: PunchCard card = (PunchCard) managed.get(resc);
0503: if (card == null) //the resource has been removed!
0504: {
0505: if (logger.isLoggable(MLevel.FINE))
0506: logger
0507: .fine("Resource "
0508: + resc
0509: + " was removed from the pool while it was being checked out "
0510: + " or refurbished for checkout.");
0511: resc = null;
0512: } else {
0513: card.checkout_time = System.currentTimeMillis();
0514: if (debug_store_checkout_exceptions)
0515: card.checkoutStackTraceException = new Exception(
0516: "DEBUG ONLY: Overdue resource check-out stack trace.");
0517: }
0518: }
0519: }
0520:
0521: // best to do the recheckout while we don't hold this'
0522: // lock, so we don't refurbish-on-checkout while holding.
0523: if (resc == null)
0524: return checkoutResource(timeout);
0525: else
0526: return resc;
0527: }
0528:
0529: private synchronized Object prelimCheckoutResource(long timeout)
0530: throws TimeoutException, ResourcePoolException,
0531: InterruptedException {
0532: try {
0533: ensureNotBroken();
0534:
0535: int available = unused.size();
0536: if (available == 0) {
0537: int msz = managed.size();
0538:
0539: if (msz < max) {
0540: // to cover all the load, we need the current size, plus those waiting already for acquisition,
0541: // plus the current client
0542: int desired_target = msz + acquireWaiters.size()
0543: + 1;
0544:
0545: if (logger.isLoggable(MLevel.FINER))
0546: logger.log(MLevel.FINER,
0547: "acquire test -- pool size: " + msz
0548: + "; target_pool_size: "
0549: + target_pool_size
0550: + "; desired target? "
0551: + desired_target);
0552:
0553: if (desired_target >= target_pool_size) {
0554: //make sure we don't grab less than inc Connections at a time, if we can help it.
0555: desired_target = Math.max(desired_target,
0556: target_pool_size + inc);
0557:
0558: //make sure our target is within its bounds
0559: target_pool_size = Math.max(Math.min(max,
0560: desired_target), min);
0561:
0562: _recheckResizePool();
0563: }
0564: } else {
0565: if (logger.isLoggable(MLevel.FINER))
0566: logger.log(MLevel.FINER,
0567: "acquire test -- pool is already maxed out. [managed: "
0568: + msz + "; max: " + max + "]");
0569: }
0570:
0571: awaitAvailable(timeout); //throws timeout exception
0572: }
0573:
0574: Object resc = unused.get(0);
0575:
0576: // this is a hack -- but "doing it right" adds a lot of complexity, and collisions between
0577: // an idle check and a checkout should be relatively rare. anyway, it should work just fine.
0578: if (idleCheckResources.contains(resc)) {
0579: if (Debug.DEBUG && logger.isLoggable(MLevel.FINER))
0580: logger
0581: .log(
0582: MLevel.FINER,
0583: "Resource we want to check out is in idleCheck! (waiting until idle-check completes.) ["
0584: + this + "]");
0585:
0586: // we'll move remove() to after the if, so we don't have to add back
0587: // unused.add(0, resc );
0588:
0589: // we'll wait for "something to happen" -- probably an idle check to
0590: // complete -- then we'll try again and hope for the best.
0591: Thread t = Thread.currentThread();
0592: try {
0593: otherWaiters.add(t);
0594: this .wait(timeout);
0595: ensureNotBroken();
0596: } finally {
0597: otherWaiters.remove(t);
0598: }
0599: return prelimCheckoutResource(timeout);
0600: } else if (shouldExpire(resc)) {
0601: removeResource(resc);
0602: ensureMinResources();
0603: return prelimCheckoutResource(timeout);
0604: } else {
0605: unused.remove(0);
0606: return resc;
0607: }
0608: } catch (ResourceClosedException e) // one of our async threads died
0609: {
0610: //System.err.println(this + " -- the pool was found to be closed or broken during an attempt to check out a resource.");
0611: //e.printStackTrace();
0612: if (logger.isLoggable(MLevel.SEVERE))
0613: logger
0614: .log(
0615: MLevel.SEVERE,
0616: this
0617: + " -- the pool was found to be closed or broken during an attempt to check out a resource.",
0618: e);
0619:
0620: this .unexpectedBreak();
0621: throw e;
0622: } catch (InterruptedException e) {
0623: // System.err.println(this + " -- an attempt to checkout a resource was interrupted: some other thread " +
0624: // "must have either interrupted the Thread attempting checkout, or close() was called on the pool.");
0625: // e.printStackTrace();
0626: if (broken) {
0627: if (logger.isLoggable(MLevel.FINER))
0628: logger
0629: .log(
0630: MLevel.FINER,
0631: this
0632: + " -- an attempt to checkout a resource was interrupted, because the pool is now closed. "
0633: + "[Thread: "
0634: + Thread.currentThread()
0635: .getName() + ']', e);
0636: else if (logger.isLoggable(MLevel.INFO))
0637: logger
0638: .log(
0639: MLevel.INFO,
0640: this
0641: + " -- an attempt to checkout a resource was interrupted, because the pool is now closed. "
0642: + "[Thread: "
0643: + Thread.currentThread()
0644: .getName() + ']');
0645: } else {
0646: if (logger.isLoggable(MLevel.WARNING)) {
0647: logger
0648: .log(
0649: MLevel.WARNING,
0650: this
0651: + " -- an attempt to checkout a resource was interrupted, and the pool is still live: some other thread "
0652: + "must have either interrupted the Thread attempting checkout!",
0653: e);
0654: }
0655: }
0656: throw e;
0657: }
0658: }
0659:
0660: public synchronized void checkinResource(Object resc)
0661: throws ResourcePoolException {
0662: try {
0663: //we permit straggling resources to be checked in
0664: //without exception even if we are broken
0665: if (managed.keySet().contains(resc))
0666: doCheckinManaged(resc);
0667: else if (excluded.contains(resc))
0668: doCheckinExcluded(resc);
0669: else if (isFormerResource(resc)) {
0670: if (logger.isLoggable(MLevel.FINER))
0671: logger
0672: .finer("Resource "
0673: + resc
0674: + " checked-in after having been checked-in already, or checked-in after "
0675: + " having being destroyed for being checked-out too long.");
0676: } else
0677: throw new ResourcePoolException("ResourcePool"
0678: + (broken ? " [BROKEN!]" : "")
0679: + ": Tried to check-in a foreign resource!");
0680: if (Debug.DEBUG && Debug.TRACE == Debug.TRACE_MAX)
0681: trace();
0682: } catch (ResourceClosedException e) // one of our async threads died
0683: {
0684: // System.err.println(this +
0685: // " - checkinResource( ... ) -- even broken pools should allow checkins without exception. probable resource pool bug.");
0686: // e.printStackTrace();
0687:
0688: if (logger.isLoggable(MLevel.SEVERE))
0689: logger
0690: .log(
0691: MLevel.SEVERE,
0692: this
0693: + " - checkinResource( ... ) -- even broken pools should allow checkins without exception. probable resource pool bug.",
0694: e);
0695:
0696: this .unexpectedBreak();
0697: throw e;
0698: }
0699: }
0700:
0701: public synchronized void checkinAll() throws ResourcePoolException {
0702: try {
0703: Set checkedOutNotExcluded = new HashSet(managed.keySet());
0704: checkedOutNotExcluded.removeAll(unused);
0705: for (Iterator ii = checkedOutNotExcluded.iterator(); ii
0706: .hasNext();)
0707: doCheckinManaged(ii.next());
0708: for (Iterator ii = excluded.iterator(); ii.hasNext();)
0709: doCheckinExcluded(ii.next());
0710: } catch (ResourceClosedException e) // one of our async threads died
0711: {
0712: // System.err.println(this +
0713: // " - checkinAll() -- even broken pools should allow checkins without exception. probable resource pool bug.");
0714: // e.printStackTrace();
0715:
0716: if (logger.isLoggable(MLevel.SEVERE))
0717: logger
0718: .log(
0719: MLevel.SEVERE,
0720: this
0721: + " - checkinAll() -- even broken pools should allow checkins without exception. probable resource pool bug.",
0722: e);
0723:
0724: this .unexpectedBreak();
0725: throw e;
0726: }
0727: }
0728:
0729: public synchronized int statusInPool(Object resc)
0730: throws ResourcePoolException {
0731: try {
0732: if (unused.contains(resc))
0733: return KNOWN_AND_AVAILABLE;
0734: else if (managed.keySet().contains(resc)
0735: || excluded.contains(resc))
0736: return KNOWN_AND_CHECKED_OUT;
0737: else
0738: return UNKNOWN_OR_PURGED;
0739: } catch (ResourceClosedException e) // one of our async threads died
0740: {
0741: // e.printStackTrace();
0742: if (logger.isLoggable(MLevel.SEVERE))
0743: logger.log(MLevel.SEVERE, "Apparent pool break.", e);
0744: this .unexpectedBreak();
0745: throw e;
0746: }
0747: }
0748:
0749: public synchronized void markBroken(Object resc) {
0750: try {
0751: if (Debug.DEBUG && Debug.TRACE == Debug.TRACE_MAX
0752: && logger.isLoggable(MLevel.FINER))
0753: logger.log(MLevel.FINER, "Resource " + resc
0754: + " marked broken by pool (" + this + ").");
0755:
0756: _markBroken(resc);
0757: ensureMinResources();
0758: } catch (ResourceClosedException e) // one of our async threads died
0759: {
0760: //e.printStackTrace();
0761: if (logger.isLoggable(MLevel.SEVERE))
0762: logger.log(MLevel.SEVERE, "Apparent pool break.", e);
0763: this .unexpectedBreak();
0764: }
0765: }
0766:
0767: //min is immutable, no need to synchronize
0768: public int getMinPoolSize() {
0769: return min;
0770: }
0771:
0772: //max is immutable, no need to synchronize
0773: public int getMaxPoolSize() {
0774: return max;
0775: }
0776:
0777: public synchronized int getPoolSize() throws ResourcePoolException {
0778: return managed.size();
0779: }
0780:
0781: // //i don't think i like the async, no-guarantees approach
0782: // public synchronized void requestResize( int req_sz )
0783: // {
0784: // if (req_sz > max)
0785: // req_sz = max;
0786: // else if (req_sz < min)
0787: // req_sz = min;
0788: // int sz = managed.size();
0789: // if (req_sz > sz)
0790: // postAcquireUntil( req_sz );
0791: // else if (req_sz < sz)
0792: // postRemoveTowards( req_sz );
0793: // }
0794:
0795: public synchronized int getAvailableCount() {
0796: return unused.size();
0797: }
0798:
0799: public synchronized int getExcludedCount() {
0800: return excluded.size();
0801: }
0802:
0803: public synchronized int getAwaitingCheckinCount() {
0804: return managed.size() - unused.size() + excluded.size();
0805: }
0806:
0807: public synchronized void resetPool() {
0808: try {
0809: for (Iterator ii = cloneOfManaged().keySet().iterator(); ii
0810: .hasNext();)
0811: markBrokenNoEnsureMinResources(ii.next());
0812: ensureMinResources();
0813: } catch (ResourceClosedException e) // one of our async threads died
0814: {
0815: //e.printStackTrace();
0816: if (logger.isLoggable(MLevel.SEVERE))
0817: logger.log(MLevel.SEVERE, "Apparent pool break.", e);
0818: this .unexpectedBreak();
0819: }
0820: }
0821:
0822: public synchronized void close() throws ResourcePoolException {
0823: //we permit closes when we are already broken, so
0824: //that resources that were checked out when the break
0825: //occured can still be cleaned up
0826: close(true);
0827: }
0828:
0829: public void finalize() throws Throwable {
0830: //obviously, clients mustn't rely on finalize,
0831: //but must close pools ASAP after use.
0832: //System.err.println("finalizing..." + this);
0833:
0834: if (!broken)
0835: this .close();
0836: }
0837:
0838: //no need to sync
0839: public void addResourcePoolListener(ResourcePoolListener rpl) {
0840: if (!supportsEvents())
0841: throw new RuntimeException(
0842: this
0843: + " does not support ResourcePoolEvents. "
0844: + "Probably it was constructed by a BasicResourceFactory configured not to support such events.");
0845: else
0846: rpes.addResourcePoolListener(rpl);
0847: }
0848:
0849: //no need to sync
0850: public void removeResourcePoolListener(ResourcePoolListener rpl) {
0851: if (!supportsEvents())
0852: throw new RuntimeException(
0853: this
0854: + " does not support ResourcePoolEvents. "
0855: + "Probably it was constructed by a BasicResourceFactory configured not to support such events.");
0856: else
0857: rpes.removeResourcePoolListener(rpl);
0858: }
0859:
0860: private synchronized boolean isForceKillAcquiresPending() {
0861: return force_kill_acquires;
0862: }
0863:
0864: // this is designed as a response to a determination that our resource source is down.
0865: // rather than declaring ourselves broken in this case (as we did previously), we
0866: // kill all pending acquisition attempts, but retry on new acqusition requests.
0867: private synchronized void forceKillAcquires()
0868: throws InterruptedException {
0869: Thread t = Thread.currentThread();
0870:
0871: try {
0872: force_kill_acquires = true;
0873: this .notifyAll(); //wake up any threads waiting on an acquire, and force them all to die.
0874: while (acquireWaiters.size() > 0) //we want to let all the waiting acquires die before we unset force_kill_acquires
0875: {
0876: otherWaiters.add(t);
0877: this .wait();
0878: }
0879: force_kill_acquires = false;
0880: } finally {
0881: otherWaiters.remove(t);
0882: }
0883: }
0884:
0885: //same as close(), but we do not destroy checked out
0886: //resources
0887: private synchronized void unexpectedBreak() {
0888: if (logger.isLoggable(MLevel.SEVERE))
0889: logger.log(MLevel.SEVERE, this
0890: + " -- Unexpectedly broken!!!",
0891: new ResourcePoolException(
0892: "Unexpected Break Stack Trace!"));
0893: close(false);
0894: }
0895:
0896: // no need to sync
0897: private boolean canFireEvents() {
0898: return (asyncEventQueue != null && !isBroken());
0899: }
0900:
0901: // no need to sync
0902: private void asyncFireResourceAcquired(final Object resc,
0903: final int pool_size, final int available_size,
0904: final int removed_but_unreturned_size) {
0905: if (canFireEvents()) {
0906: Runnable r = new Runnable() {
0907: public void run() {
0908: rpes
0909: .fireResourceAcquired(resc, pool_size,
0910: available_size,
0911: removed_but_unreturned_size);
0912: }
0913: };
0914: asyncEventQueue.postRunnable(r);
0915: }
0916: }
0917:
0918: // no need to sync
0919: private void asyncFireResourceCheckedIn(final Object resc,
0920: final int pool_size, final int available_size,
0921: final int removed_but_unreturned_size) {
0922: if (canFireEvents()) {
0923: Runnable r = new Runnable() {
0924: public void run() {
0925: rpes
0926: .fireResourceCheckedIn(resc, pool_size,
0927: available_size,
0928: removed_but_unreturned_size);
0929: }
0930: };
0931: asyncEventQueue.postRunnable(r);
0932: }
0933: }
0934:
0935: // no need to sync
0936: private void asyncFireResourceCheckedOut(final Object resc,
0937: final int pool_size, final int available_size,
0938: final int removed_but_unreturned_size) {
0939: if (canFireEvents()) {
0940: Runnable r = new Runnable() {
0941: public void run() {
0942: rpes
0943: .fireResourceCheckedOut(resc, pool_size,
0944: available_size,
0945: removed_but_unreturned_size);
0946: }
0947: };
0948: asyncEventQueue.postRunnable(r);
0949: }
0950: }
0951:
0952: // no need to sync
0953: private void asyncFireResourceRemoved(final Object resc,
0954: final boolean checked_out_resource, final int pool_size,
0955: final int available_size,
0956: final int removed_but_unreturned_size) {
0957: if (canFireEvents()) {
0958: //System.err.println("ASYNC RSRC REMOVED");
0959: //new Exception().printStackTrace();
0960: Runnable r = new Runnable() {
0961: public void run() {
0962: rpes
0963: .fireResourceRemoved(resc,
0964: checked_out_resource, pool_size,
0965: available_size,
0966: removed_but_unreturned_size);
0967: }
0968: };
0969: asyncEventQueue.postRunnable(r);
0970: }
0971: }
0972:
0973: // needn't be called from a sync'ed method
0974: private void destroyResource(final Object resc) {
0975: destroyResource(resc, false);
0976: }
0977:
0978: // needn't be called from a sync'ed method
0979: private void destroyResource(final Object resc, boolean synchronous) {
0980: class DestroyResourceTask implements Runnable {
0981: public void run() {
0982: try {
0983: if (Debug.DEBUG && Debug.TRACE == Debug.TRACE_MAX
0984: && logger.isLoggable(MLevel.FINER))
0985: logger.log(MLevel.FINER,
0986: "Preparing to destroy resource: "
0987: + resc);
0988:
0989: mgr.destroyResource(resc);
0990:
0991: if (Debug.DEBUG && Debug.TRACE == Debug.TRACE_MAX
0992: && logger.isLoggable(MLevel.FINER))
0993: logger.log(MLevel.FINER,
0994: "Successfully destroyed resource: "
0995: + resc);
0996: } catch (Exception e) {
0997: if (logger.isLoggable(MLevel.WARNING))
0998: logger.log(MLevel.WARNING,
0999: "Failed to destroy resource: " + resc,
1000: e);
1001:
1002: // System.err.println("Failed to destroy resource: " + resc);
1003: // e.printStackTrace();
1004: }
1005: }
1006: }
1007:
1008: Runnable r = new DestroyResourceTask();
1009: if (synchronous || broken) //if we're broken, our taskRunner may be dead, so we destroy synchronously
1010: {
1011: if (logger.isLoggable(MLevel.FINEST)
1012: && !broken
1013: && Boolean.TRUE.equals(ThreadUtils
1014: .reflectiveHoldsLock(this )))
1015: logger
1016: .log(
1017: MLevel.FINEST,
1018: this
1019: + ": Destroyiong a resource on an active pool, synchronousy while holding pool's lock! "
1020: + "(not a bug, but a potential bottleneck... is there a good reason for this?)",
1021: new Exception("DEBUG STACK TRACE"));
1022:
1023: r.run();
1024: } else {
1025: try {
1026: taskRunner.postRunnable(r);
1027: } catch (Exception e) {
1028: if (logger.isLoggable(MLevel.FINER))
1029: logger
1030: .log(
1031: MLevel.FINER,
1032: "AsynchronousRunner refused to accept task to destroy resource. "
1033: + "It is probably shared, and has probably been closed underneath us. "
1034: + "Reverting to synchronous destruction. This is not usually a problem.",
1035: e);
1036: destroyResource(resc, true);
1037: }
1038: }
1039: }
1040:
1041: //this method SHOULD NOT be invoked from a synchronized
1042: //block!!!!
1043: private void doAcquire() throws Exception {
1044: assert !Thread.holdsLock(this );
1045:
1046: Object resc = mgr.acquireResource(); //note we acquire the resource while we DO NOT hold the pool's lock!
1047:
1048: boolean destroy = false;
1049: int msz;
1050:
1051: synchronized (this ) //assimilate resc if we do need it
1052: {
1053: // ++total_acquired;
1054:
1055: // if (logger.isLoggable( MLevel.FINER))
1056: // logger.log(MLevel.FINER, "acquired new resource, total_acquired: " + total_acquired);
1057:
1058: msz = managed.size();
1059: if (msz < target_pool_size)
1060: assimilateResource(resc);
1061: else
1062: destroy = true;
1063: }
1064:
1065: if (destroy) {
1066: mgr.destroyResource(resc); //destroy resc if superfluous, without holding the pool's lock
1067: if (logger.isLoggable(MLevel.FINER))
1068: logger.log(MLevel.FINER,
1069: "destroying overacquired resource: " + resc);
1070: }
1071:
1072: }
1073:
1074: public synchronized void setPoolSize(int sz)
1075: throws ResourcePoolException {
1076: try {
1077: setTargetPoolSize(sz);
1078: while (managed.size() != sz)
1079: this .wait();
1080: } catch (Exception e) {
1081: String msg = "An exception occurred while trying to set the pool size!";
1082: if (logger.isLoggable(MLevel.FINER))
1083: logger.log(MLevel.FINER, msg, e);
1084: throw ResourcePoolUtils.convertThrowable(msg, e);
1085: }
1086: }
1087:
1088: public synchronized void setTargetPoolSize(int sz) {
1089: if (sz > max) {
1090: throw new IllegalArgumentException("Requested size [" + sz
1091: + "] is greater than max [" + max + "].");
1092: } else if (sz < min) {
1093: throw new IllegalArgumentException("Requested size [" + sz
1094: + "] is less than min [" + min + "].");
1095: }
1096:
1097: this .target_pool_size = sz;
1098:
1099: _recheckResizePool();
1100: }
1101:
1102: // private void acquireUntil(int num) throws Exception
1103: // {
1104: // int msz = managed.size();
1105: // for (int i = msz; i < num; ++i)
1106: // assimilateResource();
1107: // }
1108:
1109: //the following methods should only be invoked from
1110: //sync'ed methods / blocks...
1111:
1112: // private Object useUnusedButNotInIdleCheck()
1113: // {
1114: // for (Iterator ii = unused.iterator(); ii.hasNext(); )
1115: // {
1116: // Object maybeOut = ii.next();
1117: // if (! idleCheckResources.contains( maybeOut ))
1118: // {
1119: // ii.remove();
1120: // return maybeOut;
1121: // }
1122: // }
1123: // throw new RuntimeException("Internal Error -- the pool determined that it did have a resource available for checkout, but was unable to find one.");
1124: // }
1125:
1126: // private int actuallyAvailable()
1127: // { return unused.size() - idleCheckResources.size(); }
1128:
1129: // must own this' lock
1130: private void markBrokenNoEnsureMinResources(Object resc) {
1131: assert Thread.holdsLock(this );
1132:
1133: try {
1134: _markBroken(resc);
1135: } catch (ResourceClosedException e) // one of our async threads died
1136: {
1137: //e.printStackTrace();
1138: if (logger.isLoggable(MLevel.SEVERE))
1139: logger.log(MLevel.SEVERE, "Apparent pool break.", e);
1140: this .unexpectedBreak();
1141: }
1142: }
1143:
1144: // must own this' lock
1145: private void _markBroken(Object resc) {
1146: assert Thread.holdsLock(this );
1147:
1148: if (unused.contains(resc))
1149: removeResource(resc);
1150: else
1151: excludeResource(resc);
1152: }
1153:
1154: //DEBUG
1155: //Exception firstClose = null;
1156:
1157: public synchronized void close(boolean close_checked_out_resources) {
1158: if (!broken) //ignore repeated calls to close
1159: {
1160: //DEBUG
1161: //firstClose = new Exception("First close() -- debug stack trace [CRAIG]");
1162: //firstClose.printStackTrace();
1163:
1164: this .broken = true;
1165: final Collection cleanupResources = (close_checked_out_resources ? (Collection) cloneOfManaged()
1166: .keySet()
1167: : (Collection) cloneOfUnused());
1168: if (cullTask != null)
1169: cullTask.cancel();
1170: if (idleRefurbishTask != null)
1171: idleRefurbishTask.cancel();
1172:
1173: // we destroy resources asynchronously, but with a dedicated one-off Thread, rather than
1174: // our asynchronous runner, because our asynchrous runner may be shutting down. The
1175: // destruction is asynchrounous because destroying a resource might require the resource's
1176: // lock, and we already have the pool's lock. But client threads may well have the resource's
1177: // lock while they try to check-in to the pool. The async destruction of resources avoids
1178: // the possibility of deadlock.
1179:
1180: managed.keySet().removeAll(cleanupResources);
1181: unused.removeAll(cleanupResources);
1182: Thread resourceDestroyer = new Thread(
1183: "Resource Destroyer in BasicResourcePool.close()") {
1184: public void run() {
1185: for (Iterator ii = cleanupResources.iterator(); ii
1186: .hasNext();) {
1187: try {
1188: Object resc = ii.next();
1189: //System.err.println("Destroying resource... " + resc);
1190:
1191: destroyResource(resc, true);
1192: } catch (Exception e) {
1193: if (Debug.DEBUG) {
1194: //e.printStackTrace();
1195: if (logger.isLoggable(MLevel.FINE))
1196: logger
1197: .log(
1198: MLevel.FINE,
1199: "BasicResourcePool -- A resource couldn't be cleaned up on close()",
1200: e);
1201: }
1202: }
1203: }
1204: }
1205: };
1206: resourceDestroyer.start();
1207:
1208: for (Iterator ii = acquireWaiters.iterator(); ii.hasNext();)
1209: ((Thread) ii.next()).interrupt();
1210: for (Iterator ii = otherWaiters.iterator(); ii.hasNext();)
1211: ((Thread) ii.next()).interrupt();
1212: if (factory != null)
1213: factory.markBroken(this );
1214:
1215: // System.err.println(this + " closed.");
1216: } else {
1217: if (logger.isLoggable(MLevel.WARNING))
1218: logger.warning(this
1219: + " -- close() called multiple times.");
1220: //System.err.println(this + " -- close() called multiple times.");
1221:
1222: //DEBUG
1223: //firstClose.printStackTrace();
1224: //new Exception("Repeat close() [CRAIG]").printStackTrace();
1225: }
1226: }
1227:
1228: private void doCheckinManaged(final Object resc)
1229: throws ResourcePoolException {
1230: assert Thread.holdsLock(this );
1231:
1232: if (unused.contains(resc)) {
1233: if (Debug.DEBUG)
1234: throw new ResourcePoolException(
1235: "Tried to check-in an already checked-in resource: "
1236: + resc);
1237: } else if (broken)
1238: removeResource(resc, true); //synchronous... if we're broken, async tasks might not work
1239: else {
1240: class RefurbishCheckinResourceTask implements Runnable {
1241: public void run() {
1242: boolean resc_okay = attemptRefurbishResourceOnCheckin(resc);
1243: synchronized (BasicResourcePool.this ) {
1244: PunchCard card = (PunchCard) managed.get(resc);
1245:
1246: if (resc_okay && card != null) //we have to check that the resource is still in the pool
1247: {
1248: unused.add(0, resc);
1249:
1250: card.last_checkin_time = System
1251: .currentTimeMillis();
1252: card.checkout_time = -1;
1253: } else {
1254: if (card != null)
1255: card.checkout_time = -1; //so we don't see this as still checked out and log an overdue cxn in removeResource()
1256:
1257: removeResource(resc);
1258: ensureMinResources();
1259:
1260: if (card == null
1261: && logger.isLoggable(MLevel.FINE))
1262: logger
1263: .fine("Resource "
1264: + resc
1265: + " was removed from the pool during its refurbishment for checkin.");
1266: }
1267:
1268: asyncFireResourceCheckedIn(resc,
1269: managed.size(), unused.size(), excluded
1270: .size());
1271: BasicResourcePool.this .notifyAll();
1272: }
1273: }
1274: }
1275:
1276: Runnable doMe = new RefurbishCheckinResourceTask();
1277: taskRunner.postRunnable(doMe);
1278: }
1279: }
1280:
1281: private void doCheckinExcluded(Object resc) {
1282: assert Thread.holdsLock(this );
1283:
1284: excluded.remove(resc);
1285: destroyResource(resc);
1286: }
1287:
1288: /*
1289: * by the semantics of wait(), a timeout of zero means forever.
1290: */
1291: private void awaitAvailable(long timeout)
1292: throws InterruptedException, TimeoutException,
1293: ResourcePoolException {
1294: assert Thread.holdsLock(this );
1295:
1296: if (force_kill_acquires)
1297: throw new ResourcePoolException(
1298: "A ResourcePool cannot acquire a new resource -- the factory or source appears to be down.");
1299:
1300: Thread t = Thread.currentThread();
1301: try {
1302: acquireWaiters.add(t);
1303:
1304: int avail;
1305: long start = (timeout > 0 ? System.currentTimeMillis() : -1);
1306: if (Debug.DEBUG && Debug.TRACE == Debug.TRACE_MAX) {
1307: if (logger.isLoggable(MLevel.FINE))
1308: logger
1309: .fine("awaitAvailable(): "
1310: + (exampleResource != null ? exampleResource
1311: : "[unknown]"));
1312: trace();
1313: }
1314: while ((avail = unused.size()) == 0) {
1315: // the if case below can only occur when 1) a user attempts a
1316: // checkout which would provoke an acquire; 2) this
1317: // increments the pending acquires, so we go to the
1318: // wait below without provoking postAcquireMore(); 3)
1319: // the resources are acquired; 4) external management
1320: // of the pool (via for instance unpoolResource()
1321: // depletes the newly acquired resources before we
1322: // regain this' monitor; 5) we fall into wait() with
1323: // no acquires being scheduled, and perhaps a managed.size()
1324: // of zero, leading to deadlock. This could only occur in
1325: // fairly pathological situations where the pool is being
1326: // externally forced to a very low (even zero) size, but
1327: // since I've seen it, I've fixed it.
1328: if (pending_acquires == 0 && managed.size() < max)
1329: _recheckResizePool();
1330:
1331: this .wait(timeout);
1332: if (timeout > 0
1333: && System.currentTimeMillis() - start > timeout)
1334: throw new TimeoutException(
1335: "A client timed out while waiting to acquire a resource from "
1336: + this
1337: + " -- timeout at awaitAvailable()");
1338: if (force_kill_acquires)
1339: throw new CannotAcquireResourceException(
1340: "A ResourcePool could not acquire a resource from its primary factory or source.");
1341: ensureNotBroken();
1342: }
1343: } finally {
1344: acquireWaiters.remove(t);
1345: if (acquireWaiters.size() == 0)
1346: this .notifyAll();
1347: }
1348: }
1349:
1350: private void assimilateResource(Object resc) throws Exception {
1351: assert Thread.holdsLock(this );
1352:
1353: managed.put(resc, new PunchCard());
1354: unused.add(0, resc);
1355: //System.err.println("assimilate resource... unused: " + unused.size());
1356: asyncFireResourceAcquired(resc, managed.size(), unused.size(),
1357: excluded.size());
1358: this .notifyAll();
1359: if (Debug.DEBUG && Debug.TRACE == Debug.TRACE_MAX)
1360: trace();
1361: if (Debug.DEBUG && exampleResource == null)
1362: exampleResource = resc;
1363: }
1364:
1365: // should NOT be called from synchronized method
1366: private void synchronousRemoveArbitraryResource() {
1367: assert !Thread.holdsLock(this );
1368:
1369: Object removeMe = null;
1370:
1371: synchronized (this ) {
1372: if (unused.size() > 0) {
1373: removeMe = unused.get(0);
1374: managed.remove(removeMe);
1375: unused.remove(removeMe);
1376: } else {
1377: Set checkedOut = cloneOfManaged().keySet();
1378: if (checkedOut.isEmpty()) {
1379: unexpectedBreak();
1380: logger
1381: .severe("A pool from which a resource is requested to be removed appears to have no managed resources?!");
1382: } else
1383: excludeResource(checkedOut.iterator().next());
1384: }
1385: }
1386:
1387: if (removeMe != null)
1388: destroyResource(removeMe, true);
1389: }
1390:
1391: private void removeResource(Object resc) {
1392: removeResource(resc, false);
1393: }
1394:
1395: private void removeResource(Object resc, boolean synchronous) {
1396: assert Thread.holdsLock(this );
1397:
1398: PunchCard pc = (PunchCard) managed.remove(resc);
1399:
1400: if (pc != null) {
1401: if (pc.checkout_time > 0 && !broken) //this is a checked-out resource in an active pool, must be overdue if we are removing it
1402: {
1403: if (logger.isLoggable(MLevel.INFO)) {
1404: logger
1405: .info("A checked-out resource is overdue, and will be destroyed: "
1406: + resc);
1407: if (pc.checkoutStackTraceException != null) {
1408: logger
1409: .log(
1410: MLevel.INFO,
1411: "Logging the stack trace by which the overdue resource was checked-out.",
1412: pc.checkoutStackTraceException);
1413: }
1414: }
1415: }
1416: } else if (logger.isLoggable(MLevel.FINE))
1417: logger
1418: .fine("Resource "
1419: + resc
1420: + " was removed twice. (Lotsa reasons a resource can be removed, sometimes simultaneously. It's okay)");
1421:
1422: unused.remove(resc);
1423: destroyResource(resc, synchronous);
1424: addToFormerResources(resc);
1425: asyncFireResourceRemoved(resc, false, managed.size(), unused
1426: .size(), excluded.size());
1427:
1428: if (Debug.DEBUG && Debug.TRACE == Debug.TRACE_MAX)
1429: trace();
1430: //System.err.println("RESOURCE REMOVED!");
1431: }
1432:
1433: //when we want to conceptually remove a checked
1434: //out resource from the pool
1435: private void excludeResource(Object resc) {
1436: assert Thread.holdsLock(this );
1437:
1438: managed.remove(resc);
1439: excluded.add(resc);
1440: if (Debug.DEBUG && unused.contains(resc))
1441: throw new InternalError(
1442: "We should only \"exclude\" checked-out resources!");
1443: asyncFireResourceRemoved(resc, true, managed.size(), unused
1444: .size(), excluded.size());
1445: }
1446:
1447: private void removeTowards(int new_sz) {
1448: assert Thread.holdsLock(this );
1449:
1450: int num_to_remove = managed.size() - new_sz;
1451: int count = 0;
1452: for (Iterator ii = cloneOfUnused().iterator(); ii.hasNext()
1453: && count < num_to_remove; ++count) {
1454: Object resc = ii.next();
1455: removeResource(resc);
1456: }
1457: }
1458:
1459: private void cullExpired() {
1460: assert Thread.holdsLock(this );
1461:
1462: if (logger.isLoggable(MLevel.FINER))
1463: logger.log(MLevel.FINER,
1464: "BEGIN check for expired resources. [" + this
1465: + "]");
1466:
1467: // if we do not time-out checkedout resources, we only need to test unused resources
1468: Collection checkMe = (destroy_unreturned_resc_time > 0 ? (Collection) cloneOfManaged()
1469: .keySet()
1470: : cloneOfUnused());
1471:
1472: for (Iterator ii = checkMe.iterator(); ii.hasNext();) {
1473: Object resc = ii.next();
1474: if (shouldExpire(resc)) {
1475: if (logger.isLoggable(MLevel.FINER))
1476: logger.log(MLevel.FINER,
1477: "Removing expired resource: " + resc + " ["
1478: + this + "]");
1479:
1480: target_pool_size = Math.max(min, target_pool_size - 1); //expiring a resource resources the target size to match
1481:
1482: removeResource(resc);
1483:
1484: if (Debug.DEBUG && Debug.TRACE == Debug.TRACE_MAX)
1485: trace();
1486: }
1487: }
1488: if (logger.isLoggable(MLevel.FINER))
1489: logger.log(MLevel.FINER,
1490: "FINISHED check for expired resources. [" + this
1491: + "]");
1492: ensureMinResources();
1493: }
1494:
1495: private void checkIdleResources() {
1496: assert Thread.holdsLock(this );
1497:
1498: List u = cloneOfUnused();
1499: for (Iterator ii = u.iterator(); ii.hasNext();) {
1500: Object resc = ii.next();
1501: if (idleCheckResources.add(resc))
1502: taskRunner.postRunnable(new AsyncTestIdleResourceTask(
1503: resc));
1504: }
1505:
1506: if (Debug.DEBUG && Debug.TRACE == Debug.TRACE_MAX)
1507: trace();
1508: }
1509:
1510: private boolean shouldExpire(Object resc) {
1511: assert Thread.holdsLock(this );
1512:
1513: boolean expired = false;
1514:
1515: PunchCard pc = (PunchCard) managed.get(resc);
1516:
1517: // the resource has already been removed
1518: // we return true, because removing twice does no harm
1519: // (false should work as well, but true seems safer.
1520: // we certainly don't want to do anything else with
1521: // this resource.)
1522: if (pc == null) {
1523: if (logger.isLoggable(MLevel.FINE))
1524: logger
1525: .fine("Resource "
1526: + resc
1527: + " was being tested for expiration, but has already been removed from the pool.");
1528: return true;
1529: }
1530:
1531: long now = System.currentTimeMillis();
1532:
1533: if (pc.checkout_time < 0) //resource is not checked out
1534: {
1535: long idle_age = now - pc.last_checkin_time;
1536: if (excess_max_idle_time > 0) {
1537: int msz = managed.size();
1538: expired = (msz > min && idle_age > excess_max_idle_time);
1539: if (expired && logger.isLoggable(MLevel.FINER))
1540: logger.log(MLevel.FINER,
1541: "EXPIRED excess idle resource: " + resc
1542: + " ---> idle_time: " + idle_age
1543: + "; excess_max_idle_time: "
1544: + excess_max_idle_time
1545: + "; pool_size: " + msz
1546: + "; min_pool_size: " + min + " ["
1547: + this + "]");
1548: }
1549: if (!expired && max_idle_time > 0) {
1550: expired = idle_age > max_idle_time;
1551: if (expired && logger.isLoggable(MLevel.FINER))
1552: logger.log(MLevel.FINER, "EXPIRED idle resource: "
1553: + resc + " ---> idle_time: " + idle_age
1554: + "; max_idle_time: " + max_idle_time
1555: + " [" + this + "]");
1556: }
1557: if (!expired && max_resource_age > 0) {
1558: long abs_age = now - pc.acquisition_time;
1559: expired = (abs_age > max_resource_age);
1560:
1561: if (expired && logger.isLoggable(MLevel.FINER))
1562: logger.log(MLevel.FINER, "EXPIRED old resource: "
1563: + resc + " ---> absolute_age: " + abs_age
1564: + "; max_absolute_age: " + max_resource_age
1565: + " [" + this + "]");
1566: }
1567: } else //resource is checked out
1568: {
1569: long checkout_age = now - pc.checkout_time;
1570: expired = checkout_age > destroy_unreturned_resc_time;
1571: }
1572:
1573: return expired;
1574: }
1575:
1576: // private boolean resourcesInIdleCheck()
1577: // { return idleCheckresources.size() > 0; }
1578:
1579: // private int countAvailable()
1580: // { return unused.size() - idleCheckResources.size(); }
1581:
1582: // we needn't hold this' lock
1583: private void ensureStartResources() {
1584: recheckResizePool();
1585: }
1586:
1587: // we needn't hold this' lock
1588: private void ensureMinResources() {
1589: recheckResizePool();
1590: }
1591:
1592: private boolean attemptRefurbishResourceOnCheckout(Object resc) {
1593: assert !Thread.holdsLock(this );
1594:
1595: try {
1596: mgr.refurbishResourceOnCheckout(resc);
1597: return true;
1598: } catch (Exception e) {
1599: //uh oh... bad resource...
1600: if (Debug.DEBUG) {
1601: //e.printStackTrace();
1602: if (logger.isLoggable(MLevel.FINE))
1603: logger.log(MLevel.FINE,
1604: "A resource could not be refurbished for checkout. ["
1605: + resc + ']', e);
1606: }
1607: synchronized (this ) {
1608: ++failed_checkouts;
1609: setLastCheckoutFailure(e);
1610: }
1611: return false;
1612: }
1613: }
1614:
1615: private boolean attemptRefurbishResourceOnCheckin(Object resc) {
1616: assert !Thread.holdsLock(this );
1617:
1618: try {
1619: mgr.refurbishResourceOnCheckin(resc);
1620: return true;
1621: } catch (Exception e) {
1622: //uh oh... bad resource...
1623: if (Debug.DEBUG) {
1624: //e.printStackTrace();
1625: if (logger.isLoggable(MLevel.FINE))
1626: logger.log(MLevel.FINE,
1627: "A resource could not be refurbished on checkin. ["
1628: + resc + ']', e);
1629: }
1630: synchronized (this ) {
1631: ++failed_checkins;
1632: setLastCheckinFailure(e);
1633: }
1634: return false;
1635: }
1636: }
1637:
1638: private void ensureNotBroken() throws ResourcePoolException {
1639: assert Thread.holdsLock(this );
1640:
1641: if (broken)
1642: throw new ResourcePoolException(
1643: "Attempted to use a closed or broken resource pool");
1644: }
1645:
1646: private void trace() {
1647: assert Thread.holdsLock(this );
1648:
1649: if (logger.isLoggable(MLevel.FINEST)) {
1650: String exampleResStr = (exampleResource == null ? ""
1651: : " (e.g. " + exampleResource + ")");
1652: logger.finest("trace " + this + " [managed: "
1653: + managed.size() + ", " + "unused: "
1654: + unused.size() + ", excluded: " + excluded.size()
1655: + ']' + exampleResStr);
1656: }
1657: }
1658:
1659: private final HashMap cloneOfManaged() {
1660: assert Thread.holdsLock(this );
1661:
1662: return (HashMap) managed.clone();
1663: }
1664:
1665: private final LinkedList cloneOfUnused() {
1666: assert Thread.holdsLock(this );
1667:
1668: return (LinkedList) unused.clone();
1669: }
1670:
1671: private final HashSet cloneOfExcluded() {
1672: assert Thread.holdsLock(this );
1673:
1674: return (HashSet) excluded.clone();
1675: }
1676:
1677: class ScatteredAcquireTask implements Runnable {
1678: int attempts_remaining;
1679:
1680: ScatteredAcquireTask() {
1681: this ((num_acq_attempts >= 0 ? num_acq_attempts : -1), true);
1682: }
1683:
1684: private ScatteredAcquireTask(int attempts_remaining,
1685: boolean first_attempt) {
1686: this .attempts_remaining = attempts_remaining;
1687: if (first_attempt) {
1688: incrementPendingAcquires();
1689: if (logger.isLoggable(MLevel.FINEST))
1690: logger
1691: .finest("Starting acquisition series. Incremented pending_acquires ["
1692: + pending_acquires
1693: + "], "
1694: + " attempts_remaining: "
1695: + attempts_remaining);
1696: } else {
1697: if (logger.isLoggable(MLevel.FINEST))
1698: logger
1699: .finest("Continuing acquisition series. pending_acquires ["
1700: + pending_acquires
1701: + "], "
1702: + " attempts_remaining: "
1703: + attempts_remaining);
1704: }
1705: }
1706:
1707: public void run() {
1708: try {
1709: boolean fkap = isForceKillAcquiresPending();
1710: if (!fkap) {
1711: //we don't want this call to be sync'd
1712: //on the pool, so that resource acquisition
1713: //does not interfere with other pool clients.
1714: BasicResourcePool.this .doAcquire();
1715: }
1716: decrementPendingAcquires();
1717: if (logger.isLoggable(MLevel.FINEST))
1718: logger
1719: .finest("Acquisition series terminated "
1720: + (fkap ? "because force-kill-acquires is pending"
1721: : "successfully")
1722: + ". Decremented pending_acquires ["
1723: + pending_acquires + "], "
1724: + " attempts_remaining: "
1725: + attempts_remaining);
1726: } catch (Exception e) {
1727: BasicResourcePool.this .setLastAcquisitionFailure(e);
1728:
1729: if (attempts_remaining == 0) //last try in a round...
1730: {
1731: decrementPendingAcquires();
1732: if (logger.isLoggable(MLevel.WARNING)) {
1733: logger
1734: .log(
1735: MLevel.WARNING,
1736: this
1737: + " -- Acquisition Attempt Failed!!! Clearing pending acquires. "
1738: + "While trying to acquire a needed new resource, we failed "
1739: + "to succeed more than the maximum number of allowed "
1740: + "acquisition attempts ("
1741: + num_acq_attempts
1742: + "). "
1743: + "Last acquisition attempt exception: ",
1744: e);
1745: }
1746: if (break_on_acquisition_failure) {
1747: //System.err.println("\tTHE RESOURCE POOL IS PERMANENTLY BROKEN!");
1748: if (logger.isLoggable(MLevel.SEVERE))
1749: logger
1750: .severe("A RESOURCE POOL IS PERMANENTLY BROKEN! ["
1751: + this
1752: + "] "
1753: + "(because a series of "
1754: + num_acq_attempts
1755: + " acquisition attempts "
1756: + "failed.)");
1757: unexpectedBreak();
1758: } else {
1759: try {
1760: forceKillAcquires();
1761: } catch (InterruptedException ie) {
1762: if (logger.isLoggable(MLevel.WARNING))
1763: logger
1764: .log(
1765: MLevel.WARNING,
1766: "Failed to force-kill pending acquisition attempts after acquisition failue, "
1767: + " due to an InterruptedException!",
1768: ie);
1769:
1770: // we might still have clients waiting, so we should try
1771: // to ensure there are sufficient connections to serve
1772: recheckResizePool();
1773: }
1774: }
1775: if (logger.isLoggable(MLevel.FINEST))
1776: logger
1777: .finest("Acquisition series terminated unsuccessfully. Decremented pending_acquires ["
1778: + pending_acquires
1779: + "], "
1780: + " attempts_remaining: "
1781: + attempts_remaining);
1782: } else {
1783: // if attempts_remaining < 0, we try to acquire forever, so the end-of-batch
1784: // log message below will never be triggered if there is a persistent problem
1785: // so in this case, it's better flag a higher-than-debug-level message for
1786: // each failed attempt. (Thanks to Eric Crahen for calling attention to this
1787: // issue.)
1788: MLevel logLevel = (attempts_remaining > 0 ? MLevel.FINE
1789: : MLevel.INFO);
1790: if (logger.isLoggable(logLevel))
1791: logger
1792: .log(
1793: logLevel,
1794: "An exception occurred while acquiring a poolable resource. Will retry.",
1795: e);
1796:
1797: TimerTask doNextAcquire = new TimerTask() {
1798: public void run() {
1799: taskRunner
1800: .postRunnable(new ScatteredAcquireTask(
1801: attempts_remaining - 1,
1802: false));
1803: }
1804: };
1805: cullAndIdleRefurbishTimer.schedule(doNextAcquire,
1806: acq_attempt_delay);
1807: }
1808: }
1809: }
1810:
1811: }
1812:
1813: /*
1814: * task we post to separate thread to acquire
1815: * pooled resources
1816: */
1817: class AcquireTask implements Runnable {
1818: boolean success = false;
1819:
1820: public AcquireTask() {
1821: incrementPendingAcquires();
1822: }
1823:
1824: public void run() {
1825: try {
1826: Exception lastException = null;
1827: for (int i = 0; shouldTry(i); ++i) {
1828: try {
1829: if (i > 0)
1830: Thread.sleep(acq_attempt_delay);
1831:
1832: //we don't want this call to be sync'd
1833: //on the pool, so that resource acquisition
1834: //does not interfere with other pool clients.
1835: BasicResourcePool.this .doAcquire();
1836:
1837: success = true;
1838: } catch (InterruptedException e) {
1839: // end the whole task on interrupt, regardless of success
1840: // or failure
1841: throw e;
1842: } catch (Exception e) {
1843: //e.printStackTrace();
1844:
1845: // if num_acq_attempts <= 0, we try to acquire forever, so the end-of-batch
1846: // log message below will never be triggered if there is a persistent problem
1847: // so in this case, it's better flag a higher-than-debug-level message for
1848: // each failed attempt. (Thanks to Eric Crahen for calling attention to this
1849: // issue.)
1850: MLevel logLevel = (num_acq_attempts > 0 ? MLevel.FINE
1851: : MLevel.INFO);
1852: if (logger.isLoggable(logLevel))
1853: logger
1854: .log(
1855: logLevel,
1856: "An exception occurred while acquiring a poolable resource. Will retry.",
1857: e);
1858:
1859: lastException = e;
1860: setLastAcquisitionFailure(e);
1861: }
1862: }
1863: if (!success) {
1864: if (logger.isLoggable(MLevel.WARNING)) {
1865: logger
1866: .log(
1867: MLevel.WARNING,
1868: this
1869: + " -- Acquisition Attempt Failed!!! Clearing pending acquires. "
1870: + "While trying to acquire a needed new resource, we failed "
1871: + "to succeed more than the maximum number of allowed "
1872: + "acquisition attempts ("
1873: + num_acq_attempts
1874: + "). "
1875: + (lastException == null ? ""
1876: : "Last acquisition attempt exception: "),
1877: lastException);
1878: }
1879: if (break_on_acquisition_failure) {
1880: //System.err.println("\tTHE RESOURCE POOL IS PERMANENTLY BROKEN!");
1881: if (logger.isLoggable(MLevel.SEVERE))
1882: logger
1883: .severe("A RESOURCE POOL IS PERMANENTLY BROKEN! ["
1884: + this + "]");
1885: unexpectedBreak();
1886: } else
1887: forceKillAcquires();
1888: } else
1889: recheckResizePool();
1890: } catch (ResourceClosedException e) // one of our async threads died
1891: {
1892: //e.printStackTrace();
1893: if (Debug.DEBUG) {
1894: if (logger.isLoggable(MLevel.FINE))
1895: logger
1896: .log(
1897: MLevel.FINE,
1898: "a resource pool async thread died.",
1899: e);
1900: }
1901: unexpectedBreak();
1902: } catch (InterruptedException e) //from force kill acquires, or by the thread pool during the long task...
1903: {
1904: if (logger.isLoggable(MLevel.WARNING)) {
1905: logger
1906: .log(
1907: MLevel.WARNING,
1908: BasicResourcePool.this
1909: + " -- Thread unexpectedly interrupted while performing an acquisition attempt.",
1910: e);
1911: }
1912:
1913: // System.err.println(BasicResourcePool.this + " -- Thread unexpectedly interrupted while waiting for stale acquisition attempts to die.");
1914: // e.printStackTrace();
1915:
1916: recheckResizePool();
1917: } finally {
1918: decrementPendingAcquires();
1919: }
1920: }
1921:
1922: private boolean shouldTry(int attempt_num) {
1923: //try if we haven't already succeeded
1924: //and someone hasn't signalled that our resource source is down
1925: //and not max attempts is set,
1926: //or we are less than the set limit
1927: return !success
1928: && !isForceKillAcquiresPending()
1929: && (num_acq_attempts <= 0 || attempt_num < num_acq_attempts);
1930: }
1931: }
1932:
1933: /*
1934: * task we post to separate thread to remove
1935: * unspecified pooled resources
1936: *
1937: * TODO: do removal and destruction synchronously
1938: * but carefully not synchronized during the
1939: * destruction of the resource.
1940: */
1941: class RemoveTask implements Runnable {
1942: public RemoveTask() {
1943: incrementPendingRemoves();
1944: }
1945:
1946: public void run() {
1947: try {
1948: synchronousRemoveArbitraryResource();
1949: recheckResizePool();
1950: } finally {
1951: decrementPendingRemoves();
1952: }
1953: }
1954: }
1955:
1956: class CullTask extends TimerTask {
1957: public void run() {
1958: try {
1959: if (Debug.DEBUG && Debug.TRACE >= Debug.TRACE_MED
1960: && logger.isLoggable(MLevel.FINER))
1961: logger.log(MLevel.FINER,
1962: "Checking for expired resources - "
1963: + new Date() + " ["
1964: + BasicResourcePool.this + "]");
1965: synchronized (BasicResourcePool.this ) {
1966: cullExpired();
1967: }
1968: } catch (ResourceClosedException e) // one of our async threads died
1969: {
1970: if (Debug.DEBUG) {
1971: if (logger.isLoggable(MLevel.FINE))
1972: logger
1973: .log(
1974: MLevel.FINE,
1975: "a resource pool async thread died.",
1976: e);
1977: }
1978: unexpectedBreak();
1979: }
1980: }
1981: }
1982:
1983: // this is run by a single-threaded timer, so we don't have
1984: // to worry about multiple threads executing the task at the same
1985: // time
1986: class CheckIdleResourcesTask extends TimerTask {
1987: public void run() {
1988: try {
1989: //System.err.println("c3p0-JENNIFER: refurbishing idle resources - " + new Date() + " [" + BasicResourcePool.this + "]");
1990: if (Debug.DEBUG && Debug.TRACE >= Debug.TRACE_MED
1991: && logger.isLoggable(MLevel.FINER))
1992: logger.log(MLevel.FINER,
1993: "Refurbishing idle resources - "
1994: + new Date() + " ["
1995: + BasicResourcePool.this + "]");
1996: synchronized (BasicResourcePool.this ) {
1997: checkIdleResources();
1998: }
1999: } catch (ResourceClosedException e) // one of our async threads died
2000: {
2001: //e.printStackTrace();
2002: if (Debug.DEBUG) {
2003: if (logger.isLoggable(MLevel.FINE))
2004: logger
2005: .log(
2006: MLevel.FINE,
2007: "a resource pool async thread died.",
2008: e);
2009: }
2010: unexpectedBreak();
2011: }
2012: }
2013: }
2014:
2015: class AsyncTestIdleResourceTask implements Runnable {
2016: // unchanging after ctor
2017: Object resc;
2018:
2019: // protected by this' lock
2020: boolean pending = true;
2021: boolean failed;
2022:
2023: AsyncTestIdleResourceTask(Object resc) {
2024: this .resc = resc;
2025: }
2026:
2027: public void run() {
2028: assert !Thread.holdsLock(BasicResourcePool.this );
2029:
2030: try {
2031: try {
2032: mgr.refurbishIdleResource(resc);
2033: } catch (Exception e) {
2034: if (logger.isLoggable(MLevel.FINE))
2035: logger.log(MLevel.FINE,
2036: "BasicResourcePool: An idle resource is broken and will be purged. ["
2037: + resc + ']', e);
2038:
2039: synchronized (BasicResourcePool.this ) {
2040: if (managed.keySet().contains(resc)) //resc might have been culled as expired while we tested
2041: {
2042: removeResource(resc);
2043: ensureMinResources();
2044: }
2045:
2046: ++failed_idle_tests;
2047: setLastIdleCheckFailure(e);
2048: }
2049: }
2050: } finally {
2051: synchronized (BasicResourcePool.this ) {
2052: idleCheckResources.remove(resc);
2053: BasicResourcePool.this .notifyAll();
2054: }
2055: }
2056: }
2057: }
2058:
2059: final static class PunchCard {
2060: long acquisition_time;
2061: long last_checkin_time;
2062: long checkout_time;
2063: Exception checkoutStackTraceException;
2064:
2065: PunchCard() {
2066: this .acquisition_time = System.currentTimeMillis();
2067: this .last_checkin_time = acquisition_time;
2068: this .checkout_time = -1;
2069: this .checkoutStackTraceException = null;
2070: }
2071: }
2072:
2073: // static class CheckInProgressResourceHolder
2074: // {
2075: // Object checkResource;
2076:
2077: // public synchronized void setCheckResource( Object resc )
2078: // {
2079: // this.checkResource = resc;
2080: // this.notifyAll();
2081: // }
2082:
2083: // public void unsetCheckResource()
2084: // { setCheckResource( null ); }
2085:
2086: // /**
2087: // * @return true if we actually had to wait
2088: // */
2089: // public synchronized boolean awaitNotInCheck( Object resc )
2090: // {
2091: // boolean had_to_wait = false;
2092: // boolean set_interrupt = false;
2093: // while ( checkResource == resc )
2094: // {
2095: // try
2096: // {
2097: // had_to_wait = true;
2098: // this.wait();
2099: // }
2100: // catch ( InterruptedException e )
2101: // {
2102: // e.printStackTrace();
2103: // set_interrupt = true;
2104: // }
2105: // }
2106: // if ( set_interrupt )
2107: // Thread.currentThread().interrupt();
2108: // return had_to_wait;
2109: // }
2110: // }
2111: }
|