1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim:set ts=2 sw=2 sts=2 et cindent: */
3 : /* ***** BEGIN LICENSE BLOCK *****
4 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 : *
6 : * The contents of this file are subject to the Mozilla Public License Version
7 : * 1.1 (the "License"); you may not use this file except in compliance with
8 : * the License. You may obtain a copy of the License at
9 : * http://www.mozilla.org/MPL/
10 : *
11 : * Software distributed under the License is distributed on an "AS IS" basis,
12 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 : * for the specific language governing rights and limitations under the
14 : * License.
15 : *
16 : * The Original Code is Mozilla code.
17 : *
18 : * The Initial Developer of the Original Code is Google Inc.
19 : * Portions created by the Initial Developer are Copyright (C) 2006
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : * Darin Fisher <darin@meer.net>
24 : *
25 : * Alternatively, the contents of this file may be used under the terms of
26 : * either the GNU General Public License Version 2 or later (the "GPL"), or
27 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 : * in which case the provisions of the GPL or the LGPL are applicable instead
29 : * of those above. If you wish to allow use of your version of this file only
30 : * under the terms of either the GPL or the LGPL, and not to allow others to
31 : * use your version of this file under the terms of the MPL, indicate your
32 : * decision by deleting the provisions above and replace them with the notice
33 : * and other provisions required by the GPL or the LGPL. If you do not delete
34 : * the provisions above, a recipient may use your version of this file under
35 : * the terms of any one of the MPL, the GPL or the LGPL.
36 : *
37 : * ***** END LICENSE BLOCK ***** */
38 :
39 : #include "nsIClassInfoImpl.h"
40 : #include "nsThreadPool.h"
41 : #include "nsThreadManager.h"
42 : #include "nsThread.h"
43 : #include "nsMemory.h"
44 : #include "nsAutoPtr.h"
45 : #include "prinrval.h"
46 : #include "prlog.h"
47 :
48 : using namespace mozilla;
49 :
50 : #ifdef PR_LOGGING
51 1464 : static PRLogModuleInfo *sLog = PR_NewLogModule("nsThreadPool");
52 : #endif
53 : #define LOG(args) PR_LOG(sLog, PR_LOG_DEBUG, args)
54 :
55 : // DESIGN:
56 : // o Allocate anonymous threads.
57 : // o Use nsThreadPool::Run as the main routine for each thread.
58 : // o Each thread waits on the event queue's monitor, checking for
59 : // pending events and rescheduling itself as an idle thread.
60 :
61 : #define DEFAULT_THREAD_LIMIT 4
62 : #define DEFAULT_IDLE_THREAD_LIMIT 1
63 : #define DEFAULT_IDLE_THREAD_TIMEOUT PR_SecondsToInterval(60)
64 :
65 3613 : NS_IMPL_THREADSAFE_ADDREF(nsThreadPool)
66 5172 : NS_IMPL_THREADSAFE_RELEASE(nsThreadPool)
67 : NS_IMPL_CLASSINFO(nsThreadPool, NULL, nsIClassInfo::THREADSAFE,
68 : NS_THREADPOOL_CID)
69 1873 : NS_IMPL_QUERY_INTERFACE3_CI(nsThreadPool, nsIThreadPool, nsIEventTarget,
70 0 : nsIRunnable)
71 0 : NS_IMPL_CI_INTERFACE_GETTER2(nsThreadPool, nsIThreadPool, nsIEventTarget)
72 :
73 1559 : nsThreadPool::nsThreadPool()
74 : : mThreadLimit(DEFAULT_THREAD_LIMIT)
75 : , mIdleThreadLimit(DEFAULT_IDLE_THREAD_LIMIT)
76 1559 : , mIdleThreadTimeout(DEFAULT_IDLE_THREAD_TIMEOUT)
77 : , mIdleCount(0)
78 3118 : , mShutdown(false)
79 : {
80 1559 : }
81 :
82 3118 : nsThreadPool::~nsThreadPool()
83 : {
84 1559 : Shutdown();
85 1559 : }
86 :
87 : nsresult
88 1257 : nsThreadPool::PutEvent(nsIRunnable *event)
89 : {
90 : // Avoid spawning a new thread while holding the event queue lock...
91 :
92 1257 : bool spawnThread = false;
93 : {
94 2514 : ReentrantMonitorAutoEnter mon(mEvents.GetReentrantMonitor());
95 :
96 1257 : LOG(("THRD-P(%p) put [%d %d %d]\n", this, mIdleCount, mThreads.Count(),
97 : mThreadLimit));
98 1257 : NS_ASSERTION(mIdleCount <= (PRUint32) mThreads.Count(), "oops");
99 :
100 : // Make sure we have a thread to service this event.
101 1257 : if (mIdleCount == 0 && mThreads.Count() < (PRInt32) mThreadLimit)
102 181 : spawnThread = true;
103 :
104 1257 : mEvents.PutEvent(event);
105 : }
106 :
107 1257 : LOG(("THRD-P(%p) put [spawn=%d]\n", this, spawnThread));
108 1257 : if (!spawnThread)
109 1076 : return NS_OK;
110 :
111 362 : nsCOMPtr<nsIThread> thread;
112 181 : nsThreadManager::get()->NewThread(0,
113 : nsIThreadManager::DEFAULT_STACK_SIZE,
114 181 : getter_AddRefs(thread));
115 181 : NS_ENSURE_STATE(thread);
116 :
117 181 : bool killThread = false;
118 : {
119 362 : ReentrantMonitorAutoEnter mon(mEvents.GetReentrantMonitor());
120 181 : if (mThreads.Count() < (PRInt32) mThreadLimit) {
121 181 : mThreads.AppendObject(thread);
122 : } else {
123 0 : killThread = true; // okay, we don't need this thread anymore
124 : }
125 : }
126 181 : LOG(("THRD-P(%p) put [%p kill=%d]\n", this, thread.get(), killThread));
127 181 : if (killThread) {
128 0 : thread->Shutdown();
129 : } else {
130 181 : thread->Dispatch(this, NS_DISPATCH_NORMAL);
131 : }
132 :
133 181 : return NS_OK;
134 : }
135 :
136 : void
137 28 : nsThreadPool::ShutdownThread(nsIThread *thread)
138 : {
139 28 : LOG(("THRD-P(%p) shutdown async [%p]\n", this, thread));
140 :
141 : // This method is responsible for calling Shutdown on |thread|. This must be
142 : // done from some other thread, so we use the main thread of the application.
143 :
144 28 : NS_ASSERTION(!NS_IsMainThread(), "wrong thread");
145 :
146 56 : nsRefPtr<nsIRunnable> r = NS_NewRunnableMethod(thread, &nsIThread::Shutdown);
147 28 : NS_DispatchToMainThread(r);
148 28 : }
149 :
150 : NS_IMETHODIMP
151 181 : nsThreadPool::Run()
152 : {
153 181 : LOG(("THRD-P(%p) enter\n", this));
154 :
155 362 : nsCOMPtr<nsIThread> current;
156 181 : nsThreadManager::get()->GetCurrentThread(getter_AddRefs(current));
157 :
158 181 : bool shutdownThreadOnExit = false;
159 181 : bool exitThread = false;
160 181 : bool wasIdle = false;
161 : PRIntervalTime idleSince;
162 :
163 362 : nsCOMPtr<nsIThreadPoolListener> listener;
164 : {
165 362 : ReentrantMonitorAutoEnter mon(mEvents.GetReentrantMonitor());
166 181 : listener = mListener;
167 : }
168 :
169 181 : if (listener) {
170 0 : listener->OnThreadCreated();
171 : }
172 :
173 2985 : do {
174 5970 : nsCOMPtr<nsIRunnable> event;
175 : {
176 5970 : ReentrantMonitorAutoEnter mon(mEvents.GetReentrantMonitor());
177 2985 : if (!mEvents.GetPendingEvent(getter_AddRefs(event))) {
178 1728 : PRIntervalTime now = PR_IntervalNow();
179 1728 : PRIntervalTime timeout = PR_MillisecondsToInterval(mIdleThreadTimeout);
180 :
181 : // If we are shutting down, then don't keep any idle threads
182 1728 : if (mShutdown) {
183 153 : exitThread = true;
184 : } else {
185 1575 : if (wasIdle) {
186 : // if too many idle threads or idle for too long, then bail.
187 325 : if (mIdleCount > mIdleThreadLimit || (now - idleSince) >= timeout)
188 0 : exitThread = true;
189 : } else {
190 : // if would be too many idle threads...
191 1250 : if (mIdleCount == mIdleThreadLimit) {
192 28 : exitThread = true;
193 : } else {
194 1222 : ++mIdleCount;
195 1222 : idleSince = now;
196 1222 : wasIdle = true;
197 : }
198 : }
199 : }
200 :
201 1728 : if (exitThread) {
202 181 : if (wasIdle)
203 153 : --mIdleCount;
204 181 : shutdownThreadOnExit = mThreads.RemoveObject(current);
205 : } else {
206 1547 : PRIntervalTime delta = timeout - (now - idleSince);
207 1547 : LOG(("THRD-P(%p) waiting [%d]\n", this, delta));
208 1547 : mon.Wait(delta);
209 : }
210 1257 : } else if (wasIdle) {
211 1069 : wasIdle = false;
212 1069 : --mIdleCount;
213 : }
214 : }
215 2985 : if (event) {
216 1257 : LOG(("THRD-P(%p) running [%p]\n", this, event.get()));
217 1257 : event->Run();
218 : }
219 2985 : } while (!exitThread);
220 :
221 181 : if (listener) {
222 0 : listener->OnThreadShuttingDown();
223 : }
224 :
225 181 : if (shutdownThreadOnExit) {
226 28 : ShutdownThread(current);
227 : }
228 :
229 181 : LOG(("THRD-P(%p) leave\n", this));
230 181 : return NS_OK;
231 : }
232 :
233 : NS_IMETHODIMP
234 1261 : nsThreadPool::Dispatch(nsIRunnable *event, PRUint32 flags)
235 : {
236 1261 : LOG(("THRD-P(%p) dispatch [%p %x]\n", this, event, flags));
237 :
238 1261 : NS_ENSURE_STATE(!mShutdown);
239 :
240 1257 : if (flags & DISPATCH_SYNC) {
241 0 : nsCOMPtr<nsIThread> thread;
242 0 : nsThreadManager::get()->GetCurrentThread(getter_AddRefs(thread));
243 0 : NS_ENSURE_STATE(thread);
244 :
245 : nsRefPtr<nsThreadSyncDispatch> wrapper =
246 0 : new nsThreadSyncDispatch(thread, event);
247 0 : PutEvent(wrapper);
248 :
249 0 : while (wrapper->IsPending())
250 0 : NS_ProcessNextEvent(thread);
251 : } else {
252 1257 : NS_ASSERTION(flags == NS_DISPATCH_NORMAL, "unexpected dispatch flags");
253 1257 : PutEvent(event);
254 : }
255 1257 : return NS_OK;
256 : }
257 :
258 : NS_IMETHODIMP
259 0 : nsThreadPool::IsOnCurrentThread(bool *result)
260 : {
261 : // No one should be calling this method. If this assertion gets hit, then we
262 : // need to think carefully about what this method should be returning.
263 0 : NS_NOTREACHED("implement me");
264 :
265 0 : *result = false;
266 0 : return NS_OK;
267 : }
268 :
269 : NS_IMETHODIMP
270 3118 : nsThreadPool::Shutdown()
271 : {
272 6236 : nsCOMArray<nsIThread> threads;
273 6236 : nsCOMPtr<nsIThreadPoolListener> listener;
274 : {
275 6236 : ReentrantMonitorAutoEnter mon(mEvents.GetReentrantMonitor());
276 3118 : mShutdown = true;
277 3118 : mon.NotifyAll();
278 :
279 3118 : threads.AppendObjects(mThreads);
280 3118 : mThreads.Clear();
281 :
282 : // Swap in a null listener so that we release the listener at the end of
283 : // this method. The listener will be kept alive as long as the other threads
284 : // that were created when it was set.
285 3118 : mListener.swap(listener);
286 : }
287 :
288 : // It's important that we shutdown the threads while outside the event queue
289 : // monitor. Otherwise, we could end up dead-locking.
290 :
291 3271 : for (PRInt32 i = 0; i < threads.Count(); ++i)
292 153 : threads[i]->Shutdown();
293 :
294 3118 : return NS_OK;
295 : }
296 :
297 : NS_IMETHODIMP
298 0 : nsThreadPool::GetThreadLimit(PRUint32 *value)
299 : {
300 0 : *value = mThreadLimit;
301 0 : return NS_OK;
302 : }
303 :
304 : NS_IMETHODIMP
305 1559 : nsThreadPool::SetThreadLimit(PRUint32 value)
306 : {
307 3118 : ReentrantMonitorAutoEnter mon(mEvents.GetReentrantMonitor());
308 1559 : mThreadLimit = value;
309 1559 : if (mIdleThreadLimit > mThreadLimit)
310 0 : mIdleThreadLimit = mThreadLimit;
311 1559 : mon.NotifyAll(); // wake up threads so they observe this change
312 1559 : return NS_OK;
313 : }
314 :
315 : NS_IMETHODIMP
316 0 : nsThreadPool::GetIdleThreadLimit(PRUint32 *value)
317 : {
318 0 : *value = mIdleThreadLimit;
319 0 : return NS_OK;
320 : }
321 :
322 : NS_IMETHODIMP
323 1559 : nsThreadPool::SetIdleThreadLimit(PRUint32 value)
324 : {
325 3118 : ReentrantMonitorAutoEnter mon(mEvents.GetReentrantMonitor());
326 1559 : mIdleThreadLimit = value;
327 1559 : if (mIdleThreadLimit > mThreadLimit)
328 1426 : mIdleThreadLimit = mThreadLimit;
329 1559 : mon.NotifyAll(); // wake up threads so they observe this change
330 1559 : return NS_OK;
331 : }
332 :
333 : NS_IMETHODIMP
334 0 : nsThreadPool::GetIdleThreadTimeout(PRUint32 *value)
335 : {
336 0 : *value = mIdleThreadTimeout;
337 0 : return NS_OK;
338 : }
339 :
340 : NS_IMETHODIMP
341 1559 : nsThreadPool::SetIdleThreadTimeout(PRUint32 value)
342 : {
343 3118 : ReentrantMonitorAutoEnter mon(mEvents.GetReentrantMonitor());
344 1559 : mIdleThreadTimeout = value;
345 1559 : mon.NotifyAll(); // wake up threads so they observe this change
346 1559 : return NS_OK;
347 : }
348 :
349 : NS_IMETHODIMP
350 0 : nsThreadPool::GetListener(nsIThreadPoolListener** aListener)
351 : {
352 0 : ReentrantMonitorAutoEnter mon(mEvents.GetReentrantMonitor());
353 0 : NS_IF_ADDREF(*aListener = mListener);
354 0 : return NS_OK;
355 : }
356 :
357 : NS_IMETHODIMP
358 0 : nsThreadPool::SetListener(nsIThreadPoolListener* aListener)
359 : {
360 0 : nsCOMPtr<nsIThreadPoolListener> swappedListener(aListener);
361 : {
362 0 : ReentrantMonitorAutoEnter mon(mEvents.GetReentrantMonitor());
363 0 : mListener.swap(swappedListener);
364 : }
365 0 : return NS_OK;
366 4392 : }
|