VirtualBox

source: vbox/trunk/src/libs/xpcom18a4/xpcom/threads/nsThread.cpp@ 101982

Last change on this file since 101982 was 101982, checked in by vboxsync, 16 months ago

libs/xpcom: Convert PR_Sleep to RTThreadSleep/RTThreadYield, bugref:10545

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 12.6 KB
Line 
1/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 *
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
9 *
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
14 *
15 * The Original Code is mozilla.org code.
16 *
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 1998
20 * the Initial Developer. All Rights Reserved.
21 *
22 * Contributor(s):
23 *
24 * Alternatively, the contents of this file may be used under the terms of
25 * either of the GNU General Public License Version 2 or later (the "GPL"),
26 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 * in which case the provisions of the GPL or the LGPL are applicable instead
28 * of those above. If you wish to allow use of your version of this file only
29 * under the terms of either the GPL or the LGPL, and not to allow others to
30 * use your version of this file under the terms of the MPL, indicate your
31 * decision by deleting the provisions above and replace them with the notice
32 * and other provisions required by the GPL or the LGPL. If you do not delete
33 * the provisions above, a recipient may use your version of this file under
34 * the terms of any one of the MPL, the GPL or the LGPL.
35 *
36 * ***** END LICENSE BLOCK ***** */
37
38#include "nsThread.h"
39#include "prmem.h"
40#include "prlog.h"
41#include "nsAutoLock.h"
42
43#include <iprt/assert.h>
44
45RTTLS nsThread::kIThreadSelfIndex = NIL_RTTLS;
46static nsIThread *gMainThread = 0;
47
48#if defined(PR_LOGGING)
49//
50// Log module for nsIThread logging...
51//
52// To enable logging (see prlog.h for full details):
53//
54// set NSPR_LOG_MODULES=nsIThread:5
55// set NSPR_LOG_FILE=nspr.log
56//
57// this enables PR_LOG_DEBUG level information and places all output in
58// the file nspr.log
59//
60// gSocketLog is defined in nsSocketTransport.cpp
61//
62PRLogModuleInfo* nsIThreadLog = nsnull;
63
64#endif /* PR_LOGGING */
65
66////////////////////////////////////////////////////////////////////////////////
67
68nsThread::nsThread()
69 : mThread(nsnull), mDead(PR_FALSE), mStartLock(nsnull)
70{
71#if defined(PR_LOGGING)
72 //
73 // Initialize the global PRLogModule for nsIThread logging
74 // if necessary...
75 //
76 if (nsIThreadLog == nsnull) {
77 nsIThreadLog = PR_NewLogModule("nsIThread");
78 }
79#endif /* PR_LOGGING */
80
81 // enforce matching of constants to enums in prthread.h
82 NS_ASSERTION(int(nsIThread::PRIORITY_LOW) == int(PR_PRIORITY_LOW) &&
83 int(nsIThread::PRIORITY_NORMAL) == int(PRIORITY_NORMAL) &&
84 int(nsIThread::PRIORITY_HIGH) == int(PRIORITY_HIGH) &&
85 int(nsIThread::PRIORITY_URGENT) == int(PRIORITY_URGENT) &&
86 int(nsIThread::SCOPE_LOCAL) == int(PR_LOCAL_THREAD) &&
87 int(nsIThread::SCOPE_GLOBAL) == int(PR_GLOBAL_THREAD) &&
88 int(nsIThread::STATE_JOINABLE) == int(PR_JOINABLE_THREAD) &&
89 int(nsIThread::STATE_UNJOINABLE) == int(PR_UNJOINABLE_THREAD),
90 "Bad constant in nsIThread!");
91}
92
93nsThread::~nsThread()
94{
95 if (mStartLock)
96 PR_DestroyLock(mStartLock);
97
98 PR_LOG(nsIThreadLog, PR_LOG_DEBUG,
99 ("nsIThread %p destroyed\n", this));
100
101 // This code used to free the nsIThreadLog loginfo stuff
102 // Don't do that; loginfo structures are owned by nspr
103 // and would be freed if we ever called PR_Cleanup()
104 // see bug 142072
105}
106
107void
108nsThread::Main(void* arg)
109{
110 nsThread* self = (nsThread*)arg;
111
112 self->WaitUntilReadyToStartMain();
113
114 nsresult rv = NS_OK;
115 rv = self->RegisterThreadSelf();
116 NS_ASSERTION(rv == NS_OK, "failed to set thread self");
117
118 PR_LOG(nsIThreadLog, PR_LOG_DEBUG,
119 ("nsIThread %p start run %p\n", self, self->mRunnable.get()));
120 rv = self->mRunnable->Run();
121 NS_ASSERTION(NS_SUCCEEDED(rv), "runnable failed");
122
123#ifdef DEBUG
124 // Because a thread can die after gMainThread dies and takes nsIThreadLog with it,
125 // we need to check for it being null so that we don't crash on shutdown.
126 if (nsIThreadLog) {
127 PRThreadState state;
128 rv = self->GetState(&state);
129 PR_LOG(nsIThreadLog, PR_LOG_DEBUG,
130 ("nsIThread %p end run %p\n", self, self->mRunnable.get()));
131 }
132#endif
133
134 // explicitly drop the runnable now in case there are circular references
135 // between it and the thread object
136 self->mRunnable = nsnull;
137}
138
139DECLCALLBACK(void)
140nsThread::Exit(void* arg)
141{
142 nsThread* self = (nsThread*)arg;
143
144 if (self->mDead) {
145 NS_ERROR("attempt to Exit() thread twice");
146 return;
147 }
148
149 self->mDead = PR_TRUE;
150
151#if defined(PR_LOGGING)
152 if (nsIThreadLog) {
153 PR_LOG(nsIThreadLog, PR_LOG_DEBUG,
154 ("nsIThread %p exited\n", self));
155 }
156#endif
157 NS_RELEASE(self);
158}
159
160NS_METHOD
161nsThread::Create(nsISupports *aOuter, REFNSIID aIID, void **aResult)
162{
163 nsThread* thread = new nsThread();
164 if (!thread) return NS_ERROR_OUT_OF_MEMORY;
165 nsresult rv = thread->QueryInterface(aIID, aResult);
166 if (NS_FAILED(rv)) delete thread;
167 return rv;
168}
169
170NS_IMPL_THREADSAFE_ISUPPORTS1(nsThread, nsIThread)
171
172NS_IMETHODIMP
173nsThread::Join()
174{
175 // don't check for mDead here because nspr calls Exit (cleaning up
176 // thread-local storage) before they let us join with the thread
177
178 PR_LOG(nsIThreadLog, PR_LOG_DEBUG,
179 ("nsIThread %p start join\n", this));
180 if (!mThread)
181 return NS_ERROR_NOT_INITIALIZED;
182 PRStatus status = PR_JoinThread(mThread);
183 // XXX can't use NS_RELEASE here because the macro wants to set
184 // this to null (bad c++)
185 PR_LOG(nsIThreadLog, PR_LOG_DEBUG,
186 ("nsIThread %p end join\n", this));
187 if (status == PR_SUCCESS) {
188 NS_RELEASE_THIS(); // most likely the final release of this thread
189 return NS_OK;
190 }
191 else
192 return NS_ERROR_FAILURE;
193}
194
195NS_IMETHODIMP
196nsThread::GetPriority(PRThreadPriority *result)
197{
198 if (mDead)
199 return NS_ERROR_FAILURE;
200 if (!mThread)
201 return NS_ERROR_NOT_INITIALIZED;
202 *result = PR_GetThreadPriority(mThread);
203 return NS_OK;
204}
205
206NS_IMETHODIMP
207nsThread::SetPriority(PRThreadPriority value)
208{
209 if (mDead)
210 return NS_ERROR_FAILURE;
211 if (!mThread)
212 return NS_ERROR_NOT_INITIALIZED;
213 PR_SetThreadPriority(mThread, value);
214 return NS_OK;
215}
216
217NS_IMETHODIMP
218nsThread::Interrupt()
219{
220 if (mDead)
221 return NS_ERROR_FAILURE;
222 if (!mThread)
223 return NS_ERROR_NOT_INITIALIZED;
224 PRStatus status = PR_Interrupt(mThread);
225 return status == PR_SUCCESS ? NS_OK : NS_ERROR_FAILURE;
226}
227
228NS_IMETHODIMP
229nsThread::GetScope(PRThreadScope *result)
230{
231 if (mDead)
232 return NS_ERROR_FAILURE;
233 if (!mThread)
234 return NS_ERROR_NOT_INITIALIZED;
235 *result = PR_GetThreadScope(mThread);
236 return NS_OK;
237}
238
239NS_IMETHODIMP
240nsThread::GetState(PRThreadState *result)
241{
242 if (mDead)
243 return NS_ERROR_FAILURE;
244 if (!mThread)
245 return NS_ERROR_NOT_INITIALIZED;
246 *result = PR_GetThreadState(mThread);
247 return NS_OK;
248}
249
250NS_IMETHODIMP
251nsThread::GetPRThread(PRThread* *result)
252{
253 if (mDead) {
254 *result = nsnull;
255 return NS_ERROR_FAILURE;
256 }
257 *result = mThread;
258 return NS_OK;
259}
260
261NS_IMETHODIMP
262nsThread::Init(nsIRunnable* runnable,
263 PRUint32 stackSize,
264 PRThreadPriority priority,
265 PRThreadScope scope,
266 PRThreadState state)
267{
268 NS_ENSURE_ARG_POINTER(runnable);
269 mRunnable = runnable;
270
271 NS_ADDREF_THIS(); // released in nsThread::Exit
272 if (state == PR_JOINABLE_THREAD)
273 NS_ADDREF_THIS(); // released in nsThread::Join
274 mStartLock = PR_NewLock();
275 if (mStartLock == nsnull)
276 return NS_ERROR_OUT_OF_MEMORY;
277 PR_Lock(mStartLock);
278 mThread = PR_CreateThread(PR_USER_THREAD, Main, this,
279 priority, scope, state, stackSize);
280 PR_Unlock(mStartLock);
281 PR_LOG(nsIThreadLog, PR_LOG_DEBUG,
282 ("nsIThread %p created\n", this));
283
284 if (mThread == nsnull)
285 return NS_ERROR_OUT_OF_MEMORY;
286 return NS_OK;
287}
288
289/* readonly attribute nsIThread currentThread; */
290NS_IMETHODIMP
291nsThread::GetCurrentThread(nsIThread * *aCurrentThread)
292{
293 return GetIThread(PR_GetCurrentThread(), aCurrentThread);
294}
295
296/* void sleep (in PRUint32 msec); */
297NS_IMETHODIMP
298nsThread::Sleep(PRUint32 msec)
299{
300 if (PR_GetCurrentThread() != mThread)
301 return NS_ERROR_FAILURE;
302
303 if (RTThreadSleep(msec) != VINF_SUCCESS)
304 return NS_ERROR_FAILURE;
305
306 return NS_OK;
307}
308
309NS_COM nsresult
310NS_NewThread(nsIThread* *result,
311 nsIRunnable* runnable,
312 PRUint32 stackSize,
313 PRThreadState state,
314 PRThreadPriority priority,
315 PRThreadScope scope)
316{
317 nsresult rv;
318 nsThread* thread = new nsThread();
319 if (thread == nsnull)
320 return NS_ERROR_OUT_OF_MEMORY;
321 NS_ADDREF(thread);
322
323 rv = thread->Init(runnable, stackSize, priority, scope, state);
324 if (NS_FAILED(rv)) {
325 NS_RELEASE(thread);
326 return rv;
327 }
328
329 *result = thread;
330 return NS_OK;
331}
332
333NS_COM nsresult
334NS_NewThread(nsIThread* *result,
335 PRUint32 stackSize,
336 PRThreadState state,
337 PRThreadPriority priority,
338 PRThreadScope scope)
339{
340 nsThread* thread = new nsThread();
341 if (thread == nsnull)
342 return NS_ERROR_OUT_OF_MEMORY;
343 NS_ADDREF(thread);
344 *result = thread;
345 return NS_OK;
346}
347
348////////////////////////////////////////////////////////////////////////////////
349
350nsresult
351nsThread::RegisterThreadSelf()
352{
353 int vrc;
354
355 if (kIThreadSelfIndex == NIL_RTTLS) {
356 vrc = RTTlsAllocEx(&kIThreadSelfIndex, Exit);
357 if (RT_FAILURE(vrc)) return NS_ERROR_FAILURE;
358 }
359
360 vrc = RTTlsSet(kIThreadSelfIndex, this);
361 if (RT_FAILURE(vrc)) return NS_ERROR_FAILURE;
362
363 return NS_OK;
364}
365
366void
367nsThread::WaitUntilReadyToStartMain()
368{
369 PR_Lock(mStartLock);
370 PR_Unlock(mStartLock);
371 PR_DestroyLock(mStartLock);
372 mStartLock = nsnull;
373}
374
375NS_COM nsresult
376nsIThread::GetCurrent(nsIThread* *result)
377{
378 return GetIThread(PR_GetCurrentThread(), result);
379}
380
381NS_COM nsresult
382nsIThread::GetIThread(PRThread* prthread, nsIThread* *result)
383{
384 nsThread* thread;
385
386 if (nsThread::kIThreadSelfIndex == NIL_RTTLS) {
387 int vrc = RTTlsAllocEx(&nsThread::kIThreadSelfIndex, nsThread::Exit);
388 if (RT_FAILURE(vrc)) return NS_ERROR_FAILURE;
389 }
390
391 thread = (nsThread*)RTTlsGet(nsThread::kIThreadSelfIndex);
392 if (thread == nsnull) {
393 // if the current thread doesn't have an nsIThread associated
394 // with it, make one
395 thread = new nsThread();
396 if (thread == nsnull)
397 return NS_ERROR_OUT_OF_MEMORY;
398 NS_ADDREF(thread); // released by Exit
399 thread->SetPRThread(prthread);
400 nsresult rv = thread->RegisterThreadSelf();
401 if (NS_FAILED(rv)) return rv;
402 }
403 NS_ADDREF(thread);
404 *result = thread;
405 return NS_OK;
406}
407
408NS_COM nsresult
409nsIThread::SetMainThread()
410{
411 // strictly speaking, it could be set twice. but practically speaking,
412 // it's almost certainly an error if it is
413 if (gMainThread != 0) {
414 NS_ERROR("Setting main thread twice?");
415 return NS_ERROR_FAILURE;
416 }
417 return GetCurrent(&gMainThread);
418}
419
420NS_COM nsresult
421nsIThread::GetMainThread(nsIThread **result)
422{
423 NS_ASSERTION(result, "bad result pointer");
424 if (gMainThread == 0)
425 return NS_ERROR_FAILURE;
426 *result = gMainThread;
427 NS_ADDREF(gMainThread);
428 return NS_OK;
429}
430
431NS_COM PRBool
432nsIThread::IsMainThread()
433{
434 if (gMainThread == 0)
435 return PR_TRUE;
436
437 PRThread *theMainThread;
438 gMainThread->GetPRThread(&theMainThread);
439 return theMainThread == PR_GetCurrentThread();
440}
441
442void
443nsThread::Shutdown()
444{
445 if (gMainThread) {
446 // XXX nspr doesn't seem to be calling the main thread's destructor
447 // callback, so let's help it out:
448 nsThread::Exit(NS_STATIC_CAST(nsThread*, gMainThread));
449 nsrefcnt cnt;
450 NS_RELEASE2(gMainThread, cnt);
451 NS_WARN_IF_FALSE(cnt == 0, "Main thread being held past XPCOM shutdown.");
452 gMainThread = nsnull;
453
454 int vrc = RTTlsFree(kIThreadSelfIndex);
455 AssertRC(vrc); RT_NOREF(vrc);
456 kIThreadSelfIndex = NIL_RTTLS;
457 }
458}
459
460////////////////////////////////////////////////////////////////////////////////
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette