Back to index

lightning-sunbird  0.9+nobinonly
nsThread.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either of the GNU General Public License Version 2 or later (the "GPL"),
00026  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 #include "nsThread.h"
00039 #include "prmem.h"
00040 #include "prlog.h"
00041 #include "nsAutoLock.h"
00042 
00043 PRUintn nsThread::kIThreadSelfIndex = 0;
00044 static nsIThread *gMainThread = 0;
00045 
00046 #if defined(PR_LOGGING)
00047 //
00048 // Log module for nsIThread logging...
00049 //
00050 // To enable logging (see prlog.h for full details):
00051 //
00052 //    set NSPR_LOG_MODULES=nsIThread:5
00053 //    set NSPR_LOG_FILE=nspr.log
00054 //
00055 // this enables PR_LOG_DEBUG level information and places all output in
00056 // the file nspr.log
00057 //
00058 // gSocketLog is defined in nsSocketTransport.cpp
00059 //
00060 PRLogModuleInfo* nsIThreadLog = nsnull;
00061 
00062 #endif /* PR_LOGGING */
00063 
00065 
00066 nsThread::nsThread()
00067     : mThread(nsnull), mDead(PR_FALSE), mStartLock(nsnull)
00068 {
00069 #if defined(PR_LOGGING)
00070     //
00071     // Initialize the global PRLogModule for nsIThread logging 
00072     // if necessary...
00073     //
00074     if (nsIThreadLog == nsnull) {
00075         nsIThreadLog = PR_NewLogModule("nsIThread");
00076     }
00077 #endif /* PR_LOGGING */
00078 
00079     // enforce matching of constants to enums in prthread.h
00080     NS_ASSERTION(int(nsIThread::PRIORITY_LOW)     == int(PR_PRIORITY_LOW) &&
00081                  int(nsIThread::PRIORITY_NORMAL)  == int(PRIORITY_NORMAL) &&
00082                  int(nsIThread::PRIORITY_HIGH)    == int(PRIORITY_HIGH) &&
00083                  int(nsIThread::PRIORITY_URGENT)  == int(PRIORITY_URGENT) &&
00084                  int(nsIThread::SCOPE_LOCAL)      == int(PR_LOCAL_THREAD) &&
00085                  int(nsIThread::SCOPE_GLOBAL)     == int(PR_GLOBAL_THREAD) &&
00086                  int(nsIThread::STATE_JOINABLE)   == int(PR_JOINABLE_THREAD) &&
00087                  int(nsIThread::STATE_UNJOINABLE) == int(PR_UNJOINABLE_THREAD),
00088                  "Bad constant in nsIThread!");
00089 }
00090 
00091 nsThread::~nsThread()
00092 {
00093     if (mStartLock)
00094         PR_DestroyLock(mStartLock);
00095 
00096     PR_LOG(nsIThreadLog, PR_LOG_DEBUG,
00097            ("nsIThread %p destroyed\n", this));
00098 
00099     // This code used to free the nsIThreadLog loginfo stuff
00100     // Don't do that; loginfo structures are owned by nspr
00101     // and would be freed if we ever called PR_Cleanup()
00102     // see bug 142072
00103 }
00104 
00105 void
00106 nsThread::Main(void* arg)
00107 {
00108     nsThread* self = (nsThread*)arg;
00109 
00110     self->WaitUntilReadyToStartMain();
00111 
00112     nsresult rv = NS_OK;
00113     rv = self->RegisterThreadSelf();
00114     NS_ASSERTION(rv == NS_OK, "failed to set thread self");
00115 
00116     PR_LOG(nsIThreadLog, PR_LOG_DEBUG,
00117            ("nsIThread %p start run %p\n", self, self->mRunnable.get()));
00118     rv = self->mRunnable->Run();
00119     NS_ASSERTION(NS_SUCCEEDED(rv), "runnable failed");
00120 
00121 #ifdef DEBUG
00122     // Because a thread can die after gMainThread dies and takes nsIThreadLog with it,
00123     // we need to check for it being null so that we don't crash on shutdown.
00124     if (nsIThreadLog) {
00125         PRThreadState state;
00126         rv = self->GetState(&state);
00127         PR_LOG(nsIThreadLog, PR_LOG_DEBUG,
00128                ("nsIThread %p end run %p\n", self, self->mRunnable.get()));
00129     }
00130 #endif
00131 
00132     // explicitly drop the runnable now in case there are circular references
00133     // between it and the thread object
00134     self->mRunnable = nsnull;
00135 }
00136 
00137 void
00138 nsThread::Exit(void* arg)
00139 {
00140     nsThread* self = (nsThread*)arg;
00141 
00142     if (self->mDead) {
00143         NS_ERROR("attempt to Exit() thread twice");
00144         return;
00145     }
00146 
00147     self->mDead = PR_TRUE;
00148 
00149 #if defined(PR_LOGGING)
00150     if (nsIThreadLog) {
00151         PR_LOG(nsIThreadLog, PR_LOG_DEBUG,
00152                ("nsIThread %p exited\n", self));
00153     }
00154 #endif
00155     NS_RELEASE(self);
00156 }
00157 
00158 NS_METHOD
00159 nsThread::Create(nsISupports *aOuter, REFNSIID aIID, void **aResult)
00160 {
00161     nsThread* thread = new nsThread();
00162     if (!thread) return NS_ERROR_OUT_OF_MEMORY;
00163     nsresult rv = thread->QueryInterface(aIID, aResult);
00164     if (NS_FAILED(rv)) delete thread;
00165     return rv;
00166 }
00167 
00168 NS_IMPL_THREADSAFE_ISUPPORTS1(nsThread, nsIThread)
00169 
00170 NS_IMETHODIMP
00171 nsThread::Join()
00172 {
00173     // don't check for mDead here because nspr calls Exit (cleaning up
00174     // thread-local storage) before they let us join with the thread
00175 
00176     PR_LOG(nsIThreadLog, PR_LOG_DEBUG,
00177            ("nsIThread %p start join\n", this));
00178     if (!mThread)
00179         return NS_ERROR_NOT_INITIALIZED;
00180     PRStatus status = PR_JoinThread(mThread);
00181     // XXX can't use NS_RELEASE here because the macro wants to set
00182     // this to null (bad c++)
00183     PR_LOG(nsIThreadLog, PR_LOG_DEBUG,
00184            ("nsIThread %p end join\n", this));
00185     if (status != PR_SUCCESS)
00186         return NS_ERROR_FAILURE;
00187 
00188     NS_RELEASE_THIS();   // most likely the final release of this thread 
00189     return NS_OK;
00190 }
00191 
00192 NS_IMETHODIMP
00193 nsThread::GetPriority(PRThreadPriority *result)
00194 {
00195     if (mDead)
00196         return NS_ERROR_FAILURE;
00197     if (!mThread)
00198         return NS_ERROR_NOT_INITIALIZED;
00199     *result = PR_GetThreadPriority(mThread);
00200     return NS_OK;
00201 }
00202 
00203 NS_IMETHODIMP
00204 nsThread::SetPriority(PRThreadPriority value)
00205 {
00206     if (mDead)
00207         return NS_ERROR_FAILURE;
00208     if (!mThread)
00209         return NS_ERROR_NOT_INITIALIZED;
00210     PR_SetThreadPriority(mThread, value);
00211     return NS_OK;
00212 }
00213 
00214 NS_IMETHODIMP
00215 nsThread::Interrupt()
00216 {
00217     if (mDead)
00218         return NS_ERROR_FAILURE;
00219     if (!mThread)
00220         return NS_ERROR_NOT_INITIALIZED;
00221     PRStatus status = PR_Interrupt(mThread);
00222     return status == PR_SUCCESS ? NS_OK : NS_ERROR_FAILURE;
00223 }
00224 
00225 NS_IMETHODIMP
00226 nsThread::GetScope(PRThreadScope *result)
00227 {
00228     if (mDead)
00229         return NS_ERROR_FAILURE;
00230     if (!mThread)
00231         return NS_ERROR_NOT_INITIALIZED;
00232     *result = PR_GetThreadScope(mThread);
00233     return NS_OK;
00234 }
00235 
00236 NS_IMETHODIMP
00237 nsThread::GetState(PRThreadState *result)
00238 {
00239     if (mDead)
00240         return NS_ERROR_FAILURE;
00241     if (!mThread)
00242         return NS_ERROR_NOT_INITIALIZED;
00243     *result = PR_GetThreadState(mThread);
00244     return NS_OK;
00245 }
00246 
00247 NS_IMETHODIMP
00248 nsThread::GetPRThread(PRThread* *result)
00249 {
00250     if (mDead) {
00251         *result = nsnull;
00252         return NS_ERROR_FAILURE;
00253     }    
00254     *result = mThread;
00255     return NS_OK;
00256 }
00257 
00258 NS_IMETHODIMP
00259 nsThread::Init(nsIRunnable* runnable,
00260                PRUint32 stackSize,
00261                PRThreadPriority priority,
00262                PRThreadScope scope,
00263                PRThreadState state)
00264 {
00265     NS_ENSURE_ARG_POINTER(runnable);
00266     if (mRunnable)
00267         return NS_ERROR_ALREADY_INITIALIZED;
00268 
00269     mRunnable = runnable;
00270 
00271     if (mStartLock)
00272         return NS_ERROR_ALREADY_INITIALIZED;
00273 
00274     mStartLock = PR_NewLock();
00275     if (mStartLock == nsnull) {
00276         mRunnable = nsnull;
00277         return NS_ERROR_OUT_OF_MEMORY;
00278     }
00279 
00280     NS_ADDREF_THIS();       // released in nsThread::Exit
00281     if (state == PR_JOINABLE_THREAD)
00282         NS_ADDREF_THIS();   // released in nsThread::Join
00283 
00284     PR_Lock(mStartLock);
00285     mDead = PR_FALSE;
00286     mThread = PR_CreateThread(PR_USER_THREAD, Main, this,
00287                               priority, scope, state, stackSize);
00288     /* As soon as we PR_Unlock(mStartLock), if mThread was successfully 
00289      * created, it could run and exit very quickly. In which case, it 
00290      * would null mThread and therefore if we check if (mThread) we could 
00291      * confuse a successfully created, yet already exited thread with
00292      * OOM - failure to create the thread. So instead we store a local thr 
00293      * which we check to see if we really failed to create the thread.
00294      */
00295     PRThread *thr = mThread;
00296     PR_Unlock(mStartLock);
00297 
00298     if (thr == nsnull) {
00299         mDead = PR_TRUE;         // otherwise cleared in nsThread::Exit
00300         mRunnable = nsnull;      // otherwise cleared in nsThread::Main(when done)
00301         PR_DestroyLock(mStartLock);
00302         mStartLock = nsnull;
00303         NS_RELEASE_THIS();       // otherwise released in nsThread::Exit
00304         if (state == PR_JOINABLE_THREAD)
00305             NS_RELEASE_THIS();   // otherwise released in nsThread::Join
00306         return NS_ERROR_OUT_OF_MEMORY;
00307     }
00308 
00309     PR_LOG(nsIThreadLog, PR_LOG_DEBUG,
00310            ("nsIThread %p created\n", this));
00311 
00312     return NS_OK;
00313 }
00314 
00315 /* readonly attribute nsIThread currentThread; */
00316 NS_IMETHODIMP 
00317 nsThread::GetCurrentThread(nsIThread * *aCurrentThread)
00318 {
00319     return GetIThread(PR_GetCurrentThread(), aCurrentThread);
00320 }
00321 
00322 /* void sleep (in PRUint32 msec); */
00323 NS_IMETHODIMP 
00324 nsThread::Sleep(PRUint32 msec)
00325 {
00326     if (PR_GetCurrentThread() != mThread)
00327         return NS_ERROR_FAILURE;
00328     
00329     if (PR_Sleep(PR_MillisecondsToInterval(msec)) != PR_SUCCESS)
00330         return NS_ERROR_FAILURE;
00331 
00332     return NS_OK;
00333 }
00334 
00335 NS_COM nsresult
00336 NS_NewThread(nsIThread* *result, 
00337              nsIRunnable* runnable,
00338              PRUint32 stackSize,
00339              PRThreadState state,
00340              PRThreadPriority priority,
00341              PRThreadScope scope)
00342 {
00343     nsresult rv;
00344     nsThread* thread = new nsThread();
00345     if (thread == nsnull)
00346         return NS_ERROR_OUT_OF_MEMORY;
00347     NS_ADDREF(thread);
00348 
00349     rv = thread->Init(runnable, stackSize, priority, scope, state);
00350     if (NS_FAILED(rv)) {
00351         NS_RELEASE(thread);
00352         return rv;
00353     }
00354 
00355     *result = thread;
00356     return NS_OK;
00357 }
00358 
00359 NS_COM nsresult
00360 NS_NewThread(nsIThread* *result, 
00361              PRUint32 stackSize,
00362              PRThreadState state,
00363              PRThreadPriority priority,
00364              PRThreadScope scope)
00365 {
00366     nsThread* thread = new nsThread();
00367     if (thread == nsnull)
00368         return NS_ERROR_OUT_OF_MEMORY;
00369     NS_ADDREF(thread);
00370     *result = thread;
00371     return NS_OK;
00372 }
00373 
00375 
00376 nsresult
00377 nsThread::RegisterThreadSelf()
00378 {
00379     PRStatus status;
00380 
00381     if (kIThreadSelfIndex == 0) {
00382         status = PR_NewThreadPrivateIndex(&kIThreadSelfIndex, Exit);
00383         if (status != PR_SUCCESS) return NS_ERROR_FAILURE;
00384     }
00385 
00386     status = PR_SetThreadPrivate(kIThreadSelfIndex, this);
00387     if (status != PR_SUCCESS) return NS_ERROR_FAILURE;
00388 
00389     return NS_OK;
00390 }
00391 
00392 void 
00393 nsThread::WaitUntilReadyToStartMain()
00394 {
00395     PR_Lock(mStartLock);
00396     PR_Unlock(mStartLock);
00397     PR_DestroyLock(mStartLock);
00398     mStartLock = nsnull;
00399 }
00400 
00401 NS_COM nsresult
00402 nsIThread::GetCurrent(nsIThread* *result)
00403 {
00404     return GetIThread(PR_GetCurrentThread(), result);
00405 }
00406 
00407 NS_COM nsresult
00408 nsIThread::GetIThread(PRThread* prthread, nsIThread* *result)
00409 {
00410     PRStatus status;
00411     nsThread* thread;
00412 
00413     if (nsThread::kIThreadSelfIndex == 0) {
00414         status = PR_NewThreadPrivateIndex(&nsThread::kIThreadSelfIndex, nsThread::Exit);
00415         if (status != PR_SUCCESS) return NS_ERROR_FAILURE;
00416     }
00417 
00418     thread = (nsThread*)PR_GetThreadPrivate(nsThread::kIThreadSelfIndex);
00419     if (thread == nsnull) {
00420         // if the current thread doesn't have an nsIThread associated
00421         // with it, make one
00422         thread = new nsThread();
00423         if (thread == nsnull)
00424             return NS_ERROR_OUT_OF_MEMORY;
00425         NS_ADDREF(thread);      // released by Exit
00426         thread->SetPRThread(prthread);
00427         nsresult rv = thread->RegisterThreadSelf();
00428         if (NS_FAILED(rv)) return rv;
00429     }
00430     NS_ADDREF(thread);
00431     *result = thread;
00432     return NS_OK;
00433 }
00434 
00435 NS_COM nsresult
00436 nsIThread::SetMainThread()
00437 {
00438     // strictly speaking, it could be set twice. but practically speaking,
00439     // it's almost certainly an error if it is
00440     if (gMainThread != 0) {
00441         NS_ERROR("Setting main thread twice?");
00442         return NS_ERROR_FAILURE;
00443     }
00444     return GetCurrent(&gMainThread);
00445 }
00446 
00447 NS_COM nsresult
00448 nsIThread::GetMainThread(nsIThread **result)
00449 {
00450     NS_ASSERTION(result, "bad result pointer");
00451     if (gMainThread == 0)
00452         return NS_ERROR_FAILURE;
00453     *result = gMainThread;
00454     NS_ADDREF(gMainThread);
00455     return NS_OK;
00456 }
00457 
00458 NS_COM PRBool
00459 nsIThread::IsMainThread()
00460 {
00461     if (gMainThread == 0)
00462         return PR_TRUE;
00463     
00464     PRThread *theMainThread;
00465     gMainThread->GetPRThread(&theMainThread);
00466     return theMainThread == PR_GetCurrentThread();
00467 }
00468 
00469 void 
00470 nsThread::Shutdown()
00471 {
00472     if (gMainThread) {
00473         // In most recent versions of NSPR the main thread's destructor
00474         // callback will get called.
00475         // In older versions of NSPR it will not get called,
00476         // (unless we call PR_Cleanup).
00477         // Because of that we:
00478         // - call the function ourselves
00479         // - set the data pointer to NULL to ensure the function will
00480         //   not get called again by NSPR
00481         // The PR_SetThreadPrivate call does both of these.
00482         // See also bugs 379550, 362768.
00483         PR_SetThreadPrivate(kIThreadSelfIndex, NULL);
00484         nsrefcnt cnt;
00485         NS_RELEASE2(gMainThread, cnt);
00486         NS_WARN_IF_FALSE(cnt == 0, "Main thread being held past XPCOM shutdown.");
00487         gMainThread = nsnull;
00488         
00489         kIThreadSelfIndex = 0;
00490     }
00491 }
00492