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

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

Webmaster