aa99a6554872244ad712418160a8fe74aa1ab473
[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                  
49 #if TARGET_JVM
50                 volatile 
51 #endif
52                 bool stopped = false;
53                 
54                 static object globalLock = new object ();
55                 static RemotingThreadPool sharedPool;
56                 
57                 public static RemotingThreadPool GetSharedPool ()
58                 {
59                         lock (globalLock) {
60                                 if (sharedPool == null)
61                                         sharedPool = new RemotingThreadPool ();
62                                 sharedPool.poolUsers++;
63                         }
64                         return sharedPool;
65                 }
66                 
67                 public void Free ()
68                 {
69                         lock (globalLock) {
70                                 if (--poolUsers > 0)
71                                         return;
72                                 
73                                 lock (workItems) {
74                                         stopped = true;
75                                         threadDone.Set ();
76                                         workItems.Clear ();
77                                         foreach (Thread t in runningThreads)
78 #if !TARGET_JVM
79                                                 t.Abort ();
80 #else
81                                                 t.Interrupt();
82 #endif
83                                         runningThreads.Clear ();
84                                 }
85                                 if (this == sharedPool)
86                                         sharedPool = null;
87                         }
88                 }
89                 
90                 public bool RunThread (ThreadStart threadStart)
91                 {
92                         lock (workItems) {
93                                 if (stopped)
94                                         throw new RemotingException ("Server channel stopped.");
95                                         
96                                 if (freeThreads > 0) {
97                                         freeThreads--;
98                                         workItems.Enqueue (threadStart);
99                                         Monitor.Pulse (workItems);
100                                         return true;
101                                 } else if (runningThreads.Count < MinThreads) {
102                                         workItems.Enqueue (threadStart);
103                                         StartPoolThread ();
104                                         return true;
105                                 }
106                         }
107                         
108                         // Try again some ms later, and if there are still no free threads,
109                         // then create a new one
110
111                         threadDone.WaitOne (PoolGrowDelay, false);
112                         
113                         lock (workItems) {
114                                 if (stopped)
115                                         throw new RemotingException ("Server channel stopped.");
116                                 
117                                 if (freeThreads > 0) {
118                                         freeThreads--;
119                                         workItems.Enqueue (threadStart);
120                                         Monitor.Pulse (workItems);
121                                 } else {
122                                         if (runningThreads.Count >= ThreadLimit)
123                                                 return false;
124                                         workItems.Enqueue (threadStart);
125                                         StartPoolThread ();
126                                 }
127                         }
128                         return true;
129                 }
130                 
131                 void StartPoolThread ()
132                 {
133                         Thread thread = new Thread (new ThreadStart (PoolThread));
134                         runningThreads.Add (thread);
135                         thread.IsBackground = true;
136                         thread.Start ();
137                 }
138                 
139                 void PoolThread ()
140                 {
141 #if !TARGET_JVM
142                         while (true) {
143 #else
144                         while (!stopped) 
145                         {
146 #endif
147                                 ThreadStart work = null;
148                                 do {
149                                         lock (workItems) {
150                                                 if (workItems.Count > 0)
151                                                         work = (ThreadStart) workItems.Dequeue ();
152                                                 else {
153                                                         freeThreads ++;
154                                                         threadDone.Set ();
155                                                         if (!Monitor.Wait (workItems, ThreadWaitTime)) {
156                                                                 // Maybe it timed out when the work was being queued.
157                                                                 if (workItems.Count > 0) {
158                                                                         work = (ThreadStart) workItems.Dequeue ();
159                                                                 } else {
160                                                                         freeThreads --;
161                                                                         if (freeThreads == 0) threadDone.Reset ();
162                                                                         runningThreads.Remove (Thread.CurrentThread);
163                                                                         return;
164                                                                 }
165                                                         }
166                                                 }
167                                         }
168                                 } while (work == null);
169                                 try {
170                                         work ();
171                                 }
172                                 catch (Exception ex)
173                                 {
174 #if DEBUG
175                                         Console.WriteLine("The exception was caught during RemotingThreadPool.PoolThread - work: {0}, {1}", ex.GetType(), ex.Message);
176 #endif
177                                 }
178                         }
179                 }
180         }
181 }
182