/* * CVS identifier: * * $Id: ThreadPool.java,v 1.9 2002/05/22 15:00:55 grosbois Exp $ * * Class: ThreadPool * * Description: A pool of threads * * * * COPYRIGHT: * * This software module was originally developed by Raphaël Grosbois and * Diego Santa Cruz (Swiss Federal Institute of Technology-EPFL); Joel * Askelöf (Ericsson Radio Systems AB); and Bertrand Berthelot, David * Bouchard, Félix Henry, Gerard Mozelle and Patrice Onno (Canon Research * Centre France S.A) in the course of development of the JPEG2000 * standard as specified by ISO/IEC 15444 (JPEG 2000 Standard). This * software module is an implementation of a part of the JPEG 2000 * Standard. Swiss Federal Institute of Technology-EPFL, Ericsson Radio * Systems AB and Canon Research Centre France S.A (collectively JJ2000 * Partners) agree not to assert against ISO/IEC and users of the JPEG * 2000 Standard (Users) any of their rights under the copyright, not * including other intellectual property rights, for this software module * with respect to the usage by ISO/IEC and Users of this software module * or modifications thereof for use in hardware or software products * claiming conformance to the JPEG 2000 Standard. Those intending to use * this software module in hardware or software products are advised that * their use may infringe existing patents. The original developers of * this software module, JJ2000 Partners and ISO/IEC assume no liability * for use of this software module or modifications thereof. No license * or right to this software module is granted for non JPEG 2000 Standard * conforming products. JJ2000 Partners have full right to use this * software module for his/her own purpose, assign or donate this * software module to any third party and to inhibit third parties from * using this software module for non JPEG 2000 Standard conforming * products. This copyright notice must be included in all copies or * derivative works of this software module. * * Copyright (c) 1999/2000 JJ2000 Partners. * */ using System; namespace CSJ2K.j2k.util { /// This class implements a thread pool. The thread pool contains a set of /// threads which can be given work to do. /// ///

If the Java Virtual Machine (JVM) uses native threads, then the /// different threads will be able to execute in different processors in /// parallel on multiprocessors machines. However, under some JVMs and /// operating systems using native threads is not sufficient to allow the JVM /// access to multiple processors. This is the case when native threads are /// implemented using POSIX threads on lightweight processes /// (i.e. PTHREAD_SCOPE_PROCESS sopce scheduling), which is the case on most /// UNIX operating systems. In order to do provide access to multiple /// processors it is necessary to set the concurrency level to the number of /// processors or slightly higher. This can be achieved by setting the Java /// system property with the name defined by CONCURRENCY_PROP_NAME to some /// non-negative number. This will make use of the 'NativeServices' class and /// supporting native libraries. See 'NativeServices' for details. See /// 'CONCURRENCY_PROP_NAME' for the name of the property. /// ///

Initially the thread pool contains a user specified number of idle /// threads. Idle threads can be given a target which is run. While running the /// target the thread temporarily leaves the idle list. When the target /// finishes, it joins the idle list again, waiting for a new target. When a /// target is finished a thread can be notified on a particular object that is /// given as a lock. /// ///

Jobs can be submitted using Runnable interfaces, using the 'runTarget()' /// methods. When the job is submitted, an idle thread will be obtained, the /// 'run()' method of the 'Runnable' interface will be executed and when it /// completes the thread will be returned to the idle list. In general the /// 'run()' method should complete in a rather short time, so that the threds /// of the pool are not starved. /// ///

If using the non-asynchronous calls to 'runTarget()', it is important /// that any target's 'run()' method, or any method called from it, does not /// use non-asynchronous calls to 'runTarget()' on the same thread pool where /// it was started. Otherwise this could create a dead-lock when there are not /// enough idle threads. /// ///

The pool also has a global error and runtime exception condition (one /// for 'Error' and one for 'RuntimeException'). If a target's 'run()' method /// throws an 'Error' or 'RuntimeException' the corresponding exception /// condition is set and the exception object saved. In any subsequent call to /// 'checkTargetErrors()' the saved exception object is thrown. Likewise, if a /// target's 'run()' method throws any other subclass of 'Throwable' a new /// 'RuntimeException' is created and saved. It will be thrown on a subsequent /// call to 'checkTargetErrors()'. If more than one exception occurs between /// calls to 'checkTargetErrors()' only the last one is saved. Any 'Error' /// condition has precedence on all 'RuntimeException' conditions. The threads /// in the pool are unaffected by any exceptions thrown by targets. /// ///

The only exception to the above is the 'ThreadDeath' exception. If a /// target's 'run()' method throws the 'ThreadDeath' exception a warning /// message is printed and the exception is propagated, which will terminate /// the thread in which it occurs. This could lead to instabilities of the /// pool. The 'ThreadDeath' exception should never be thrown by the program. It /// is thrown by the Java(TM) Virtual Machine when Thread.stop() is /// called. This method is deprecated and should never be called. /// ///

All the threads in the pool are "daemon" threads and will automatically /// terminate when no daemon threads are running. /// ///

/// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// public class ThreadPool { /// Returns the size of the pool. That is the number of threads in this /// pool (idle + busy). /// /// /// The pool's size. /// /// /// virtual public int Size { get { return idle.Length; } } /// The name of the property that sets the concurrency level: /// jj2000.j2k.util.ThreadPool.concurrency /// public const System.String CONCURRENCY_PROP_NAME = "jj2000.j2k.util.ThreadPool.concurrency"; /// The array of idle threads and the lock for the manipulation of the /// idle thread list. /// private ThreadPoolThread[] idle; /// The number of idle threads private int nidle; /// The name of the pool private System.String poolName; /// The priority for the pool private int poolPriority; /// The last error thrown by a target. Null if none // NOTE: needs to be volatile, so that only one copy exits in memory private volatile System.ApplicationException targetE; /// The last runtime exception thrown by a target. Null if none // NOTE: needs to be volatile, so that only one copy exits in memory private volatile System.SystemException targetRE; //UPGRADE_NOTE: Field 'EnclosingInstance' was added to class 'ThreadPoolThread' to access its enclosing instance. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1019'" /// The threads that are managed by the pool. /// /// internal class ThreadPoolThread:SupportClass.ThreadClass { private void InitBlock(ThreadPool enclosingInstance) { this.enclosingInstance = enclosingInstance; } private ThreadPool enclosingInstance; public ThreadPool Enclosing_Instance { get { return enclosingInstance; } } private IThreadRunnable target; private System.Object lock_Renamed; private bool doNotifyAll; /// Creates a ThreadPoolThread object, setting its name according to /// the given 'idx', daemon type and the priority to the one of the /// pool. /// /// /// The index of this thread in the pool /// /// /// The name of the thread /// /// public ThreadPoolThread(ThreadPool enclosingInstance, int idx, System.String name):base(name) { InitBlock(enclosingInstance); IsBackground = true; //UPGRADE_TODO: The differences in the type of parameters for method 'java.lang.Thread.setPriority' may cause compilation errors. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1092'" Priority = (System.Threading.ThreadPriority) Enclosing_Instance.poolPriority; } /// The method that is run by the thread. This method first joins the /// idle state in the pool and then enters an infinite loop. In this /// loop it waits until a target to run exists and runs it. Once the /// target's run() method is done it re-joins the idle state and /// notifies the waiting lock object, if one exists. /// ///

An interrupt on this thread has no effect other than forcing a /// check on the target. Normally the target is checked every time the /// thread is woken up by notify, no interrupts should be done. /// ///

Any exception thrown by the target's 'run()' method is catched /// and this thread is not affected, except for 'ThreadDeath'. If a /// 'ThreadDeath' exception is catched a warning message is printed by /// the 'FacilityManager' and the exception is propagated up. For /// exceptions which are subclasses of 'Error' or 'RuntimeException' /// the corresponding error condition is set and this thread is not /// affected. For any other exceptions a new 'RuntimeException' is /// created and the error condition is set, this thread is not affected. /// ///

override public void Run() { // Join the idle threads list Enclosing_Instance.putInIdleList(this); // Permanently lock the object while running so that target can // not be changed until we are waiting again. While waiting for a // target the lock is released. lock (this) { while (true) { // Wait until we get a target while (target == null) { try { System.Threading.Monitor.Wait(this); } catch (System.Threading.ThreadInterruptedException) { } } // Run the target and catch all possible errors try { target.Run(); } //UPGRADE_NOTE: Exception 'java.lang.ThreadDeath' was converted to 'System.ApplicationException' which has different behavior. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1100'" catch (System.Threading.ThreadAbortException td) { // We have been instructed to abruptly terminate // the thread, which should never be done. This can // cause another thread, or the system, to lock. FacilityManager.getMsgLogger().printmsg(CSJ2K.j2k.util.MsgLogger_Fields.WARNING, "Thread.stop() called on a ThreadPool " + "thread or ThreadDeath thrown. This is " + "deprecated. Lock-up might occur."); throw td; } catch (System.ApplicationException e) { Enclosing_Instance.targetE = e; } catch (System.SystemException re) { Enclosing_Instance.targetRE = re; } //UPGRADE_NOTE: Exception 'java.lang.Throwable' was converted to 'System.Exception' which has different behavior. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1100'" catch (System.Exception) { // A totally unexpected error has occurred // (Thread.stop(Throwable) has been used, which should // never be. Enclosing_Instance.targetRE = new System.SystemException("Unchecked exception " + "thrown by target's " + "run() method in pool " + Enclosing_Instance.poolName + "."); } // Join idle threads Enclosing_Instance.putInIdleList(this); // Release the target and notify lock (i.e. wakeup) target = null; if (lock_Renamed != null) { lock (lock_Renamed) { if (doNotifyAll) { System.Threading.Monitor.PulseAll(lock_Renamed); } else { System.Threading.Monitor.Pulse(lock_Renamed); } } } } } } /// Assigns a target to this thread, with an optional notify lock and a /// notify mode. The another target is currently running the method /// will block until it terminates. After setting the new target the /// runner thread will be wakenup and execytion will start. /// /// /// The runnable object containing the 'run()' method to /// run. /// /// /// An object on which notify will be called once the /// target's run method has finished. A thread to be notified should be /// waiting on that object. If null no thread is notified. /// /// /// If true 'notifyAll()', instead of 'notify()', will /// be called on tghe lock. /// /// //UPGRADE_NOTE: Synchronized keyword was removed from method 'setTarget'. Lock expression was added. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1027'" internal virtual void setTarget(IThreadRunnable target, System.Object lock_Renamed, bool notifyAll) { lock (this) { // Set the target this.target = target; this.lock_Renamed = lock_Renamed; doNotifyAll = notifyAll; // Wakeup the thread System.Threading.Monitor.Pulse(this); } } } /// Creates a new thread pool of the given size, thread priority and pool /// name. /// ///

If the Java system property of the name defined by /// 'CONCURRENCY_PROP_NAME' is set, then an attempt will be made to load /// the library that supports concurrency setting (see /// 'NativeServices'). If that succeds the concurrency level will be set to /// the specified value. Otherwise a warning is printed. /// ///

/// The size of the pool (number of threads to create in the /// pool). /// /// /// The priority to give to the threads in the pool. If /// less than 'Thread.MIN_PRIORITY' it will be the same as the priority of /// the calling thread. /// /// /// The name of the pool. If null a default generic name is /// chosen. /// /// /// /// /// /// /// /// public ThreadPool(int size, int priority, System.String name) { int i; ThreadPoolThread t; //System.String prop; //int clevel; // Initialize variables checking for special cases if (size <= 0) { throw new System.ArgumentException("Pool must be of positive size"); } if (priority < (int) System.Threading.ThreadPriority.Lowest) { poolPriority = (System.Int32) SupportClass.ThreadClass.Current().Priority; } else { poolPriority = (priority < (int) System.Threading.ThreadPriority.Highest)?priority:(int) System.Threading.ThreadPriority.Highest; } if (name == null) { poolName = "Anonymous ThreadPool"; } else { poolName = name; } // Allocate internal variables idle = new ThreadPoolThread[size]; nidle = 0; // Create and start the threads for (i = 0; i < size; i++) { t = new ThreadPoolThread(this, i, poolName + "-" + i); t.Start(); } } /// Runs the run method of the specified target in an idle thread of this /// pool. When the target's run method completes, the thread waiting on the /// lock object is notified, if any. If there is currently no idle thread /// the method will block until a thread of the pool becomes idle or the /// calling thread is interrupted. /// ///

This method is the same as runTarget(t,l,true,false). /// ///

/// The target. The 'run()' method of this object will be run in /// an idle thread of the pool. /// /// /// The lock object. A thread waiting on the lock of the 'l' /// object will be notified, through the 'notify()' call, when the target's /// run method completes. If null no thread is notified. /// /// /// True if the target was submitted to some thread. False if no /// idle thread could be found and the target was not submitted for /// execution. /// /// /// public virtual bool runTarget(IThreadRunnable t, System.Object l) { return runTarget(t, l, false, false); } /// Runs the run method of the specified target in an idle thread of this /// pool. When the target's run method completes, the thread waiting on the /// lock object is notified, if any. If there is currently no idle thread /// and the asynchronous mode is not used the method will block until a /// thread of the pool becomes idle or the calling thread is /// interrupted. If the asynchronous mode is used then the method will not /// block and will return false. /// ///

This method is the same as runTarget(t,l,async,false). /// ///

/// The target. The 'run()' method of this object will be run in /// an idle thread of the pool. /// /// /// The lock object. A thread waiting on the lock of the 'l' /// object will be notified, through the 'notify()' call, when the target's /// run method completes. If null no thread is notified. /// /// /// If true the asynchronous mode will be used. /// /// /// True if the target was submitted to some thread. False if no /// idle thread could be found and the target was not submitted for /// execution. /// /// /// public virtual bool runTarget(IThreadRunnable t, System.Object l, bool async) { return runTarget(t, l, async, false); } /// Runs the run method of the specified target in an idle thread of this /// pool. When the target's run method completes, the thread waiting on the /// lock object is notified, if any. If there is currently no idle thread /// and the asynchronous mode is not used the method will block until a /// thread of the pool becomes idle or the calling thread is /// interrupted. If the asynchronous mode is used then the method will not /// block and will return false. /// /// /// The target. The 'run()' method of this object will be run in /// an idle thread of the pool. /// /// /// The lock object. A thread waiting on the lock of the 'l' /// object will be notified, through the 'notify()' call, when the target's /// run method completes. If null no thread is notified. /// /// /// If true the asynchronous mode will be used. /// /// /// If true, threads waiting on the lock of the 'l' object /// will be notified trough the 'notifyAll()' instead of the normal /// 'notify()' call. This is not normally needed. /// /// /// True if the target was submitted to some thread. False if no /// idle thread could be found and the target was not submitted for /// execution. /// /// /// public virtual bool runTarget(IThreadRunnable t, System.Object l, bool async, bool notifyAll) { ThreadPoolThread runner; // The thread to run the target // Get a thread to run runner = getIdle(async); // If no runner return failure if (runner == null) return false; // Set the runner runner.setTarget(t, l, notifyAll); return true; } /// Checks that no error or runtime exception in any target have occurred /// so far. If an error or runtime exception has occurred in a target's run /// method they are thrown by this method. /// /// /// If an error condition has been thrown by a target /// 'run()' method. /// /// /// If a runtime exception has been thrown by a /// target 'run()' method. /// /// public virtual void checkTargetErrors() { // Check for Error if (targetE != null) throw targetE; // Check for RuntimeException if (targetRE != null) throw targetRE; } /// Clears the current target error conditions, if any. Note that a thread /// in the pool might have set the error conditions since the last check /// and that those error conditions will be lost. Likewise, before /// returning from this method another thread might set the error /// conditions. There is no guarantee that no error conditions exist when /// returning from this method. /// ///

In order to ensure that no error conditions exist when returning /// from this method cooperation from the targets and the thread using this /// pool is necessary (i.e. currently no targets running or waiting to /// run). /// ///

public virtual void clearTargetErrors() { // Clear the error and runtime exception conditions targetE = null; targetRE = null; } /// Puts the thread 't' in the idle list. The thread 't' should be in fact /// idle and ready to accept a new target when it joins the idle list. /// ///

An idle thread that is already in the list should never add itself /// to the list before it is removed. For efficiency reasons there is no /// check to see if the thread is already in the list of idle threads. /// ///

If the idle list was empty 'notify()' will be called on the 'idle' /// array, to wake up a thread that might be waiting (within the /// 'getIdle()' method) on an idle thread to become available. /// ///

/// The thread to put in the idle list. /// /// private void putInIdleList(ThreadPoolThread t) { // NOTE: if already in idle => catastrophe! (should be OK since // // this is private method) // Lock the idle array to avoid races with 'getIdle()' lock (idle) { idle[nidle] = t; nidle++; // If idle array was empty wakeup any waiting threads. if (nidle == 1) System.Threading.Monitor.Pulse(idle); } } /// Returns and idle thread and removes it from the list of idle /// threads. In asynchronous mode it will immediately return an idle /// thread, or null if none is available. In non-asynchronous mode it will /// block until a thread of the pool becomes idle or the calling thread is /// interrupted. /// ///

If in non-asynchronous mode and there are currently no idle threads /// available the calling thread will wait on the 'idle' array lock, until /// notified by 'putInIdleList()' that an idle thread might have become /// available. /// ///

/// If true asynchronous mode is used. /// /// /// An idle thread of the pool, that has been removed from the idle /// list, or null if none is available. /// /// private ThreadPoolThread getIdle(bool async) { // Lock the idle array to avoid races with 'putInIdleList()' lock (idle) { if (async) { // In asynchronous mode just return null if no idle thread if (nidle == 0) return null; } else { // In synchronous mode wait until a thread becomes idle while (nidle == 0) { try { System.Threading.Monitor.Wait(idle); } catch (System.Threading.ThreadInterruptedException) { // If we were interrupted just return null return null; } } } // Decrease the idle count and return one of the idle threads nidle--; return idle[nidle]; } } } }