diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 16:49:04 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 16:49:04 +0000 |
commit | 16f504a9dca3fe3b70568f67b7d41241ae485288 (patch) | |
tree | c60f36ada0496ba928b7161059ba5ab1ab224f9d /src/libs/xpcom18a4/xpcom/threads/nsThread.cpp | |
parent | Initial commit. (diff) | |
download | virtualbox-upstream.tar.xz virtualbox-upstream.zip |
Adding upstream version 7.0.6-dfsg.upstream/7.0.6-dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/libs/xpcom18a4/xpcom/threads/nsThread.cpp')
-rw-r--r-- | src/libs/xpcom18a4/xpcom/threads/nsThread.cpp | 457 |
1 files changed, 457 insertions, 0 deletions
diff --git a/src/libs/xpcom18a4/xpcom/threads/nsThread.cpp b/src/libs/xpcom18a4/xpcom/threads/nsThread.cpp new file mode 100644 index 00000000..b4cc15f4 --- /dev/null +++ b/src/libs/xpcom18a4/xpcom/threads/nsThread.cpp @@ -0,0 +1,457 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "nsThread.h" +#include "prmem.h" +#include "prlog.h" +#include "nsAutoLock.h" + +PRUintn nsThread::kIThreadSelfIndex = 0; +static nsIThread *gMainThread = 0; + +#if defined(PR_LOGGING) +// +// Log module for nsIThread logging... +// +// To enable logging (see prlog.h for full details): +// +// set NSPR_LOG_MODULES=nsIThread:5 +// set NSPR_LOG_FILE=nspr.log +// +// this enables PR_LOG_DEBUG level information and places all output in +// the file nspr.log +// +// gSocketLog is defined in nsSocketTransport.cpp +// +PRLogModuleInfo* nsIThreadLog = nsnull; + +#endif /* PR_LOGGING */ + +//////////////////////////////////////////////////////////////////////////////// + +nsThread::nsThread() + : mThread(nsnull), mDead(PR_FALSE), mStartLock(nsnull) +{ +#if defined(PR_LOGGING) + // + // Initialize the global PRLogModule for nsIThread logging + // if necessary... + // + if (nsIThreadLog == nsnull) { + nsIThreadLog = PR_NewLogModule("nsIThread"); + } +#endif /* PR_LOGGING */ + + // enforce matching of constants to enums in prthread.h + NS_ASSERTION(int(nsIThread::PRIORITY_LOW) == int(PR_PRIORITY_LOW) && + int(nsIThread::PRIORITY_NORMAL) == int(PRIORITY_NORMAL) && + int(nsIThread::PRIORITY_HIGH) == int(PRIORITY_HIGH) && + int(nsIThread::PRIORITY_URGENT) == int(PRIORITY_URGENT) && + int(nsIThread::SCOPE_LOCAL) == int(PR_LOCAL_THREAD) && + int(nsIThread::SCOPE_GLOBAL) == int(PR_GLOBAL_THREAD) && + int(nsIThread::STATE_JOINABLE) == int(PR_JOINABLE_THREAD) && + int(nsIThread::STATE_UNJOINABLE) == int(PR_UNJOINABLE_THREAD), + "Bad constant in nsIThread!"); +} + +nsThread::~nsThread() +{ + if (mStartLock) + PR_DestroyLock(mStartLock); + + PR_LOG(nsIThreadLog, PR_LOG_DEBUG, + ("nsIThread %p destroyed\n", this)); + + // This code used to free the nsIThreadLog loginfo stuff + // Don't do that; loginfo structures are owned by nspr + // and would be freed if we ever called PR_Cleanup() + // see bug 142072 +} + +void +nsThread::Main(void* arg) +{ + nsThread* self = (nsThread*)arg; + + self->WaitUntilReadyToStartMain(); + + nsresult rv = NS_OK; + rv = self->RegisterThreadSelf(); + NS_ASSERTION(rv == NS_OK, "failed to set thread self"); + + PR_LOG(nsIThreadLog, PR_LOG_DEBUG, + ("nsIThread %p start run %p\n", self, self->mRunnable.get())); + rv = self->mRunnable->Run(); + NS_ASSERTION(NS_SUCCEEDED(rv), "runnable failed"); + +#ifdef DEBUG + // Because a thread can die after gMainThread dies and takes nsIThreadLog with it, + // we need to check for it being null so that we don't crash on shutdown. + if (nsIThreadLog) { + PRThreadState state; + rv = self->GetState(&state); + PR_LOG(nsIThreadLog, PR_LOG_DEBUG, + ("nsIThread %p end run %p\n", self, self->mRunnable.get())); + } +#endif + + // explicitly drop the runnable now in case there are circular references + // between it and the thread object + self->mRunnable = nsnull; +} + +void +nsThread::Exit(void* arg) +{ + nsThread* self = (nsThread*)arg; + + if (self->mDead) { + NS_ERROR("attempt to Exit() thread twice"); + return; + } + + self->mDead = PR_TRUE; + +#if defined(PR_LOGGING) + if (nsIThreadLog) { + PR_LOG(nsIThreadLog, PR_LOG_DEBUG, + ("nsIThread %p exited\n", self)); + } +#endif + NS_RELEASE(self); +} + +NS_METHOD +nsThread::Create(nsISupports *aOuter, REFNSIID aIID, void **aResult) +{ + nsThread* thread = new nsThread(); + if (!thread) return NS_ERROR_OUT_OF_MEMORY; + nsresult rv = thread->QueryInterface(aIID, aResult); + if (NS_FAILED(rv)) delete thread; + return rv; +} + +NS_IMPL_THREADSAFE_ISUPPORTS1(nsThread, nsIThread) + +NS_IMETHODIMP +nsThread::Join() +{ + // don't check for mDead here because nspr calls Exit (cleaning up + // thread-local storage) before they let us join with the thread + + PR_LOG(nsIThreadLog, PR_LOG_DEBUG, + ("nsIThread %p start join\n", this)); + if (!mThread) + return NS_ERROR_NOT_INITIALIZED; + PRStatus status = PR_JoinThread(mThread); + // XXX can't use NS_RELEASE here because the macro wants to set + // this to null (bad c++) + PR_LOG(nsIThreadLog, PR_LOG_DEBUG, + ("nsIThread %p end join\n", this)); + if (status == PR_SUCCESS) { + NS_RELEASE_THIS(); // most likely the final release of this thread + return NS_OK; + } + else + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +nsThread::GetPriority(PRThreadPriority *result) +{ + if (mDead) + return NS_ERROR_FAILURE; + if (!mThread) + return NS_ERROR_NOT_INITIALIZED; + *result = PR_GetThreadPriority(mThread); + return NS_OK; +} + +NS_IMETHODIMP +nsThread::SetPriority(PRThreadPriority value) +{ + if (mDead) + return NS_ERROR_FAILURE; + if (!mThread) + return NS_ERROR_NOT_INITIALIZED; + PR_SetThreadPriority(mThread, value); + return NS_OK; +} + +NS_IMETHODIMP +nsThread::Interrupt() +{ + if (mDead) + return NS_ERROR_FAILURE; + if (!mThread) + return NS_ERROR_NOT_INITIALIZED; + PRStatus status = PR_Interrupt(mThread); + return status == PR_SUCCESS ? NS_OK : NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +nsThread::GetScope(PRThreadScope *result) +{ + if (mDead) + return NS_ERROR_FAILURE; + if (!mThread) + return NS_ERROR_NOT_INITIALIZED; + *result = PR_GetThreadScope(mThread); + return NS_OK; +} + +NS_IMETHODIMP +nsThread::GetState(PRThreadState *result) +{ + if (mDead) + return NS_ERROR_FAILURE; + if (!mThread) + return NS_ERROR_NOT_INITIALIZED; + *result = PR_GetThreadState(mThread); + return NS_OK; +} + +NS_IMETHODIMP +nsThread::GetPRThread(PRThread* *result) +{ + if (mDead) { + *result = nsnull; + return NS_ERROR_FAILURE; + } + *result = mThread; + return NS_OK; +} + +NS_IMETHODIMP +nsThread::Init(nsIRunnable* runnable, + PRUint32 stackSize, + PRThreadPriority priority, + PRThreadScope scope, + PRThreadState state) +{ + NS_ENSURE_ARG_POINTER(runnable); + mRunnable = runnable; + + NS_ADDREF_THIS(); // released in nsThread::Exit + if (state == PR_JOINABLE_THREAD) + NS_ADDREF_THIS(); // released in nsThread::Join + mStartLock = PR_NewLock(); + if (mStartLock == nsnull) + return NS_ERROR_OUT_OF_MEMORY; + PR_Lock(mStartLock); + mThread = PR_CreateThread(PR_USER_THREAD, Main, this, + priority, scope, state, stackSize); + PR_Unlock(mStartLock); + PR_LOG(nsIThreadLog, PR_LOG_DEBUG, + ("nsIThread %p created\n", this)); + + if (mThread == nsnull) + return NS_ERROR_OUT_OF_MEMORY; + return NS_OK; +} + +/* readonly attribute nsIThread currentThread; */ +NS_IMETHODIMP +nsThread::GetCurrentThread(nsIThread * *aCurrentThread) +{ + return GetIThread(PR_GetCurrentThread(), aCurrentThread); +} + +/* void sleep (in PRUint32 msec); */ +NS_IMETHODIMP +nsThread::Sleep(PRUint32 msec) +{ + if (PR_GetCurrentThread() != mThread) + return NS_ERROR_FAILURE; + + if (PR_Sleep(PR_MillisecondsToInterval(msec)) != PR_SUCCESS) + return NS_ERROR_FAILURE; + + return NS_OK; +} + +NS_COM nsresult +NS_NewThread(nsIThread* *result, + nsIRunnable* runnable, + PRUint32 stackSize, + PRThreadState state, + PRThreadPriority priority, + PRThreadScope scope) +{ + nsresult rv; + nsThread* thread = new nsThread(); + if (thread == nsnull) + return NS_ERROR_OUT_OF_MEMORY; + NS_ADDREF(thread); + + rv = thread->Init(runnable, stackSize, priority, scope, state); + if (NS_FAILED(rv)) { + NS_RELEASE(thread); + return rv; + } + + *result = thread; + return NS_OK; +} + +NS_COM nsresult +NS_NewThread(nsIThread* *result, + PRUint32 stackSize, + PRThreadState state, + PRThreadPriority priority, + PRThreadScope scope) +{ + nsThread* thread = new nsThread(); + if (thread == nsnull) + return NS_ERROR_OUT_OF_MEMORY; + NS_ADDREF(thread); + *result = thread; + return NS_OK; +} + +//////////////////////////////////////////////////////////////////////////////// + +nsresult +nsThread::RegisterThreadSelf() +{ + PRStatus status; + + if (kIThreadSelfIndex == 0) { + status = PR_NewThreadPrivateIndex(&kIThreadSelfIndex, Exit); + if (status != PR_SUCCESS) return NS_ERROR_FAILURE; + } + + status = PR_SetThreadPrivate(kIThreadSelfIndex, this); + if (status != PR_SUCCESS) return NS_ERROR_FAILURE; + + return NS_OK; +} + +void +nsThread::WaitUntilReadyToStartMain() +{ + PR_Lock(mStartLock); + PR_Unlock(mStartLock); + PR_DestroyLock(mStartLock); + mStartLock = nsnull; +} + +NS_COM nsresult +nsIThread::GetCurrent(nsIThread* *result) +{ + return GetIThread(PR_GetCurrentThread(), result); +} + +NS_COM nsresult +nsIThread::GetIThread(PRThread* prthread, nsIThread* *result) +{ + PRStatus status; + nsThread* thread; + + if (nsThread::kIThreadSelfIndex == 0) { + status = PR_NewThreadPrivateIndex(&nsThread::kIThreadSelfIndex, nsThread::Exit); + if (status != PR_SUCCESS) return NS_ERROR_FAILURE; + } + + thread = (nsThread*)PR_GetThreadPrivate(nsThread::kIThreadSelfIndex); + if (thread == nsnull) { + // if the current thread doesn't have an nsIThread associated + // with it, make one + thread = new nsThread(); + if (thread == nsnull) + return NS_ERROR_OUT_OF_MEMORY; + NS_ADDREF(thread); // released by Exit + thread->SetPRThread(prthread); + nsresult rv = thread->RegisterThreadSelf(); + if (NS_FAILED(rv)) return rv; + } + NS_ADDREF(thread); + *result = thread; + return NS_OK; +} + +NS_COM nsresult +nsIThread::SetMainThread() +{ + // strictly speaking, it could be set twice. but practically speaking, + // it's almost certainly an error if it is + if (gMainThread != 0) { + NS_ERROR("Setting main thread twice?"); + return NS_ERROR_FAILURE; + } + return GetCurrent(&gMainThread); +} + +NS_COM nsresult +nsIThread::GetMainThread(nsIThread **result) +{ + NS_ASSERTION(result, "bad result pointer"); + if (gMainThread == 0) + return NS_ERROR_FAILURE; + *result = gMainThread; + NS_ADDREF(gMainThread); + return NS_OK; +} + +NS_COM PRBool +nsIThread::IsMainThread() +{ + if (gMainThread == 0) + return PR_TRUE; + + PRThread *theMainThread; + gMainThread->GetPRThread(&theMainThread); + return theMainThread == PR_GetCurrentThread(); +} + +void +nsThread::Shutdown() +{ + if (gMainThread) { + // XXX nspr doesn't seem to be calling the main thread's destructor + // callback, so let's help it out: + nsThread::Exit(NS_STATIC_CAST(nsThread*, gMainThread)); + nsrefcnt cnt; + NS_RELEASE2(gMainThread, cnt); + NS_WARN_IF_FALSE(cnt == 0, "Main thread being held past XPCOM shutdown."); + gMainThread = nsnull; + + kIThreadSelfIndex = 0; + } +} + +//////////////////////////////////////////////////////////////////////////////// |