Package com.sun.messaging.jmq.jmsserver.util.pool

Source Code of com.sun.messaging.jmq.jmsserver.util.pool.ThreadPool$MyThreadGroup

/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 2000-2010 Oracle and/or its affiliates. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License").  You
* may not use this file except in compliance with the License.  You can
* obtain a copy of the License at
* https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
* or packager/legal/LICENSE.txt.  See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at packager/legal/LICENSE.txt.
*
* GPL Classpath Exception:
* Oracle designates this particular file as subject to the "Classpath"
* exception as provided by Oracle in the GPL Version 2 section of the License
* file that accompanied this code.
*
* Modifications:
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyright [year] [name of copyright owner]"
*
* Contributor(s):
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license."  If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above.  However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/

/*
* @(#)ThreadPool.java  1.57 06/29/07
*/

package com.sun.messaging.jmq.jmsserver.util.pool;

import java.util.*;
import com.sun.messaging.jmq.jmsserver.Globals;
import com.sun.messaging.jmq.jmsserver.resources.*;
import com.sun.messaging.jmq.jmsserver.util.MQThread;
import com.sun.messaging.jmq.util.log.Logger;


/**
* This class implements a simple thread pool.
*
* This thread pool has a minimum and maximum number of threads.
* At creation, the minimum number of threads are created and
* "BasicRunnable" objects are assigned to the threads.
*
* If there are no available threads (thread count == max),
* the system will either wait until a thread is available OR
* throw an exception.
*
* As the load on the system decreases, if the thread count is greater
* than <B>min</b>, the threads will exit IF they have not been needed after
* a certain amount of time until the system is back down to <b>min</b>
* <P>
*
* @see Operation
* @see BasicRunnable
*
*/
public class ThreadPool
{
// sync scheme:
//      use lock for objects & counts
//      use current -> for current
//      use current -> for availIndx
//      use available -> for available
//      use this -> access to lists/wait->notify
//
// make copies for resume, destroy

    public Object lock = new Object(); // indexes and counts


    public boolean destroyed = false;

    private boolean in_destroy = false;

    private static boolean DEBUG = false;

    protected Logger logger  = Globals.getLogger();


    RunnableFactory runfac = null;

    /**
     * threads which have exited so their index can be reused
     * threads exit when either they timeout OR a problem occurs
     */
    LinkedHashSet availIndx = null;


    /**
     * list of all current threads/runnables
     */
    ArrayList current = null; // XXX-LKS - may want to use a different list type


    /**
     * list of waiting runnables
     */
    LinkedList available = null;

    /**
     * current indx for next thread created
     */
    int nextThreadId = 0;


    /**
     * thread group for this thread pool
     */
    protected ThreadGroup tgroup = null;


    /**
     * minimum number of threads
     */
    protected int min;

    /**
     * maximum number of threads
     */
    protected int max;

    /**
     * name of the thread
     */
    protected String name = null;

    protected int current_count = 0;

    /**
     * flag which indicates if all threads should
     * "suspend" themselves. We dont want to truely
     * suspend the threads because that is considered
     * unsafe
     */
    protected boolean notActive = true;


    protected int priority = Thread.NORM_PRIORITY;


    public boolean isValid() {
        return !destroyed && !in_destroy;
    }

    public void setPriority(int p) {
        this.priority = p;
    }

    /**
     * retrieve the minimum # of threads
     */
    public int getMinimum()
    {
        return min;
    }

    /**
     * retrieve the maximum # of threads
     */
    public int getMaximum() {
        return max;
    }

    /**
     * set the minumum # of threads
     */
    public synchronized void setMinimum(int num)
        throws IllegalArgumentException
    {
        setMinMax(num, max);
    }

    /**
     * set the maximum # of threads
     */
    public synchronized void setMaximum(int num)
        throws IllegalArgumentException
    {
        setMinMax(min, num);
    }

    public synchronized Hashtable getDebugState() {
        Hashtable ht = new Hashtable();
        ht.put("min", new Integer(min));
        ht.put("max", new Integer(max));
        ht.put("name", name);
        ht.put("current_count", new Integer(current_count));
        ht.put("nextThreadId", new Integer(nextThreadId));
        ht.put("notActive", Boolean.valueOf(notActive));
        ht.put("destroyed", Boolean.valueOf(destroyed));
        ht.put("in_destroy", Boolean.valueOf(in_destroy));
        ht.put("priority", new Integer(priority));
        if (current != null)  {
            ht.put("currentCnt", new Integer(current.size()) );
            Vector v = new Vector();
            Iterator itr = current.iterator();
            while (itr.hasNext()) {
                BasicRunnable runner = (BasicRunnable)itr.next();
                if (runner == null)
                    v.add("Runner is null");
                else
                    v.add(runner.getDebugState());
            }
            ht.put("current", v);
        } else {
            ht.put("currentCnt", "null" );
        }

        if (availIndx != null) {
            ht.put("availIndxCnt", new Integer(availIndx.size()) );
            Vector v = new Vector();
            Iterator itr = availIndx.iterator();
            while (itr.hasNext()) {
                Integer index = (Integer)itr.next();
                v.add(index);
            }
            ht.put("availIndx", v);
        } else {
            ht.put("availIndxCnt", "null" );
        }
        if (available != null) {
            ht.put("availableCnt", new Integer(available.size()) );
            Vector v = new Vector();
            Iterator itr = available.iterator();
            while (itr.hasNext()) {
                BasicRunnable runner = (BasicRunnable)itr.next();
                v.add(runner.getDebugState());
            }
            ht.put("available", v);
        } else {
            ht.put("availableCnt", "null" );
        }
        return ht;
    }


    public synchronized void setMinMax(int newmin, int newmax)
                                  throws IllegalArgumentException {
        if (in_destroy) {
            return; // nothing to do
        }
        if (newmin == -1) newmin = min;
        if (newmax == -1) newmax = max;

        if (newmin > newmax) {
            throw new IllegalArgumentException(
                  Globals.getBrokerResources().getKString(
                  BrokerResources.X_THREADPOOL_MIN_GT_MAX,
                  String.valueOf(newmin), String.valueOf(newmax)));
        }

        // OK first deal w/ destroying any threads over min

        int count = current.size();

        // now set the destroy behavior on existing threads
        // greater than max

        for (int i = newmax; i < max && i < count; i ++)  {
            BasicRunnable runner = (BasicRunnable)current.get(i);
            if (runner == null) continue;
            runner.setThreadBehavior(BasicRunnable.B_DESTROY_THREAD);
        }

        // now turn off timeout behavior on threads above newmin
        // && below min     
        for (int i = newmin; i > min && i > 0 && i < count; i --)  {
            BasicRunnable runner = (BasicRunnable)current.get(i);
            if (runner == null) continue;
            runner.setThreadBehavior(BasicRunnable.B_STAY_RUNNING);
        }

        // now turn on timeout behavior on threads > newmin     
        for (int i = newmin; i < max && i < count; i ++)  {
            BasicRunnable runner = (BasicRunnable)current.get(i);
            if (runner == null) continue;
            runner.setThreadBehavior(BasicRunnable.B_TIMEOUT_THREAD);
        }

        // finally, redo the list of available indexes
        LinkedHashSet list = new LinkedHashSet();
        for (int i = 0; i < count; i ++)  {
            Object obj = current.get(i);
            if (obj == null) // empty
                list.add(new Integer(i));
        }
        // set the values
        min = newmin;
        max = newmax;
        availIndx = list;

    }

    public synchronized void debug() {
        String info =   "\n"
                      + "--------------------------------------------\n"
                      + " DUMPING THREAD POOL " + this + "\n"
                      + "--------------------------------------------\n"
                      + "[min, max] = [" + min + "," + max + "]\n"
                      + "---- threads ----\n"
                      + "#\tAvailable\thash\tRunner\n";
        for (int i = 0; i < max; i ++) {
            BasicRunnable runner = null;
            if (i < current.size()) {
                runner = (BasicRunnable)current.get(i);
            }
            if (runner != null) {
                info += i + "\t" + available.contains(runner) + "\t"
                     + Long.toHexString(runner.hashCode())
                     + "\t" +runner  +"\n";
            } else {
                continue;
            }
        }
        info +=  "--------------------------------------------\n"
                + "DONE DUMPING THREAD POOL\n"
                + "--------------------------------------------\n\n";
        logger.log(Logger.DEBUG, info);
    }

    /**
     * returns the current number of "threads" in the thread pool
     */
    public synchronized int getThreadNum() {
        return current_count;
    }

    /**
     * returns the # of assigned threads
     */
    public synchronized int getAssignedCnt() {
        return current_count - available.size() - availIndx.size();
    }

    /**
     * Create a thread pool of the passed in name with
     * a minimum and maximum number of threads.
     *
     * @param name the name of the thread pool
     * @param min the minimum number of threads
     * @param max the maximum number of threads
     */
    public ThreadPool(String name, int min, int max,  RunnableFactory runfac) {
        if (DEBUG) {
            String args[] = {name, String.valueOf(min), String.valueOf(max)};
            logger.log(Logger.DEBUG,
                "ThreadPool: Creating Thread Pool({0}) = [ {1}, {2} ]",
                 args);
        }
  this.min = min;
        this.max  = max;
        tgroup = new MyThreadGroup("ThreadPool(" +name+ ")");
  this.name = name;
        availIndx = new LinkedHashSet();
        current = new ArrayList();
        available = new LinkedList();
        this.runfac = runfac;
        this.current_count = 0;
        this.nextThreadId = 0;

    }

    /**
     * start threads operation. By default, when the thread pool
     * is created,  the notActive flag is true, so the threads
     * are not operating
     */
    public  void start() {
  // for now .. does the same as resume
        resume();
    }

    /**
     * retrieve the state of the notActive flag
     *
     * @return the notActive flag
     */
    public synchronized boolean isSuspended() {
  return notActive;
    }

// suspending doesnt happen immediately .. the current operation
// WILL complete before the pool is suspended
//

    /**
     * suspend threads operation.<P>
     * Threads will <B>not</B> be immediately suspended, instead
     * they will go into a "wait" state as soon as they finish
     * processing the current operation.
     *
     * This method is not synchronized, because we dont really care
     * what the state of the flag is at a given time .. it will always
     * be picked up on the next iteration of the run method.
     */
    public void suspend() {
        if (DEBUG) {
            logger.log(Logger.DEBUG,
                "ThreadPool:  SUSPENDING ThreadPool({0})",
                 name);
        }
        if (in_destroy) {
            return; // nothing to do
        }
        notActive = true;

        ArrayList copy = null;
        synchronized (this) {
            copy = new ArrayList(current);
        }

        for (int i = 0; i < copy.size(); i ++) {
            BasicRunnable runner = (BasicRunnable)copy.get(i);
            if (runner != null) {
                runner.suspend();
            }
        }           
    }



    /**
     * resume threads operation.<P>
     * Threads will notified (which will wake them up) and then
     * the threads will continue operation
     */
    public  void resume() {
        if (DEBUG) {
            logger.log(Logger.DEBUG,
                "ThreadPool:  RESUMING ThreadPool({0})", name);
        }
        if (in_destroy) {
            return; // nothing to do
        }
        notActive = false;

        ArrayList copy = null;
        synchronized (this) {
            copy = new ArrayList(current);
        }
        for (int i = 0; i < copy.size(); i ++) {
            BasicRunnable runner = (BasicRunnable)copy.get(i);
            if (runner != null) {
                runner.resume();
            }
        }           
        synchronized (this) {
            notifyAll(); // wake everyone up
        }
    }

    /**
     * suspends or resume threads operation.<P>
     * This just calls the appropriate suspend() or resume()
     * operation.
     * @see #suspend
     * @see #resume
     *
     * @param susp true if the pool should be suspended, false
     *        if the thread pool should be resumed
     */
    public void setSuspended(boolean susp) {
        if (susp)
            suspend();
        else
            resume();
    }

// XXX

    /**
     * get a runnable to use (its assigned when returned)
     */
    public  BasicRunnable getAvailRunnable(boolean wait) {

try {
        BasicRunnable runner = null;
        String how = null;
        while (!in_destroy && !destroyed) { // wait until something available
            synchronized (this) {
                if (!available.isEmpty()) {
                     how = "availableList";
                    runner = (BasicRunnable)available.removeFirst();
                } else if (!availIndx.isEmpty()) {
                    how = "existing index";
                    Iterator itr = availIndx.iterator();
                    int indx = ((Integer)itr.next()).intValue();
                    itr.remove();
                    runner = createNewThread(indx);
                } else if (nextThreadId < max) {
                    how = "new thread";
                    int indx = nextThreadId;
                    nextThreadId++;
                    runner = createNewThread(indx);
                } else {
                    return null; // nothing available
                }
            }
            if (runner == null) {
                logger.log(Logger.ERROR,
                    BrokerResources.E_INTERNAL_BROKER_ERROR,
                    how);
                continue;
            }
            synchronized(runner) {
                if (runner.available())
                    runner.setState(BasicRunnable.RUN_PREASSIGNED);
                else // getting destroyed
                    runner = null;
                    continue;
                }
            }
            if (runner != null) break;
            synchronized (this) {
                if (wait) {
                    try {
                        wait();
                    } catch (InterruptedException ex) {
                        break;
                    }
                } else {
                    break;
                }
            }
        }

        return runner;
} catch (Exception ex) {
ex.printStackTrace();
return null;
}
    }


    /**
     * create a new thread and add it to the thread list
     *
     * @throws ArrayIndexOutOfBoundsException if max threads has been reached
     */
    private synchronized BasicRunnable createNewThread(
            int indx)
        throws ArrayIndexOutOfBoundsException
    {
        if (indx >= max)
            throw new ArrayIndexOutOfBoundsException(
                Globals.getBrokerResources().getString(
                BrokerResources.X_INTERNAL_EXCEPTION, "Too many threads " +
                current_count + "," + max));
        BasicRunnable runner = null;
        if (indx < current.size()) { // get current if possible
            runner = (BasicRunnable)current.get(indx);
        }
        if (runner == null) {
            runner = runfac.getRunnable(indx, this);
            if (current.size() <= indx) {
                current.add(indx, runner);
            } else {
                current.set(indx, runner);
            }
        }

        runner.setState(BasicRunnable.RUN_READY);
        Thread thr = new MQThread(tgroup, runner, "Thread-"+name +"["
                +indx + "]");
        thr.setPriority(priority);
        if (indx >= min) {
            runner.setThreadBehavior(BasicRunnable.B_TIMEOUT_THREAD);
        }
        current_count ++;
        thr.start();
        return runner;
    }


    public synchronized void runnableDestroying(int indx) {
        if (indx >= current.size()) {
           logger.log(Logger.ERROR,
               BrokerResources.E_INTERNAL_BROKER_ERROR,
               " attempting to destroy unknown thread  " + indx);
            debug();
           return;
        }
        BasicRunnable r = (BasicRunnable)current.get(indx);
        current.set(indx, null);
        available.remove(r);
    }

    public synchronized void runnableExit(int indx) {
        if (in_destroy) {
            return; // nothing to do
        }
        current_count --;
        if (indx < min) {
            // recreate
            BasicRunnable runner = createNewThread(indx);
            available.add(runner);
        } else { // put on possibly recreate list
            if (indx > max) {
                // ignore
            } else { // stick on list
                availIndx.add(new Integer(indx));
            }
        }
        notify();
    }
   
    public  synchronized void releaseRunnable(BasicRunnable run) {
        if (in_destroy) {
            return; // nothing to do
        }

        if (run == null) {
            logger.log(Logger.WARNING, BrokerResources.E_INTERNAL_BROKER_ERROR,
                    "null basic runnable " + run);
            return;
        }
        // XXX - add logic for handling min/max
        int indx = run.getId();
        if (indx < min) { // put at front of list

            // XXX - DEBUG
            if (!available.contains(run)) {
                available.addFirst(run);
            }
        } else { // put at back of list
            if (!available.contains(run)) {
                available.addLast(run);
            }
        }
        notify();
    }

    /**
     * cleanly stops threads and destroys
     */
    public void destroy() {
        ArrayList copy = null;
        synchronized (this) {
           in_destroy = true; // prevents new threads
           copy = new ArrayList(current);
        }
        for (int i = 0; i < copy.size(); i ++) {
            BasicRunnable runner = (BasicRunnable)copy.get(i);
            if (runner != null) {
                runner.destroy();
            }
        }        
    }


    public void waitOnDestroy(long timeout)
    {
        destroy();


        ArrayList copy = null;
        synchronized (this) {
            copy = new ArrayList(current);
        }


        for (int i = 0; i < copy.size(); i ++) {
            BasicRunnable runner = (BasicRunnable)copy.get(i);
            if (runner == null) {
                continue;
            }
            if (runner.isBusy()) {
                runner.waitOnDestroy(timeout);
            }
            if (!runner.isDestroyed() && runner.isCritical())
                   logger.log(Logger.WARNING,
                            BrokerResources.W_CANNOT_DESTROY_OPERATION,
                            runner, String.valueOf(timeout));
        }        
        destroyed = true;
        synchronized (this) {
            notifyAll();
        }
    }


    public void handleException(Throwable thr) {
        logger.logStack(Logger.WARNING,
            BrokerResources.E_INTERNAL_BROKER_ERROR,
            "Unexpected Exception or Error", thr);
    }

    /**
     * this subclass of ThreadGroup handles making sure any exception
     * is correctly handled
     *
     * IF the exception is thread death .. the system will decide whether
     * or not to recreate the thread
     *
     * the exception/Error will always be logged
     *
     */
    public class MyThreadGroup extends ThreadGroup
    {
        public MyThreadGroup(String name) {
            super(name);
        }
        public void uncaughtException(Thread t, Throwable thr) {
            // notify ThreadPool that the thread is gone
            Globals.handleGlobalError(thr, "Unexpected thread pool error");
        }
    }


}

TOP

Related Classes of com.sun.messaging.jmq.jmsserver.util.pool.ThreadPool$MyThreadGroup

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.