Annotation of java/classes/org/w3c/util/ThreadCache.java, revision 1.18

1.5       abaird      1: // ThreadCache.java
1.18    ! ylafon      2: // $Id: ThreadCache.java,v 1.17 2007/09/28 14:14:47 ylafon Exp $
1.5       abaird      3: // (c) COPYRIGHT MIT and INRIA, 1996-1997.
                      4: // Please first read the full copyright statement in file COPYRIGHT.html
                      5: 
1.11      bmahe       6: package org.w3c.util;
1.1       abaird      7: 
                      8: class CachedThread extends Thread {
1.18    ! ylafon      9:     Runnable runner = null;
        !            10:     boolean alive = true;
        !            11:     ThreadCache cache = null;
        !            12:     CachedThread next = null;
        !            13:     CachedThread prev = null;
        !            14:     boolean terminated = false;
        !            15:     boolean started = false;
        !            16:     boolean firstime = true;
1.2       abaird     17: 
                     18:     synchronized boolean isTerminated() {
1.18    ! ylafon     19:         boolean ret = terminated;
        !            20:         terminated = true;
        !            21:         return ret;
1.2       abaird     22:     }
                     23: 
1.1       abaird     24:     synchronized Runnable waitForRunner() {
1.18    ! ylafon     25:         boolean to = false;
1.2       abaird     26: 
1.18    ! ylafon     27:         while (alive) {
        !            28:             // Is a runner available ?
        !            29:             if (runner != null) {
        !            30:                 Runnable torun = runner;
        !            31:                 firstime = false;
        !            32:                 runner = null;
        !            33:                 return torun;
        !            34:             } else if (firstime) {
        !            35:                 // This thread will not be declared free until it runs once:
        !            36:                 try {
        !            37:                     wait();
        !            38:                 } catch (InterruptedException ex) {
        !            39:                 }
        !            40:             } else if (alive = cache.isFree(this, to)) {
        !            41:                 // Notify the cache that we are free, and continue if allowed:
        !            42:                 try {
        !            43:                     int idleto = cache.getIdleTimeout();
        !            44:                     to = false;
        !            45:                     if (idleto > 0) {
        !            46:                         wait(idleto);
        !            47:                         to = (runner == null);
        !            48:                     } else {
        !            49:                         wait();
        !            50:                     }
        !            51:                 } catch (InterruptedException ex) {
        !            52:                 }
        !            53:             }
        !            54:         }
        !            55:         return null;
1.1       abaird     56:     }
                     57: 
                     58:     synchronized void kill() {
1.18    ! ylafon     59:         alive = false;
        !            60:         notify();
1.1       abaird     61:     }
                     62: 
1.8       abaird     63:     synchronized boolean wakeup(Runnable runnable) {
1.18    ! ylafon     64:         if (alive) {
        !            65:             runner = runnable;
        !            66:             if (!started)
        !            67:                 this.start();
        !            68:             notify();
        !            69:             return true;
        !            70:         } else {
        !            71:             return false;
        !            72:         }
1.1       abaird     73:     }
                     74: 
1.8       abaird     75:     public synchronized void start() {
1.18    ! ylafon     76:         super.start();
        !            77:         this.started = true;
1.8       abaird     78:     }
1.16      ylafon     79: 
1.1       abaird     80:     public void run() {
1.18    ! ylafon     81:         try {
        !            82:             while (true) {
        !            83:                 // Wait for a runner:
        !            84:                 Runnable torun = waitForRunner();
        !            85:                 // If runner, run:
        !            86:                 if (torun != null)
        !            87:                     torun.run();
        !            88:                 // If dead, stop
        !            89:                 if (!alive)
        !            90:                     break;
        !            91:             }
        !            92:         } finally {
        !            93:             cache.isDead(this);
        !            94:         }
1.1       abaird     95:     }
                     96: 
1.3       abaird     97:     CachedThread(ThreadCache cache, int id) {
1.18    ! ylafon     98:         super(cache.getThreadGroup(), cache.getThreadGroup().getName() + ":" + id);
        !            99:         this.cache = cache;
        !           100:         setPriority(cache.getThreadPriority());
        !           101:         setDaemon(true);
1.1       abaird    102:     }
                    103: 
                    104: }
                    105: 
                    106: public class ThreadCache {
1.2       abaird    107:     private static final boolean debug = false;
                    108: 
1.1       abaird    109:     /**
                    110:      * Default number of cached threads.
                    111:      */
                    112:     private static final int DEFAULT_CACHESIZE = 5;
                    113:     /**
1.2       abaird    114:      * Has this thread cache been initialized ?
                    115:      */
                    116:     protected boolean inited = false;
                    117:     /**
1.1       abaird    118:      * The thread group for this thread cache.
                    119:      */
                    120:     protected ThreadGroup group = null;
                    121:     /**
                    122:      * Number of cached threads.
                    123:      */
                    124:     protected int cachesize = DEFAULT_CACHESIZE;
                    125:     /**
                    126:      * Number of created threads.
                    127:      */
                    128:     protected int threadcount = 0;
                    129:     /**
1.3       abaird    130:      * Uniq thread identifier within this ThreadCache instance.
                    131:      */
                    132:     protected int threadid = 0;
                    133:     /**
1.2       abaird    134:      * Number of idle threads to always maintain alive.
                    135:      */
                    136:     protected int idlethreads = 0;
                    137:     /**
1.1       abaird    138:      * Should we queue thread requests, rather then creating new threads.
                    139:      */
                    140:     protected boolean growasneeded = false;
                    141:     /**
1.13      ylafon    142:      * Number of used threads
                    143:      */
                    144:     protected int usedthreads = 0;
                    145:     /**
1.1       abaird    146:      * List of free threads.
                    147:      */
                    148:     protected CachedThread freelist = null;
1.14      ylafon    149:     protected CachedThread freetail = null;
1.1       abaird    150:     /**
1.2       abaird    151:      * The idle timeout, for a thread to wait before being killed.
                    152:      * Defaults to <strong>5000</strong> milliseconds.
                    153:      */
                    154:     protected int idletimeout = 5000;
                    155:     /**
                    156:      * Cached thread priority.
                    157:      */
                    158:     protected int threadpriority = 5;
                    159: 
                    160:     /**
                    161:      * Get the idle timeout value for this cache.
1.18    ! ylafon    162:      *
1.2       abaird    163:      * @return The idletimeout value, or negative if no timeout applies.
                    164:      */
                    165: 
                    166:     synchronized final int getIdleTimeout() {
1.18    ! ylafon    167:         return (threadcount <= idlethreads) ? -1 : idletimeout;
1.2       abaird    168:     }
1.1       abaird    169: 
                    170:     /**
                    171:      * The given thread is about to be declared free.
1.18    ! ylafon    172:      *
1.4       abaird    173:      * @return A boolean, <strong>true</strong> if the thread is to continue
1.18    ! ylafon    174:      *         running, <strong>false</strong> if the thread should stop.
1.1       abaird    175:      */
                    176: 
1.2       abaird    177:     final synchronized boolean isFree(CachedThread t, boolean timedout) {
1.18    ! ylafon    178:         if (timedout && (threadcount > idlethreads)) {
        !           179:             if (!t.isTerminated()) {
        !           180:                 threadcount--;
        !           181:                 usedthreads--;
        !           182:                 notify();
        !           183:             }
        !           184:             return false;
        !           185:         } else if (threadcount <= cachesize) {
        !           186:             t.prev = freetail;
        !           187:             if (freetail != null)
        !           188:                 freetail.next = t;
        !           189:             freetail = t;
        !           190:             if (freelist == null)
        !           191:                 freelist = t;
        !           192:             usedthreads--;
        !           193:             notify();
        !           194:             return true;
        !           195:         } else {
        !           196:             if (!t.isTerminated()) {
        !           197:                 threadcount--;
        !           198:                 usedthreads--;
        !           199:                 notify();
        !           200:             }
        !           201:             return false;
        !           202:         }
1.1       abaird    203:     }
1.16      ylafon    204: 
1.1       abaird    205:     /**
                    206:      * The given thread has terminated, cleanup any associated state.
1.18    ! ylafon    207:      *
        !           208:      * @param t The dead CachedThread instance.
1.1       abaird    209:      */
                    210: 
                    211:     final synchronized void isDead(CachedThread t) {
1.18    ! ylafon    212:         if (debug)
        !           213:             System.out.println("** " + t + ": is dead tc=" + threadcount);
        !           214:         if (!t.isTerminated()) {
        !           215:             threadcount--;
        !           216:             notify();
        !           217:         }
1.1       abaird    218:     }
                    219: 
                    220:     /**
                    221:      * Create a new thread within this thread cache.
1.18    ! ylafon    222:      *
1.1       abaird    223:      * @return A new CachedThread instance.
                    224:      */
                    225: 
                    226:     private synchronized CachedThread createThread() {
1.18    ! ylafon    227:         threadcount++;
        !           228:         threadid++;
        !           229:         return new CachedThread(this, threadid);
1.1       abaird    230:     }
                    231: 
                    232:     /**
                    233:      * Allocate a new thread, as requested.
1.18    ! ylafon    234:      *
1.10      abaird    235:      * @param waitp Should we wait until a thread is available ?
1.18    ! ylafon    236:      * @return A launched CachedThread instance, or <strong>null</strong> if
        !           237:      *         unable to allocate a new thread, and <code>waitp</code> is <strong>
        !           238:      *         false</strong>.
1.1       abaird    239:      */
                    240: 
                    241:     protected synchronized CachedThread allocateThread(boolean waitp) {
1.18    ! ylafon    242:         CachedThread t = null;
        !           243:         while (true) {
        !           244:             if (freelist != null) {
        !           245:                 if (debug)
        !           246:                     System.out.println("*** allocateThread: free thread");
        !           247:                 t = freelist;
        !           248:                 freelist = freelist.next;
        !           249:                 if (freelist != null) {
        !           250:                     freelist.prev = null;
        !           251:                 } else {
        !           252:                     freetail = null;
        !           253:                 }
        !           254:                 t.next = null;
        !           255:                 break;
        !           256:             } else if ((threadcount < cachesize) || growasneeded) {
        !           257:                 if (debug)
        !           258:                     System.out.println("*** create new thread.");
        !           259:                 t = createThread();
        !           260:                 break;
        !           261:             } else if (waitp) {
        !           262:                 if (debug)
        !           263:                     System.out.println("*** wait for a thread.");
        !           264:                 // Wait for a thread to become available
        !           265:                 try {
        !           266:                     wait();
        !           267:                 } catch (InterruptedException ex) {
        !           268:                 }
        !           269:             } else {
        !           270:                 return null;
        !           271:             }
        !           272:         }
        !           273:         return t;
1.1       abaird    274:     }
                    275: 
                    276:     /**
1.2       abaird    277:      * Set the thread cache size.
1.18    ! ylafon    278:      * This will also update the number of idle threads to maintain, if
1.2       abaird    279:      * requested.
1.18    ! ylafon    280:      *
1.2       abaird    281:      * @param cachesize The new thread cache size.
1.18    ! ylafon    282:      * @param update    If <strong>true</strong> also update the number of
        !           283:      *                  threads to maintain idle.
1.2       abaird    284:      */
                    285: 
                    286:     public synchronized void setCachesize(int cachesize, boolean update) {
1.18    ! ylafon    287:         this.cachesize = cachesize;
        !           288:         if (update)
        !           289:             this.idlethreads = (cachesize >> 1);
1.2       abaird    290:     }
                    291: 
                    292:     /**
                    293:      * Set the thread cache size.
                    294:      * Updaet the number of idle threads to keep alive.
1.18    ! ylafon    295:      *
1.2       abaird    296:      * @param cachesize The new thread cache size.
                    297:      */
                    298: 
                    299:     public void setCachesize(int cachesize) {
1.18    ! ylafon    300:         setCachesize(cachesize, true);
1.2       abaird    301:     }
1.16      ylafon    302: 
1.2       abaird    303:     /**
1.5       abaird    304:      * Enable/disable the thread cache to grow as needed.
                    305:      * This flag should be turned on only if always getting a thread as fast
                    306:      * as possible is critical.
1.18    ! ylafon    307:      *
1.5       abaird    308:      * @param onoff The toggle.
                    309:      */
                    310: 
                    311:     public void setGrowAsNeeded(boolean onoff) {
1.18    ! ylafon    312:         this.growasneeded = onoff;
1.5       abaird    313:     }
                    314: 
                    315:     /**
1.2       abaird    316:      * Set all the cached threads priority.
                    317:      * Changing the cached thread priority should be done before the thread
1.18    ! ylafon    318:      * cache is initialized, it will <em>not</em> affect already created
1.2       abaird    319:      * threads.
1.18    ! ylafon    320:      *
1.2       abaird    321:      * @param priority The new cachewd threads priority.
                    322:      */
                    323: 
                    324:     public void setThreadPriority(int priority) {
1.18    ! ylafon    325:         threadpriority = priority;
1.2       abaird    326:     }
                    327: 
                    328:     /**
                    329:      * Get the cached thread normal priority.
1.18    ! ylafon    330:      *
1.2       abaird    331:      * @return Currently assigned cached thread priority.
                    332:      */
                    333: 
                    334:     public int getThreadPriority() {
1.18    ! ylafon    335:         return threadpriority;
1.2       abaird    336:     }
                    337: 
                    338:     /**
1.18    ! ylafon    339:      * Set the idle timeout.
        !           340:      * The idle timeout value is used to eliminate threads that have remain
        !           341:      * idle for too long (although the thread cache will ensure that a
1.3       abaird    342:      * decent minimal number of threads stay around).
1.18    ! ylafon    343:      *
1.3       abaird    344:      * @param idletimeout The new idle timeout.
                    345:      */
                    346: 
                    347:     public synchronized void setIdleTimeout(int idletimeout) {
1.18    ! ylafon    348:         this.idletimeout = idletimeout;
1.3       abaird    349:     }
                    350: 
                    351:     /**
1.1       abaird    352:      * Request a thread to run on the given object.
1.18    ! ylafon    353:      *
1.1       abaird    354:      * @param runnable The object to run with the allocated thread.
1.18    ! ylafon    355:      * @param waitp    If <strong>true</strong> wait until a free thread is
        !           356:      *                 available, otherwise, return <strong>false</strong>.
1.1       abaird    357:      * @return A boolean, <strong>true</strong> if a thread was successfully
1.18    ! ylafon    358:      *         allocated for the given object, <strong>false</strong> otherwise.
1.1       abaird    359:      */
                    360: 
                    361:     public boolean getThread(Runnable runnable, boolean waitp) {
1.18    ! ylafon    362:         if (debug)
        !           363:             System.out.println("*** getting a thread for " + runnable);
        !           364:         if (!inited)
        !           365:             throw new RuntimeException("Uninitialized thread cache");
        !           366:         // Allocate and launch the thread:
        !           367:         while (true) {
        !           368:             CachedThread t = allocateThread(waitp);
        !           369:             if (t != null) {
        !           370:                 if (t.wakeup(runnable)) {
        !           371:                     synchronized (this) {
        !           372:                         usedthreads++;
        !           373:                     }
        !           374:                     return true;
        !           375:                 }
        !           376:             } else {
        !           377:                 return false;
        !           378:             }
        !           379:         }
1.3       abaird    380:     }
                    381: 
                    382:     /**
                    383:      * Get the ThreadGroup managed by this ThreadCache instance.
1.18    ! ylafon    384:      *
1.3       abaird    385:      * @return A ThreadGroup instance.
                    386:      */
                    387: 
                    388:     public ThreadGroup getThreadGroup() {
1.18    ! ylafon    389:         return group;
1.13      ylafon    390:     }
                    391: 
                    392:     /**
                    393:      * Wait until all the threads have finished their duty
                    394:      */
                    395: 
                    396:     public synchronized void waitForCompletion() {
1.18    ! ylafon    397:         while (usedthreads > 0) {
        !           398:             if (debug)
        !           399:                 System.out.println("*** Waiting for " + usedthreads + " threads");
        !           400:             try {
        !           401:                 wait();
        !           402:             } catch (InterruptedException ex) {
        !           403:             }
        !           404:         }
1.1       abaird    405:     }
                    406: 
                    407:     /**
1.2       abaird    408:      * Initialize the given thread cache.
                    409:      * This two stage initialize method is done so that configuration
                    410:      * of the thread cache can be done before any thread get actually
                    411:      * created.
                    412:      */
                    413: 
                    414:     public synchronized void initialize() {
1.18    ! ylafon    415:         CachedThread t = createThread();
        !           416:         freelist = t;
        !           417:         freetail = t;
        !           418:         t.next = null;
        !           419:         t.prev = null;
        !           420:         t.start();
        !           421:         for (int i = 1; i < idlethreads; i++) {
        !           422:             t = createThread();
        !           423:             t.next = freelist;
        !           424:             t.prev = null;
        !           425:             freelist.prev = t;
        !           426:             freelist = t;
        !           427:             t.start();
        !           428:         }
        !           429:         inited = true;
1.2       abaird    430:     }
                    431: 
                    432:     /**
1.1       abaird    433:      * Create a thread cache, whose threads are to be children of the group.
1.18    ! ylafon    434:      *
1.1       abaird    435:      * @param group The thread group to which this thread cache is bound.
                    436:      */
                    437: 
1.2       abaird    438:     public ThreadCache(ThreadGroup group) {
1.18    ! ylafon    439:         this.group = group;
1.1       abaird    440:     }
                    441: 
                    442:     /**
                    443:      * Create a thread cache, after creating a new thread group.
1.18    ! ylafon    444:      *
1.1       abaird    445:      * @param name The name of the thread group to create.
                    446:      */
                    447: 
                    448:     public ThreadCache(String name) {
1.18    ! ylafon    449:         this(new ThreadGroup(name));
1.1       abaird    450:     }
                    451: 
                    452:     /**
                    453:      * Create a thread cache, after creating a new thread group.
1.18    ! ylafon    454:      *
1.1       abaird    455:      * @param parent The parent of the thread group to create.
1.18    ! ylafon    456:      * @param name   The name of the thread group.
1.1       abaird    457:      */
                    458: 
                    459:     public ThreadCache(ThreadGroup parent, String name) {
1.18    ! ylafon    460:         this(new ThreadGroup(parent, name));
1.1       abaird    461:     }
                    462: 
                    463: }

Webmaster