2005-01-31 Zoltan Varga <vargaz@freemail.hu>
[mono.git] / mcs / class / System.Runtime.Remoting / System.Runtime.Remoting.Channels / RemotingThreadPool.cs
1 //
2 // System.Runtime.Remoting.Channels.RemotingThreadPool.cs
3 //
4 // Author: Lluis Sanchez Gual (lluis@ximian.com)
5 //
6 // 2005 (C) Copyright, Novell, Inc.
7 //
8
9 //
10 // Permission is hereby granted, free of charge, to any person obtaining
11 // a copy of this software and associated documentation files (the
12 // "Software"), to deal in the Software without restriction, including
13 // without limitation the rights to use, copy, modify, merge, publish,
14 // distribute, sublicense, and/or sell copies of the Software, and to
15 // permit persons to whom the Software is furnished to do so, subject to
16 // the following conditions:
17 // 
18 // The above copyright notice and this permission notice shall be
19 // included in all copies or substantial portions of the Software.
20 // 
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
25 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
26 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
27 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28 //
29
30 using System;
31 using System.Collections;
32 using System.Threading;
33
34 namespace System.Runtime.Remoting.Channels
35 {
36         internal class RemotingThreadPool
37         {
38                 const int ThreadLimit = 1000;
39                 const int ThreadWaitTime = 20000; //ms
40                 const int PoolGrowDelay = 500; // ms
41                 const int MinThreads = 3;
42
43                 int freeThreads;
44                 int poolUsers;
45                 Queue workItems = new Queue ();
46                 AutoResetEvent threadDone = new AutoResetEvent (false);
47                 ArrayList runningThreads = new ArrayList ();
48                 bool stopped = false;
49                 
50                 static object globalLock = new object ();
51                 static RemotingThreadPool sharedPool;
52                 
53                 public static RemotingThreadPool GetSharedPool ()
54                 {
55                         lock (globalLock) {
56                                 if (sharedPool == null)
57                                         sharedPool = new RemotingThreadPool ();
58                                 sharedPool.poolUsers++;
59                         }
60                         return sharedPool;
61                 }
62                 
63                 public void Free ()
64                 {
65                         lock (globalLock) {
66                                 if (--poolUsers > 0)
67                                         return;
68                                 
69                                 lock (workItems) {
70                                         stopped = true;
71                                         threadDone.Set ();
72                                         workItems.Clear ();
73                                         foreach (Thread t in runningThreads)
74                                                 t.Abort ();
75                                         runningThreads.Clear ();
76                                 }
77                                 if (this == sharedPool)
78                                         sharedPool = null;
79                         }
80                 }
81                 
82                 public bool RunThread (ThreadStart threadStart)
83                 {
84                         lock (workItems) {
85                                 if (stopped)
86                                         throw new RemotingException ("Server channel stopped.");
87                                         
88                                 if (freeThreads > 0) {
89                                         freeThreads--;
90                                         workItems.Enqueue (threadStart);
91                                         Monitor.Pulse (workItems);
92                                         return true;
93                                 } else if (runningThreads.Count < MinThreads) {
94                                         workItems.Enqueue (threadStart);
95                                         StartPoolThread ();
96                                         return true;
97                                 }
98                         }
99                         
100                         // Try again some ms later, and if there are still no free threads,
101                         // then create a new one
102
103                         threadDone.WaitOne (PoolGrowDelay, false);
104                         
105                         lock (workItems) {
106                                 if (stopped)
107                                         throw new RemotingException ("Server channel stopped.");
108                                 
109                                 if (freeThreads > 0) {
110                                         freeThreads--;
111                                         workItems.Enqueue (threadStart);
112                                         Monitor.Pulse (workItems);
113                                 } else {
114                                         if (runningThreads.Count >= ThreadLimit)
115                                                 return false;
116                                         workItems.Enqueue (threadStart);
117                                         StartPoolThread ();
118                                 }
119                         }
120                         return true;
121                 }
122                 
123                 void StartPoolThread ()
124                 {
125                         Thread thread = new Thread (new ThreadStart (PoolThread));
126                         runningThreads.Add (thread);
127                         thread.IsBackground = true;
128                         thread.Start ();
129                 }
130                 
131                 void PoolThread ()
132                 {
133                         while (true) {
134                                 ThreadStart work = null;
135                                 do {
136                                         lock (workItems) {
137                                                 if (workItems.Count > 0)
138                                                         work = (ThreadStart) workItems.Dequeue ();
139                                                 else {
140                                                         freeThreads ++;
141                                                         threadDone.Set ();
142                                                         if (!Monitor.Wait (workItems, ThreadWaitTime)) {
143                                                                 // Maybe it timed out when the work was being queued.
144                                                                 if (workItems.Count > 0) {
145                                                                         work = (ThreadStart) workItems.Dequeue ();
146                                                                 } else {
147                                                                         freeThreads --;
148                                                                         if (freeThreads == 0) threadDone.Reset ();
149                                                                         runningThreads.Remove (Thread.CurrentThread);
150                                                                         return;
151                                                                 }
152                                                         }
153                                                 }
154                                         }
155                                 } while (work == null);
156                                 try {
157                                         work ();
158                                 } catch {
159                                         // Can't do anything with the exception
160                                 }
161                         }
162                 }
163         }
164 }
165