Tue May 13 15:34:29 CEST 2003 Paolo Molaro <lupus@ximian.com>
[mono.git] / mcs / class / corlib / System.Threading / ThreadPool.cs
index 3f652d0321908f838f7c47797f26120cd301e84c..e99b7691b83c3ebff4c165ca30925c637f55d2b4 100755 (executable)
 //
-// System.Threading.ThreadPool.cs
+// System.Threading.ThreadPool
 //
 // Author:
+//   Patrik Torstensson
 //   Dick Porter (dick@ximian.com)
 //
 // (C) Ximian, Inc.  http://www.ximian.com
+// (C) Patrik Torstensson
 //
+using System;
+using System.Collections;
 
+namespace System.Threading {
+       /// <summary> (Patrik T notes)
+       /// This threadpool is focused on saving resources not giving max performance. 
+       /// 
+       /// Note, this class is not perfect but it works. ;-) Should also replace
+       /// the queue with an internal one (performance)
+       /// 
+       /// This class should also use a specialized queue to increase performance..
+       /// </summary>
+       /// 
+       public sealed class ThreadPool {
+               internal struct ThreadPoolWorkItem {
+                       public WaitCallback _CallBack;
+                       public object _Context;
+               }
 
-namespace System.Threading
-{
-       public sealed class ThreadPool
-       {
-               private ThreadPool () {}
+               private int _ThreadTimeout;
+
+               private long _MaxThreads;
+               private long _CurrentThreads;
+               private long _ThreadsInUse;
+               private long _RequestInQueue;
+               private long _ThreadCreateTriggerRequests;
+
+               private Thread _MonitorThread;
+               private Queue _RequestQueue;
+
+               private ArrayList _Threads;
+               private ManualResetEvent _DataInQueue; 
+
+               static ThreadPool _Threadpool;
+
+               static ThreadPool() {
+                       _Threadpool = new ThreadPool();
+               }
+
+               private ThreadPool() {
+                       // 30 sec timeout default
+                       _ThreadTimeout = 30 * 1000; 
+
+                       // Used to signal that there is data in the queue
+                       _DataInQueue = new ManualResetEvent(false);
+         
+                       _Threads = ArrayList.Synchronized(new ArrayList());
+
+                       // Holds requests..
+                       _RequestQueue = Queue.Synchronized(new Queue(128));
+
+                       // TODO: This should be 2 x number of CPU:s in the box
+                       _MaxThreads = 16;
+                       _CurrentThreads = 0;
+                       _RequestInQueue = 0;
+                       _ThreadsInUse = 0;
+                       _ThreadCreateTriggerRequests = 5;
+
+                       // TODO: This temp starts one thread, remove this..
+                       CheckIfStartThread();
+
+                       // Keeps track of requests in the queue and increases the number of threads if needed
+
+                       // PT: Disabled - causes problems during shutdown
+                       //_MonitorThread = new Thread(new ThreadStart(MonitorThread));
+                       //_MonitorThread.Start();
+               }
+
+               internal void RemoveThread() {
+                       Interlocked.Decrement(ref _CurrentThreads);
+                       _Threads.Remove(Thread.CurrentThread);
+               }
+
+               internal void CheckIfStartThread() {
+                       bool bCreateThread = false;
+
+                       if (_CurrentThreads == 0) {
+                               bCreateThread = true;
+                       }
+
+                       if ((   _MaxThreads == -1 || _CurrentThreads < _MaxThreads) && 
+                               _ThreadsInUse > 0 && 
+                               _RequestInQueue >= _ThreadCreateTriggerRequests) {
+                               bCreateThread = true;
+                       }
+
+                       if (bCreateThread) {
+                               Interlocked.Increment(ref _CurrentThreads);
+      
+                               Thread Start = new Thread(new ThreadStart(WorkerThread));
+                               Start.IsThreadPoolThreadInternal = true;
+                               Start.IsBackground = true;
+                               Start.Start();
+            
+                               _Threads.Add(Start);
+                       }
+               }
+
+               internal void AddItem(ref ThreadPoolWorkItem Item) {
+                       if (Interlocked.Increment(ref _RequestInQueue) == 1) {
+                               _DataInQueue.Set();
+                       }
+
+                       _RequestQueue.Enqueue(Item);
+               }
+
+               // Work Thread main function
+               internal void WorkerThread() {
+                       bool bWaitForData = true;
+
+                       while (true) {
+                               if (bWaitForData) {
+                                       if (!_DataInQueue.WaitOne(_ThreadTimeout, false)) {
+                                               // Keep one thread running
+                                               if (_CurrentThreads > 1) {
+                                                       // timeout
+                                                       RemoveThread();
+                                                       return;
+                                               }
+                                       }
+                               }
+
+                               Interlocked.Increment(ref _ThreadsInUse);
+
+                               // TODO: Remove when we know how to stop the watch thread
+                               CheckIfStartThread();
+
+                               try {
+                                       ThreadPoolWorkItem oItem = (ThreadPoolWorkItem) _RequestQueue.Dequeue();
+
+                                       if (Interlocked.Decrement(ref _RequestInQueue) == 0) {
+                                               _DataInQueue.Reset();
+                                       }
+
+                                       oItem._CallBack(oItem._Context);
+                               }
+                               catch (InvalidOperationException) {
+                                       // Queue empty
+                                       bWaitForData = true;
+                               }
+                               catch (ThreadAbortException) {
+                                       // We will leave here.. (thread abort can't be handled)
+                                       RemoveThread();
+                               }
+                               finally {
+                                       Interlocked.Decrement(ref _ThreadsInUse);
+                               }
+                       }
+               }
+               
+               /* This is currently not in use
+                
+               internal void MonitorThread() {
+                       while (true) {
+                       if (_DataInQueue.WaitOne ()) {
+                               CheckIfStartThread();
+                       }
+
+                       Thread.Sleep(500);
+                       }
+               }
+               
+               */
+               internal bool QueueUserWorkItemInternal(WaitCallback callback) {
+                       return QueueUserWorkItem(callback, null);
+               }
+
+               internal bool QueueUserWorkItemInternal(WaitCallback callback, object context) {
+                       ThreadPoolWorkItem Item = new ThreadPoolWorkItem();
+
+                       Item._CallBack = callback;
+                       Item._Context = context;
+
+                       AddItem(ref Item);
+
+                       // LAMESPEC: Return value? should use exception here if anything goes wrong
+                       return true;
+               }
 
-               [MonoTODO]
                public static bool BindHandle(IntPtr osHandle) {
-                       // FIXME
-                       return(false);
+                       throw new NotSupportedException("This is a win32 specific method, not supported Mono");
                }
 
-               [MonoTODO]
                public static bool QueueUserWorkItem(WaitCallback callback) {
-                       // FIXME
-                       return(false);
+                       return _Threadpool.QueueUserWorkItemInternal(callback);
                }
 
-               [MonoTODO]
-               public static bool QueueUserWorkItem(WaitCallback callback,
-                                                    object state) {
-                       // FIXME
-                       return(false);
+               public static bool QueueUserWorkItem(WaitCallback callback, object state) {
+                       return _Threadpool.QueueUserWorkItemInternal(callback, state);
                }
 
-               [MonoTODO]
-               public static RegisteredWaitHandle RegisterWaitForSingleObject(WaitHandle waitObject,
-                                                                              WaitOrTimerCallback callback,
-                                                                              object state,
-                                                                              int millisecondsTimeOutInterval,
-                                                                              bool executeOnlyOnce) {
-                       if(millisecondsTimeOutInterval < -1) {
+               public static bool UnsafeQueueUserWorkItem(WaitCallback callback, object state) {
+                       return _Threadpool.QueueUserWorkItemInternal(callback, state);
+               }
+
+               public static RegisteredWaitHandle RegisterWaitForSingleObject(WaitHandle waitObject, WaitOrTimerCallback callback, object state, int millisecondsTimeOutInterval, bool executeOnlyOnce) {
+                       if (millisecondsTimeOutInterval < -1) {
                                throw new ArgumentOutOfRangeException("timeout < -1");
                        }
-                       // FIXME
-                       return(null);
+                       return RegisterWaitForSingleObject (waitObject, callback, state, TimeSpan.FromMilliseconds (Convert.ToDouble(millisecondsTimeOutInterval)), executeOnlyOnce);
                }
 
-               [MonoTODO]
-               public static RegisteredWaitHandle RegisterWaitForSingleObject(WaitHandle waitObject,
-                                                                              WaitOrTimerCallback callback,
-                                                                              object state,
-                                                                              long millisecondsTimeOutInterval,
-                                                                              bool executeOnlyOnce) {
-                       if(millisecondsTimeOutInterval < -1) {
+               public static RegisteredWaitHandle RegisterWaitForSingleObject(WaitHandle waitObject, WaitOrTimerCallback callback, object state, long millisecondsTimeOutInterval, bool executeOnlyOnce) {
+                       if (millisecondsTimeOutInterval < -1) {
                                throw new ArgumentOutOfRangeException("timeout < -1");
                        }
-                       // FIXME
-                       return(null);
+               
+                       return RegisterWaitForSingleObject (waitObject, callback, state, TimeSpan.FromMilliseconds (Convert.ToDouble(millisecondsTimeOutInterval)), executeOnlyOnce);
                }
 
-               [MonoTODO]
                public static RegisteredWaitHandle RegisterWaitForSingleObject(WaitHandle waitObject, WaitOrTimerCallback callback, object state, TimeSpan timeout, bool executeOnlyOnce) {
                        // LAMESPEC: I assume it means "timeout" when it says "millisecondsTimeOutInterval"
-                       if(timeout.Milliseconds < -1) {
+                       int ms=Convert.ToInt32(timeout.TotalMilliseconds);
+                       
+                       if (ms < -1) {
                                throw new ArgumentOutOfRangeException("timeout < -1");
                        }
-                       if(timeout.Milliseconds > Int32.MaxValue) {
+                       if (ms > Int32.MaxValue) {
                                throw new NotSupportedException("timeout too large");
                        }
-                       // FIXME
-                       return(null);
-               }
 
-               [CLSCompliant(false)][MonoTODO]
-               public static RegisteredWaitHandle RegisterWaitForSingleObject(WaitHandle waitObject, WaitOrTimerCallback callback, object state, uint millisecondsTimeOutInterval, bool executeOnlyOnce) {
-                       // FIXME
-                       return(null);
+                       RegisteredWaitHandle waiter = new RegisteredWaitHandle (waitObject, callback, state, timeout, executeOnlyOnce);
+                       _Threadpool.QueueUserWorkItemInternal (new WaitCallback(waiter.Wait), null);
+                       return waiter;
                }
 
-               [MonoTODO]
-               public static bool UnsafeQueueUserWorkItem(WaitCallback callback, object state) {
-                       // FIXME
-                       return(false);
+               [CLSCompliant(false)]
+               public static RegisteredWaitHandle RegisterWaitForSingleObject(WaitHandle waitObject, WaitOrTimerCallback callback, object state, uint millisecondsTimeOutInterval, bool executeOnlyOnce) {
+                       return RegisterWaitForSingleObject (waitObject, callback, state, TimeSpan.FromMilliseconds (Convert.ToDouble(millisecondsTimeOutInterval)), executeOnlyOnce);
                }
 
                [MonoTODO]
                public static RegisteredWaitHandle UnsafeRegisterWaitForSingleObject(WaitHandle waitObject, WaitOrTimerCallback callback, object state, int millisecondsTimeOutInterval, bool executeOnlyOnce) {
-                       // FIXME
-                       return(null);
+                       throw new NotImplementedException();
                }
 
                [MonoTODO]
                public static RegisteredWaitHandle UnsafeRegisterWaitForSingleObject(WaitHandle waitObject, WaitOrTimerCallback callback, object state, long millisecondsTimeOutInterval, bool executeOnlyOnce) {
-                       // FIXME
-                       return(null);
+                       throw new NotImplementedException();
                }
 
                [MonoTODO]
                public static RegisteredWaitHandle UnsafeRegisterWaitForSingleObject(WaitHandle waitObject, WaitOrTimerCallback callback, object state, TimeSpan timeout, bool executeOnlyOnce) {
-                       // FIXME
-                       return(null);
+                       throw new NotImplementedException();
                }
 
                [CLSCompliant(false)][MonoTODO]
                public static RegisteredWaitHandle UnsafeRegisterWaitForSingleObject(WaitHandle waitObject, WaitOrTimerCallback callback, object state, uint millisecondsTimeOutInterval, bool executeOnlyOnce) {
-                       // FIXME
-                       return(null);
+                       throw new NotImplementedException();
                }
        }
 }