EMMA Coverage Report (generated Tue Dec 20 11:01:01 KST 2005)
[all classes][org.apache.mina.util]

COVERAGE SUMMARY FOR SOURCE FILE [BaseThreadPool.java]

nameclass, %method, %block, %line, %
BaseThreadPool.java75%  (3/4)83%  (25/30)83%  (689/831)87%  (195.7/224)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class BaseThreadPool100% (1/1)83%  (15/18)81%  (345/426)84%  (95.4/113)
getThreadNamePrefix (): String 0%   (0/1)0%   (0/3)0%   (0/1)
setKeepAliveTime (int): void 0%   (0/1)0%   (0/4)0%   (0/2)
setMaximumPoolSize (int): void 0%   (0/1)0%   (0/10)0%   (0/4)
getPoolSize (): int 100% (1/1)67%  (10/15)67%  (2/3)
releaseThreadId (int): void 100% (1/1)74%  (14/19)93%  (3.7/4)
removeSessionBuffer (BaseThreadPool$SessionBuffer): void 100% (1/1)78%  (18/23)96%  (5.8/6)
decreasePoolSize (BaseThreadPool$Worker): void 100% (1/1)80%  (20/25)96%  (4.8/5)
increasePoolSize (BaseThreadPool$Worker): void 100% (1/1)80%  (20/25)96%  (4.8/5)
acquireThreadId (): int 100% (1/1)82%  (23/28)83%  (5/6)
fireEvent (Object, Session, EventType, Object): void 100% (1/1)83%  (49/59)95%  (13.2/14)
BaseThreadPool (String): void 100% (1/1)83%  (50/60)88%  (14/16)
getSessionBuffer (Session): BaseThreadPool$SessionBuffer 100% (1/1)88%  (37/42)94%  (9.4/10)
stop (): void 100% (1/1)89%  (63/71)86%  (20.7/24)
start (): void 100% (1/1)96%  (24/25)88%  (7/8)
<static initializer> 100% (1/1)100% (7/7)100% (2/2)
fetchSessionBuffer (Queue): BaseThreadPool$SessionBuffer 100% (1/1)100% (4/4)100% (1/1)
getKeepAliveTime (): int 100% (1/1)100% (3/3)100% (1/1)
getMaximumPoolSize (): int 100% (1/1)100% (3/3)100% (1/1)
     
class BaseThreadPool$10%   (0/1)100% (0/0)100% (0/0)100% (0/0)
     
class BaseThreadPool$SessionBuffer100% (1/1)33%  (1/3)65%  (11/17)67%  (4/6)
getEventQueue (): Queue 0%   (0/1)0%   (0/3)0%   (0/1)
getSession (): Session 0%   (0/1)0%   (0/3)0%   (0/1)
BaseThreadPool$SessionBuffer (Session): void 100% (1/1)100% (11/11)100% (4/4)
     
class BaseThreadPool$Worker100% (1/1)100% (9/9)86%  (333/388)92%  (96.3/105)
lead (): boolean 100% (1/1)71%  (22/31)83%  (6.7/8)
follow (): void 100% (1/1)75%  (30/40)89%  (8/9)
releaseBuffer (BaseThreadPool$SessionBuffer): void 100% (1/1)79%  (31/39)85%  (9.4/11)
fetchBuffer (): BaseThreadPool$SessionBuffer 100% (1/1)85%  (29/34)94%  (9.4/10)
waitForPromotion (): boolean 100% (1/1)86%  (82/95)87%  (21.7/25)
processEvents (BaseThreadPool$SessionBuffer): void 100% (1/1)87%  (34/39)94%  (9.4/10)
giveUpLead (): void 100% (1/1)90%  (44/49)97%  (11.7/12)
BaseThreadPool$Worker (BaseThreadPool): void 100% (1/1)100% (32/32)100% (7/7)
run (): void 100% (1/1)100% (29/29)100% (13/13)

1/*
2 *   @(#) $Id: BaseThreadPool.java 357871 2005-12-20 01:56:40Z trustin $
3 *
4 *   Copyright 2004 The Apache Software Foundation
5 *
6 *   Licensed under the Apache License, Version 2.0 (the "License");
7 *   you may not use this file except in compliance with the License.
8 *   You may obtain a copy of the License at
9 *
10 *       http://www.apache.org/licenses/LICENSE-2.0
11 *
12 *   Unless required by applicable law or agreed to in writing, software
13 *   distributed under the License is distributed on an "AS IS" BASIS,
14 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 *   See the License for the specific language governing permissions and
16 *   limitations under the License.
17 *
18 */
19package org.apache.mina.util;
20 
21import java.util.ArrayList;
22import java.util.IdentityHashMap;
23import java.util.Iterator;
24import java.util.List;
25import java.util.Map;
26import java.util.Set;
27 
28import org.apache.mina.common.Session;
29 
30/**
31 * A base implementation of Thread-pooling filters.
32 * This filter forwards events to its thread pool.  This is an implementation of
33 * <a href="http://deuce.doc.wustl.edu/doc/pspdfs/lf.pdf">Leader/Followers
34 * thread pool</a> by Douglas C. Schmidt et al.
35 * 
36 * @author The Apache Directory Project (dev@directory.apache.org)
37 * @version $Rev: 357871 $, $Date: 2005-12-20 10:56:40 +0900 (Tue, 20 Dec 2005) $
38 */
39public abstract class BaseThreadPool implements ThreadPool
40{
41    /**
42     * Default maximum size of thread pool (2G).
43     */
44    public static final int DEFAULT_MAXIMUM_POOL_SIZE = Integer.MAX_VALUE;
45 
46    /**
47     * Default keep-alive time of thread pool (1 min).
48     */
49    public static final int DEFAULT_KEEP_ALIVE_TIME = 60 * 1000;
50 
51    /**
52     * A queue which contains {@link Integer}s which represents reusable
53     * thread IDs.  {@link Worker} first checks this queue and then
54     * uses {@link #threadId} when no reusable thread ID is available.
55     */
56    private static final Queue threadIdReuseQueue = new Queue();
57    private static int threadId = 0;
58    
59    private static int acquireThreadId()
60    {
61        synchronized( threadIdReuseQueue )
62        {
63            Integer id = ( Integer ) threadIdReuseQueue.pop();
64            if( id == null )
65            {
66                return ++ threadId;
67            }
68            else
69            {
70                return id.intValue();
71            }
72        }
73    }
74    
75    private static void releaseThreadId( int id )
76    {
77        synchronized( threadIdReuseQueue )
78        {
79            threadIdReuseQueue.push( new Integer( id ) );
80        }
81    }
82 
83    private final String threadNamePrefix;
84    private final Map buffers = new IdentityHashMap();
85    private final BlockingQueue unfetchedSessionBuffers = new BlockingQueue();
86    private final Set allSessionBuffers = new IdentityHashSet();
87 
88    private Worker leader;
89    private final Stack followers = new Stack();
90    private final Set allWorkers = new IdentityHashSet();
91 
92    private int maximumPoolSize = DEFAULT_MAXIMUM_POOL_SIZE;
93    private int keepAliveTime = DEFAULT_KEEP_ALIVE_TIME;
94 
95    private boolean started;
96    private boolean shuttingDown;
97 
98    private int poolSize;
99    private final Object poolSizeLock = new Object();
100 
101    /**
102     * Creates a new instance with default thread pool settings.
103     * You'll have to invoke {@link #start()} method to start threads actually.
104     *
105     * @param threadNamePrefix the prefix of the thread names this pool will create.
106     */
107    public BaseThreadPool( String threadNamePrefix )
108    {
109        if( threadNamePrefix == null )
110        {
111            throw new NullPointerException( "threadNamePrefix" );
112        }
113        threadNamePrefix = threadNamePrefix.trim();
114        if( threadNamePrefix.length() == 0 )
115        {
116            throw new IllegalArgumentException( "threadNamePrefix is empty." );
117        }
118        this.threadNamePrefix = threadNamePrefix;
119    }
120    
121    public String getThreadNamePrefix()
122    {
123        return threadNamePrefix;
124    }
125 
126    public int getPoolSize()
127    {
128        synchronized( poolSizeLock )
129        {
130            return poolSize;
131        }
132    }
133 
134    public int getMaximumPoolSize()
135    {
136        return maximumPoolSize;
137    }
138 
139    public int getKeepAliveTime()
140    {
141        return keepAliveTime;
142    }
143 
144    public void setMaximumPoolSize( int maximumPoolSize )
145    {
146        if( maximumPoolSize <= 0 )
147            throw new IllegalArgumentException();
148        this.maximumPoolSize = maximumPoolSize;
149    }
150 
151    public void setKeepAliveTime( int keepAliveTime )
152    {
153        this.keepAliveTime = keepAliveTime;
154    }
155 
156    public synchronized void start()
157    {
158        if( started )
159            return;
160 
161        shuttingDown = false;
162 
163        leader = new Worker();
164        leader.start();
165        leader.lead();
166 
167        started = true;
168    }
169 
170    public synchronized void stop()
171    {
172        if( !started )
173            return;
174 
175        shuttingDown = true;
176        while( getPoolSize() != 0 )
177        {
178            List allWorkers;
179            synchronized( poolSizeLock )
180            {
181                allWorkers = new ArrayList( this.allWorkers );
182            }
183            
184            for( Iterator i = allWorkers.iterator(); i.hasNext(); )
185            {
186                Worker worker = ( Worker ) i.next();
187                while( worker.isAlive() )
188                {
189                    worker.interrupt();
190                    try
191                    {
192                        // This timeout will help us from 
193                        // infinite lock-up and interrupt workers again.
194                        worker.join( 100 ); 
195                    }
196                    catch( InterruptedException e )
197                    {
198                    }
199                }
200            }
201        }
202 
203        this.allSessionBuffers.clear();
204        this.unfetchedSessionBuffers.clear();
205        this.buffers.clear();
206        this.followers.clear();
207        this.leader = null;
208 
209        started = false;
210    }
211 
212    private void increasePoolSize( Worker worker )
213    {
214        synchronized( poolSizeLock )
215        {
216            poolSize++;
217            allWorkers.add( worker );
218        }
219    }
220 
221    private void decreasePoolSize( Worker worker )
222    {
223        synchronized( poolSizeLock )
224        {
225            poolSize--;
226            allWorkers.remove( worker );
227        }
228    }
229 
230    protected void fireEvent( Object nextFilter, Session session,
231                              EventType type, Object data )
232    {
233        final BlockingQueue unfetchedSessionBuffers = this.unfetchedSessionBuffers;
234        final Set allSessionBuffers = this.allSessionBuffers;
235        final Event event = new Event( type, nextFilter, data );
236 
237        synchronized( unfetchedSessionBuffers )
238        {
239            final SessionBuffer buf = getSessionBuffer( session );
240            final Queue eventQueue = buf.eventQueue;
241 
242            synchronized( buf )
243            {
244                eventQueue.push( event );
245            }
246 
247            if( !allSessionBuffers.contains( buf ) )
248            {
249                allSessionBuffers.add( buf );
250                unfetchedSessionBuffers.push( buf );
251            }
252        }
253    }
254 
255    /**
256     * Implement this method to forward events to <tt>nextFilter</tt>.
257     */
258    protected abstract void processEvent( Object nextFilter, Session session,
259                                          EventType type, Object data );
260 
261    
262    /**
263     * Implement this method to fetch (or pop) a {@link SessionBuffer} from
264     * the given <tt>unfetchedSessionBuffers</tt>.  The default implementation
265     * simply pops the buffer from it.  You could prioritize the fetch order.
266     * 
267     * @return A non-null {@link SessionBuffer}
268     */
269    protected SessionBuffer fetchSessionBuffer( Queue unfetchedSessionBuffers )
270    {
271        return ( SessionBuffer ) unfetchedSessionBuffers.pop();
272    }
273 
274    private SessionBuffer getSessionBuffer( Session session )
275    {
276        final Map buffers = this.buffers;
277        SessionBuffer buf = ( SessionBuffer ) buffers.get( session );
278        if( buf == null )
279        {
280            synchronized( buffers )
281            {
282                buf = ( SessionBuffer ) buffers.get( session );
283                if( buf == null )
284                {
285                    buf = new SessionBuffer( session );
286                    buffers.put( session, buf );
287                }
288            }
289        }
290        return buf;
291    }
292 
293    private void removeSessionBuffer( SessionBuffer buf )
294    {
295        final Map buffers = this.buffers;
296        final Session session = buf.session;
297        synchronized( buffers )
298        {
299            buffers.remove( session );
300        }
301    }
302 
303    protected static class SessionBuffer
304    {
305        private final Session session;
306 
307        private final Queue eventQueue = new Queue();
308 
309        private SessionBuffer( Session session )
310        {
311            this.session = session;
312        }
313        
314        public Session getSession()
315        {
316            return session;
317        }
318        
319        public Queue getEventQueue()
320        {
321            return eventQueue;
322        }
323    }
324 
325    private class Worker extends Thread
326    {
327        private final int id;
328        private final Object promotionLock = new Object();
329        private boolean dead;
330 
331        private Worker()
332        {
333            int id = acquireThreadId();
334            this.id = id;
335            this.setName( threadNamePrefix + '-' + id );
336            increasePoolSize( this );
337        }
338 
339        public boolean lead()
340        {
341            final Object promotionLock = this.promotionLock;
342            synchronized( promotionLock )
343            {
344                if( dead )
345                {
346                    return false;
347                }
348 
349                leader = this;
350                promotionLock.notify();
351            }
352            
353            return true;
354        }
355 
356        public void run()
357        {
358            for( ;; )
359            {
360                if( !waitForPromotion() )
361                    break;
362 
363                SessionBuffer buf = fetchBuffer();
364                giveUpLead();
365 
366                if( buf == null )
367                {
368                    break;
369                }
370 
371                processEvents( buf );
372                follow();
373                releaseBuffer( buf );
374            }
375 
376            decreasePoolSize( this );
377            releaseThreadId( id );
378        }
379 
380        private SessionBuffer fetchBuffer()
381        {
382            BlockingQueue unfetchedSessionBuffers = BaseThreadPool.this.unfetchedSessionBuffers;
383            synchronized( unfetchedSessionBuffers )
384            {
385                while( !shuttingDown )
386                {
387                    try
388                    {
389                        unfetchedSessionBuffers.waitForNewItem();
390                    }
391                    catch( InterruptedException e )
392                    {
393                        continue;
394                    }
395 
396                    return BaseThreadPool.this.fetchSessionBuffer( unfetchedSessionBuffers );
397                }
398            }
399            
400            return null;
401        }
402 
403        private void processEvents( SessionBuffer buf )
404        {
405            final Session session = buf.session;
406            final Queue eventQueue = buf.eventQueue;
407            for( ;; )
408            {
409                Event event;
410                synchronized( buf )
411                {
412                    event = ( Event ) eventQueue.pop();
413                    if( event == null )
414                        break;
415                }
416                processEvent( event.getNextFilter(), session,
417                              event.getType(), event.getData() );
418            }
419        }
420 
421        private void follow()
422        {
423            final Object promotionLock = this.promotionLock;
424            final Stack followers = BaseThreadPool.this.followers;
425            synchronized( promotionLock )
426            {
427                if( this != leader )
428                {
429                    synchronized( followers )
430                    {
431                        followers.push( this );
432                    }
433                }
434            }
435        }
436 
437        private void releaseBuffer( SessionBuffer buf )
438        {
439            final BlockingQueue unfetchedSessionBuffers = BaseThreadPool.this.unfetchedSessionBuffers;
440            final Set allSessionBuffers = BaseThreadPool.this.allSessionBuffers;
441            final Queue eventQueue = buf.eventQueue;
442 
443            synchronized( unfetchedSessionBuffers )
444            {
445                if( eventQueue.isEmpty() )
446                {
447                    allSessionBuffers.remove( buf );
448                    removeSessionBuffer( buf );
449                }
450                else
451                {
452                    unfetchedSessionBuffers.push( buf );
453                }
454            }
455        }
456 
457        private boolean waitForPromotion()
458        {
459            final Object promotionLock = this.promotionLock;
460 
461            final long startTime = System.currentTimeMillis();
462            long currentTime = startTime;
463            
464            synchronized( promotionLock )
465            {
466                while( this != leader && !shuttingDown )
467                {
468                    // Calculate remaining keep-alive time
469                    int keepAliveTime = getKeepAliveTime();
470                    if( keepAliveTime > 0 )
471                    {
472                        keepAliveTime -= ( currentTime - startTime );
473                    }
474                    else
475                    {
476                        keepAliveTime = Integer.MAX_VALUE;
477                    }
478                    
479                    // Break the loop if there's no remaining keep-alive time.
480                    if( keepAliveTime <= 0 )
481                    {
482                        break;
483                    }
484 
485                    // Wait for promotion
486                    try
487                    {
488                        promotionLock.wait( keepAliveTime );
489                    }
490                    catch( InterruptedException e )
491                    {
492                    }
493 
494                    // Update currentTime for the next iteration
495                    currentTime = System.currentTimeMillis();
496                }
497 
498                boolean timeToLead = this == leader && !shuttingDown;
499 
500                if( !timeToLead )
501                {
502                    // time to die
503                    synchronized( followers )
504                    {
505                        followers.remove( this );
506                    }
507 
508                    // Mark as dead explicitly when we've got promotionLock.
509                    dead = true;
510                }
511 
512                return timeToLead;
513            }
514        }
515 
516        private void giveUpLead()
517        {
518            final Stack followers = BaseThreadPool.this.followers;
519            Worker worker;
520            do
521            {
522                synchronized( followers )
523                {
524                    worker = ( Worker ) followers.pop();
525                }
526 
527                if( worker == null )
528                {
529                    // Increase the number of threads if we
530                    // are not shutting down and we can increase the number.
531                    if( !shuttingDown
532                        && getPoolSize() < getMaximumPoolSize() )
533                    {
534                        worker = new Worker();
535                        worker.lead();
536                        worker.start();
537                    }
538 
539                    // This loop should end because:
540                    // 1) lead() is called already,
541                    // 2) or it is shutting down and there's no more threads left.
542                    break;
543                }
544            }
545            while( !worker.lead() );
546        }
547    }
548}

[all classes][org.apache.mina.util]
EMMA 2.0.4217 (C) Vladimir Roubtsov