EMMA Coverage Report (generated Sat Sep 03 11:42:34 KST 2005)
[all classes][org.apache.mina.util]

COVERAGE SUMMARY FOR SOURCE FILE [BaseThreadPool.java]

nameclass, %method, %block, %line, %
BaseThreadPool.java75%  (3/4)89%  (24/27)81%  (625/775)85%  (183.6/217)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class BaseThreadPool100% (1/1)82%  (14/17)79%  (294/370)83%  (84.6/102)
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)
decreasePoolSize (): void 100% (1/1)75%  (15/20)93%  (3.7/4)
increasePoolSize (): void 100% (1/1)75%  (15/20)93%  (3.7/4)
removeSessionBuffer (BaseThreadPool$SessionBuffer): void 100% (1/1)78%  (18/23)96%  (5.8/6)
BaseThreadPool (String): void 100% (1/1)82%  (45/55)87%  (13/15)
acquireThreadId (): int 100% (1/1)82%  (23/28)83%  (5/6)
fireEvent (Object, Session, EventType, Object): void 100% (1/1)83%  (50/60)95%  (13.2/14)
getSessionBuffer (Session): BaseThreadPool$SessionBuffer 100% (1/1)88%  (37/42)94%  (9.4/10)
stop (): void 100% (1/1)91%  (30/33)82%  (14/17)
start (): void 100% (1/1)96%  (24/25)88%  (7/8)
<static initializer> 100% (1/1)100% (7/7)100% (2/2)
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$Worker100% (1/1)100% (9/9)81%  (320/394)86%  (95.1/111)
waitForPromotion (): boolean 100% (1/1)63%  (55/87)68%  (16.9/25)
lead (): boolean 100% (1/1)71%  (22/31)83%  (6.7/8)
follow (): void 100% (1/1)75%  (30/40)89%  (8/9)
fetchBuffer (): BaseThreadPool$SessionBuffer 100% (1/1)86%  (42/49)81%  (13/16)
processEvents (BaseThreadPool$SessionBuffer): void 100% (1/1)87%  (34/39)94%  (9.4/10)
releaseBuffer (BaseThreadPool$SessionBuffer): void 100% (1/1)88%  (35/40)94%  (10.4/11)
giveUpLead (): void 100% (1/1)90%  (44/49)97%  (11.7/12)
run (): void 100% (1/1)96%  (27/28)92%  (12/13)
BaseThreadPool$Worker (BaseThreadPool): void 100% (1/1)100% (31/31)100% (7/7)
     
class BaseThreadPool$SessionBuffer100% (1/1)100% (1/1)100% (11/11)100% (4/4)
BaseThreadPool$SessionBuffer (Session): void 100% (1/1)100% (11/11)100% (4/4)

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

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