1999-04-02 13:20:44 +04:00
|
|
|
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
|
|
/*
|
1999-11-06 06:43:54 +03:00
|
|
|
* The contents of this file are subject to the Netscape 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/NPL/
|
1999-04-02 13:20:44 +04:00
|
|
|
*
|
1999-11-06 06:43:54 +03:00
|
|
|
* 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.
|
1999-04-02 13:20:44 +04:00
|
|
|
*
|
1999-11-06 06:43:54 +03:00
|
|
|
* The Original Code is mozilla.org code.
|
|
|
|
*
|
|
|
|
* The Initial Developer of the Original Code is Netscape
|
1999-04-02 13:20:44 +04:00
|
|
|
* Communications Corporation. Portions created by Netscape are
|
1999-11-06 06:43:54 +03:00
|
|
|
* Copyright (C) 1998 Netscape Communications Corporation. All
|
|
|
|
* Rights Reserved.
|
|
|
|
*
|
|
|
|
* Contributor(s):
|
1999-04-02 13:20:44 +04:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include "nsThread.h"
|
|
|
|
#include "prmem.h"
|
1999-06-15 08:57:07 +04:00
|
|
|
#include "prlog.h"
|
2000-04-21 09:12:57 +04:00
|
|
|
#include "nsAutoLock.h"
|
1999-04-02 13:20:44 +04:00
|
|
|
|
1999-04-13 22:15:27 +04:00
|
|
|
PRUintn nsThread::kIThreadSelfIndex = 0;
|
1999-06-13 07:30:38 +04:00
|
|
|
static nsIThread *gMainThread = 0;
|
1999-04-06 01:02:24 +04:00
|
|
|
|
1999-06-15 08:57:07 +04:00
|
|
|
#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 */
|
|
|
|
|
1999-04-02 13:20:44 +04:00
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
nsThread::nsThread()
|
1999-09-21 00:54:23 +04:00
|
|
|
: mThread(nsnull), mDead(PR_FALSE)
|
1999-04-02 13:20:44 +04:00
|
|
|
{
|
|
|
|
NS_INIT_REFCNT();
|
1999-06-15 08:57:07 +04:00
|
|
|
|
|
|
|
#if defined(PR_LOGGING)
|
|
|
|
//
|
|
|
|
// Initialize the global PRLogModule for nsIThread logging
|
|
|
|
// if necessary...
|
|
|
|
//
|
|
|
|
if (nsIThreadLog == nsnull) {
|
|
|
|
nsIThreadLog = PR_NewLogModule("nsIThread");
|
|
|
|
}
|
|
|
|
#endif /* PR_LOGGING */
|
1999-04-02 13:20:44 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
nsThread::~nsThread()
|
|
|
|
{
|
1999-06-15 08:57:07 +04:00
|
|
|
PR_LOG(nsIThreadLog, PR_LOG_DEBUG,
|
|
|
|
("nsIThread %p destroyed\n", this));
|
1999-04-02 13:20:44 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nsThread::Main(void* arg)
|
|
|
|
{
|
|
|
|
nsThread* self = (nsThread*)arg;
|
1999-04-06 01:02:24 +04:00
|
|
|
|
1999-04-02 13:20:44 +04:00
|
|
|
nsresult rv = NS_OK;
|
1999-04-06 01:02:24 +04:00
|
|
|
rv = self->RegisterThreadSelf();
|
|
|
|
NS_ASSERTION(rv == NS_OK, "failed to set thread self");
|
|
|
|
|
1999-06-15 08:57:07 +04:00
|
|
|
PR_LOG(nsIThreadLog, PR_LOG_DEBUG,
|
1999-10-06 12:51:48 +04:00
|
|
|
("nsIThread %p start run %p\n", self, self->mRunnable.get()));
|
1999-04-02 13:20:44 +04:00
|
|
|
rv = self->mRunnable->Run();
|
|
|
|
NS_ASSERTION(NS_SUCCEEDED(rv), "runnable failed");
|
1999-04-06 01:02:24 +04:00
|
|
|
|
2000-04-21 09:12:57 +04:00
|
|
|
#ifdef DEBUG
|
1999-04-06 01:02:24 +04:00
|
|
|
PRThreadState state;
|
|
|
|
rv = self->GetState(&state);
|
1999-06-15 08:57:07 +04:00
|
|
|
PR_LOG(nsIThreadLog, PR_LOG_DEBUG,
|
1999-10-06 12:51:48 +04:00
|
|
|
("nsIThread %p end run %p\n", self, self->mRunnable.get()));
|
2000-04-21 09:12:57 +04:00
|
|
|
#endif
|
|
|
|
|
|
|
|
// explicitly drop the runnable now in case there are circular references
|
|
|
|
// between it and the thread object
|
|
|
|
self->mRunnable = nsnull;
|
1999-04-06 01:02:24 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nsThread::Exit(void* arg)
|
|
|
|
{
|
|
|
|
nsThread* self = (nsThread*)arg;
|
1999-04-13 22:15:27 +04:00
|
|
|
self->mDead = PR_TRUE;
|
1999-06-15 08:57:07 +04:00
|
|
|
PR_LOG(nsIThreadLog, PR_LOG_DEBUG,
|
|
|
|
("nsIThread %p exited\n", self));
|
1999-04-06 01:02:24 +04:00
|
|
|
NS_RELEASE(self);
|
1999-04-02 13:20:44 +04:00
|
|
|
}
|
|
|
|
|
1999-10-02 03:30:06 +04:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2000-03-06 00:26:01 +03:00
|
|
|
NS_IMPL_THREADSAFE_ISUPPORTS1(nsThread, nsIThread)
|
1999-04-02 13:20:44 +04:00
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsThread::Join()
|
|
|
|
{
|
1999-04-13 22:15:27 +04:00
|
|
|
// don't check for mDead here because nspr calls Exit (cleaning up
|
|
|
|
// thread-local storage) before they let us join with the thread
|
|
|
|
|
1999-06-15 08:57:07 +04:00
|
|
|
PR_LOG(nsIThreadLog, PR_LOG_DEBUG,
|
|
|
|
("nsIThread %p start join\n", this));
|
1999-04-02 13:20:44 +04:00
|
|
|
PRStatus status = PR_JoinThread(mThread);
|
1999-04-13 22:15:27 +04:00
|
|
|
// XXX can't use NS_RELEASE here because the macro wants to set
|
|
|
|
// this to null (bad c++)
|
1999-06-15 08:57:07 +04:00
|
|
|
PR_LOG(nsIThreadLog, PR_LOG_DEBUG,
|
|
|
|
("nsIThread %p end join\n", this));
|
1999-04-13 22:15:27 +04:00
|
|
|
if (status == PR_SUCCESS) {
|
2000-04-21 09:12:57 +04:00
|
|
|
NS_RELEASE_THIS(); // most likely the final release of this thread
|
1999-04-13 22:15:27 +04:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return NS_ERROR_FAILURE;
|
1999-04-02 13:20:44 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsThread::GetPriority(PRThreadPriority *result)
|
|
|
|
{
|
1999-04-13 22:15:27 +04:00
|
|
|
if (mDead)
|
1999-04-06 01:02:24 +04:00
|
|
|
return NS_ERROR_FAILURE;
|
1999-04-02 13:20:44 +04:00
|
|
|
*result = PR_GetThreadPriority(mThread);
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsThread::SetPriority(PRThreadPriority value)
|
|
|
|
{
|
1999-04-13 22:15:27 +04:00
|
|
|
if (mDead)
|
1999-04-06 01:02:24 +04:00
|
|
|
return NS_ERROR_FAILURE;
|
1999-04-02 13:20:44 +04:00
|
|
|
PR_SetThreadPriority(mThread, value);
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsThread::Interrupt()
|
|
|
|
{
|
1999-04-13 22:15:27 +04:00
|
|
|
if (mDead)
|
1999-04-06 01:02:24 +04:00
|
|
|
return NS_ERROR_FAILURE;
|
1999-04-02 13:20:44 +04:00
|
|
|
PRStatus status = PR_Interrupt(mThread);
|
|
|
|
return status == PR_SUCCESS ? NS_OK : NS_ERROR_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsThread::GetScope(PRThreadScope *result)
|
|
|
|
{
|
1999-04-13 22:15:27 +04:00
|
|
|
if (mDead)
|
1999-04-06 01:02:24 +04:00
|
|
|
return NS_ERROR_FAILURE;
|
1999-04-02 13:20:44 +04:00
|
|
|
*result = PR_GetThreadScope(mThread);
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsThread::GetState(PRThreadState *result)
|
|
|
|
{
|
1999-04-13 22:15:27 +04:00
|
|
|
if (mDead)
|
1999-04-06 01:02:24 +04:00
|
|
|
return NS_ERROR_FAILURE;
|
1999-04-02 13:20:44 +04:00
|
|
|
*result = PR_GetThreadState(mThread);
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
1999-04-06 01:02:24 +04:00
|
|
|
NS_IMETHODIMP
|
|
|
|
nsThread::GetPRThread(PRThread* *result)
|
|
|
|
{
|
1999-04-13 22:15:27 +04:00
|
|
|
if (mDead)
|
1999-04-06 01:02:24 +04:00
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
*result = mThread;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
1999-10-02 03:30:06 +04:00
|
|
|
NS_IMETHODIMP
|
|
|
|
nsThread::Init(nsIRunnable* runnable,
|
|
|
|
PRUint32 stackSize,
|
|
|
|
PRThreadPriority priority,
|
|
|
|
PRThreadScope scope,
|
|
|
|
PRThreadState state)
|
|
|
|
{
|
|
|
|
mRunnable = runnable;
|
|
|
|
|
|
|
|
NS_ADDREF_THIS(); // released in nsThread::Exit
|
|
|
|
if (state == PR_JOINABLE_THREAD)
|
|
|
|
NS_ADDREF_THIS(); // released in nsThread::Join
|
|
|
|
mThread = PR_CreateThread(PR_USER_THREAD, Main, this,
|
|
|
|
priority, scope, state, stackSize);
|
|
|
|
PR_LOG(nsIThreadLog, PR_LOG_DEBUG,
|
|
|
|
("nsIThread %p created\n", this));
|
|
|
|
if (mThread == nsnull)
|
|
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
1999-05-26 05:38:36 +04:00
|
|
|
NS_COM nsresult
|
1999-04-02 13:20:44 +04:00
|
|
|
NS_NewThread(nsIThread* *result,
|
|
|
|
nsIRunnable* runnable,
|
|
|
|
PRUint32 stackSize,
|
1999-10-02 03:30:06 +04:00
|
|
|
PRThreadState state,
|
1999-04-02 13:20:44 +04:00
|
|
|
PRThreadPriority priority,
|
1999-10-02 03:30:06 +04:00
|
|
|
PRThreadScope scope)
|
1999-04-02 13:20:44 +04:00
|
|
|
{
|
|
|
|
nsresult rv;
|
|
|
|
nsThread* thread = new nsThread();
|
|
|
|
if (thread == nsnull)
|
|
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
1999-09-21 00:54:23 +04:00
|
|
|
NS_ADDREF(thread);
|
1999-04-02 13:20:44 +04:00
|
|
|
|
1999-04-13 22:15:27 +04:00
|
|
|
rv = thread->Init(runnable, stackSize, priority, scope, state);
|
1999-04-02 13:20:44 +04:00
|
|
|
if (NS_FAILED(rv)) {
|
1999-09-21 00:54:23 +04:00
|
|
|
NS_RELEASE(thread);
|
1999-04-02 13:20:44 +04:00
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
*result = thread;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
1999-10-02 03:30:06 +04:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
1999-04-02 13:20:44 +04:00
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
1999-04-06 01:02:24 +04:00
|
|
|
nsresult
|
|
|
|
nsThread::RegisterThreadSelf()
|
|
|
|
{
|
|
|
|
PRStatus status;
|
|
|
|
|
1999-04-13 22:15:27 +04:00
|
|
|
if (kIThreadSelfIndex == 0) {
|
|
|
|
status = PR_NewThreadPrivateIndex(&kIThreadSelfIndex, Exit);
|
1999-04-06 01:02:24 +04:00
|
|
|
if (status != PR_SUCCESS) return NS_ERROR_FAILURE;
|
1999-04-13 22:15:27 +04:00
|
|
|
NS_ASSERTION(kIThreadSelfIndex != 0, "couldn't get thread private index");
|
1999-04-06 01:02:24 +04:00
|
|
|
}
|
|
|
|
|
1999-04-13 22:15:27 +04:00
|
|
|
status = PR_SetThreadPrivate(kIThreadSelfIndex, this);
|
1999-04-06 01:02:24 +04:00
|
|
|
if (status != PR_SUCCESS) return NS_ERROR_FAILURE;
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
1999-05-26 05:38:36 +04:00
|
|
|
NS_COM nsresult
|
1999-04-06 01:02:24 +04:00
|
|
|
nsIThread::GetCurrent(nsIThread* *result)
|
1999-04-06 10:09:15 +04:00
|
|
|
{
|
|
|
|
return GetIThread(PR_CurrentThread(), result);
|
|
|
|
}
|
|
|
|
|
1999-05-26 05:38:36 +04:00
|
|
|
NS_COM nsresult
|
1999-04-06 10:09:15 +04:00
|
|
|
nsIThread::GetIThread(PRThread* prthread, nsIThread* *result)
|
1999-04-06 01:02:24 +04:00
|
|
|
{
|
|
|
|
PRStatus status;
|
|
|
|
nsThread* thread;
|
|
|
|
|
1999-04-13 22:15:27 +04:00
|
|
|
if (nsThread::kIThreadSelfIndex == 0) {
|
|
|
|
status = PR_NewThreadPrivateIndex(&nsThread::kIThreadSelfIndex, nsThread::Exit);
|
1999-04-06 01:02:24 +04:00
|
|
|
if (status != PR_SUCCESS) return NS_ERROR_FAILURE;
|
1999-04-13 22:15:27 +04:00
|
|
|
NS_ASSERTION(nsThread::kIThreadSelfIndex != 0, "couldn't get thread private index");
|
1999-04-06 01:02:24 +04:00
|
|
|
}
|
|
|
|
|
1999-04-13 22:15:27 +04:00
|
|
|
thread = (nsThread*)PR_GetThreadPrivate(nsThread::kIThreadSelfIndex);
|
1999-04-06 01:02:24 +04:00
|
|
|
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;
|
2000-03-06 00:26:01 +03:00
|
|
|
NS_ADDREF(thread); // released by Exit
|
1999-04-06 10:09:15 +04:00
|
|
|
thread->SetPRThread(prthread);
|
1999-04-06 01:02:24 +04:00
|
|
|
nsresult rv = thread->RegisterThreadSelf();
|
|
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
}
|
|
|
|
NS_ADDREF(thread);
|
|
|
|
*result = thread;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
1999-06-13 07:30:38 +04:00
|
|
|
NS_COM nsresult
|
|
|
|
nsIThread::SetMainThread()
|
|
|
|
{
|
1999-09-21 00:54:23 +04:00
|
|
|
// 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);
|
1999-06-13 07:30:38 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
NS_COM nsresult
|
|
|
|
nsIThread::GetMainThread(nsIThread **result)
|
|
|
|
{
|
1999-09-21 00:54:23 +04:00
|
|
|
NS_ASSERTION(result, "bad result pointer");
|
|
|
|
if (gMainThread == 0)
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
*result = gMainThread;
|
|
|
|
NS_ADDREF(gMainThread);
|
|
|
|
return NS_OK;
|
1999-06-13 07:30:38 +04:00
|
|
|
}
|
|
|
|
|
2000-03-06 00:26:01 +03:00
|
|
|
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.");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
1999-04-06 01:02:24 +04:00
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
2000-04-21 09:12:57 +04:00
|
|
|
nsThreadPool::nsThreadPool()
|
|
|
|
: mMinThreads(0), mMaxThreads(0), mShuttingDown(PR_FALSE)
|
1999-04-02 13:20:44 +04:00
|
|
|
{
|
|
|
|
NS_INIT_REFCNT();
|
|
|
|
}
|
|
|
|
|
|
|
|
nsThreadPool::~nsThreadPool()
|
|
|
|
{
|
1999-04-06 01:02:24 +04:00
|
|
|
if (mThreads) {
|
1999-04-13 22:15:27 +04:00
|
|
|
Shutdown();
|
1999-04-06 01:02:24 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
if (mRequestMonitor) {
|
|
|
|
PR_DestroyMonitor(mRequestMonitor);
|
|
|
|
}
|
1999-04-02 13:20:44 +04:00
|
|
|
}
|
|
|
|
|
2000-03-06 00:26:01 +03:00
|
|
|
NS_IMPL_THREADSAFE_ISUPPORTS1(nsThreadPool, nsIThreadPool)
|
1999-04-02 13:20:44 +04:00
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsThreadPool::DispatchRequest(nsIRunnable* runnable)
|
|
|
|
{
|
|
|
|
nsresult rv;
|
2000-04-21 09:12:57 +04:00
|
|
|
nsAutoMonitor mon(mRequestMonitor);
|
1999-04-02 13:20:44 +04:00
|
|
|
|
2000-04-21 09:12:57 +04:00
|
|
|
NS_ASSERTION(mMinThreads > 0, "forgot to call Init");
|
1999-04-06 05:42:01 +04:00
|
|
|
if (mShuttingDown) {
|
|
|
|
rv = NS_ERROR_FAILURE;
|
|
|
|
}
|
|
|
|
else {
|
2000-04-21 09:12:57 +04:00
|
|
|
PRUint32 requestCnt, threadCount;
|
|
|
|
|
|
|
|
rv = mRequests->Count(&requestCnt);
|
|
|
|
if (NS_FAILED(rv)) goto exit;
|
|
|
|
|
|
|
|
rv = mThreads->Count(&threadCount);
|
|
|
|
if (NS_FAILED(rv)) goto exit;
|
|
|
|
|
|
|
|
if ((requestCnt >= threadCount) && (threadCount < mMaxThreads)) {
|
|
|
|
rv = AddThread();
|
|
|
|
if (NS_FAILED(rv)) goto exit;
|
|
|
|
}
|
|
|
|
|
2000-03-06 04:13:20 +03:00
|
|
|
// XXX for now AppendElement returns a PRBool
|
1999-04-15 03:06:22 +04:00
|
|
|
rv = ((PRBool) mRequests->AppendElement(runnable)) ? NS_OK : NS_ERROR_FAILURE;
|
2000-04-21 09:12:57 +04:00
|
|
|
if (NS_SUCCEEDED(rv)) {
|
|
|
|
rv = mon.Notify();
|
|
|
|
if (NS_FAILED(rv)) goto exit;
|
|
|
|
}
|
1999-04-06 05:42:01 +04:00
|
|
|
}
|
2000-04-21 09:12:57 +04:00
|
|
|
|
|
|
|
exit:
|
|
|
|
#if defined(PR_LOGGING)
|
|
|
|
nsCOMPtr<nsIThread> th;
|
|
|
|
nsIThread::GetCurrent(getter_AddRefs(th));
|
1999-08-11 12:13:07 +04:00
|
|
|
PR_LOG(nsIThreadLog, PR_LOG_DEBUG,
|
2000-04-21 09:12:57 +04:00
|
|
|
("nsIThreadPool thread %p dispatched %p status %x\n", th.get(), runnable, rv));
|
|
|
|
#endif
|
1999-04-02 13:20:44 +04:00
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsIRunnable*
|
2000-04-21 09:12:57 +04:00
|
|
|
nsThreadPool::GetRequest(nsIThread* currentThread)
|
1999-04-02 13:20:44 +04:00
|
|
|
{
|
1999-04-06 01:02:24 +04:00
|
|
|
nsresult rv = NS_OK;
|
|
|
|
nsIRunnable* request = nsnull;
|
2000-04-21 09:12:57 +04:00
|
|
|
nsAutoMonitor mon(mRequestMonitor);
|
1999-04-06 01:02:24 +04:00
|
|
|
|
1999-05-13 08:56:04 +04:00
|
|
|
PRUint32 cnt;
|
|
|
|
while (PR_TRUE) {
|
|
|
|
rv = mRequests->Count(&cnt);
|
|
|
|
if (NS_FAILED(rv) || cnt != 0)
|
|
|
|
break;
|
|
|
|
|
1999-04-06 05:42:01 +04:00
|
|
|
if (mShuttingDown) {
|
|
|
|
rv = NS_ERROR_FAILURE;
|
|
|
|
break;
|
|
|
|
}
|
2000-04-21 09:12:57 +04:00
|
|
|
|
|
|
|
// no requests, and we're not shutting down yet...
|
|
|
|
// if we have more than the minimum required threads already then
|
|
|
|
// we can just go away
|
|
|
|
PRUint32 threadCnt;
|
|
|
|
rv = mThreads->Count(&threadCnt);
|
|
|
|
if (NS_FAILED(rv)) break;
|
|
|
|
|
|
|
|
#if 0 /* XXX I had to take this code out to cut the number of threads back to
|
|
|
|
* the minimum count because if you just let the threads terminate
|
|
|
|
* themselves without joining with them, they'll just end up hanging around
|
|
|
|
* anyway. So we might as well keep them on the active list. Fix later!
|
|
|
|
*/
|
|
|
|
if (threadCnt > mMinThreads) {
|
|
|
|
PR_LOG(nsIThreadLog, PR_LOG_DEBUG,
|
|
|
|
("nsIThreadPool thread %p being removed (%d threads left)\n",
|
|
|
|
currentThread.get(), threadCnt - 1));
|
|
|
|
|
|
|
|
rv = mThreads->RemoveElement(currentThread) ? NS_OK : NS_ERROR_FAILURE; // XXX fix result
|
|
|
|
if (NS_FAILED(rv)) break;
|
|
|
|
|
|
|
|
// release the thread once more because the thread pool isn't
|
|
|
|
// going to join with it now:
|
|
|
|
nsIThread* current = currentThread;
|
|
|
|
NS_RELEASE(current);
|
|
|
|
|
|
|
|
return nsnull; // causes nsThreadPoolRunnable::Run to quit
|
|
|
|
}
|
|
|
|
#endif
|
1999-06-15 08:57:07 +04:00
|
|
|
PR_LOG(nsIThreadLog, PR_LOG_DEBUG,
|
2000-04-21 09:12:57 +04:00
|
|
|
("nsIThreadPool thread %p waiting (%d threads in pool)\n",
|
|
|
|
currentThread, threadCnt));
|
|
|
|
rv = mon.Wait();
|
|
|
|
if (NS_FAILED(rv) || mShuttingDown) {
|
1999-04-06 01:02:24 +04:00
|
|
|
rv = NS_ERROR_FAILURE;
|
1999-04-06 05:42:01 +04:00
|
|
|
break;
|
1999-04-06 01:02:24 +04:00
|
|
|
}
|
|
|
|
}
|
1999-04-02 13:20:44 +04:00
|
|
|
|
1999-04-06 01:02:24 +04:00
|
|
|
if (NS_SUCCEEDED(rv)) {
|
1999-05-13 08:56:04 +04:00
|
|
|
NS_ASSERTION((NS_SUCCEEDED(mRequests->Count(&cnt)) && cnt > 0),
|
|
|
|
"request queue out of sync");
|
1999-06-02 04:07:54 +04:00
|
|
|
request = (nsIRunnable*)mRequests->ElementAt(0);
|
1999-04-06 01:02:24 +04:00
|
|
|
NS_ASSERTION(request != nsnull, "null runnable");
|
1999-04-02 13:20:44 +04:00
|
|
|
|
1999-04-06 01:02:24 +04:00
|
|
|
PRBool removed = mRequests->RemoveElementAt(0);
|
|
|
|
NS_ASSERTION(removed, "nsISupportsArray broken");
|
|
|
|
}
|
1999-08-11 12:13:07 +04:00
|
|
|
PR_LOG(nsIThreadLog, PR_LOG_DEBUG,
|
2000-04-21 09:12:57 +04:00
|
|
|
("nsIThreadPool thread %p got request %p\n",
|
|
|
|
currentThread, request));
|
|
|
|
|
1999-04-02 13:20:44 +04:00
|
|
|
return request;
|
|
|
|
}
|
|
|
|
|
1999-10-02 03:30:06 +04:00
|
|
|
NS_METHOD
|
|
|
|
nsThreadPool::Create(nsISupports *aOuter, REFNSIID aIID, void **aResult)
|
|
|
|
{
|
2000-04-21 09:12:57 +04:00
|
|
|
nsThreadPool* pool = new nsThreadPool();
|
1999-10-02 03:30:06 +04:00
|
|
|
if (!pool) return NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
nsresult rv = pool->QueryInterface(aIID, aResult);
|
|
|
|
if (NS_FAILED(rv)) delete pool;
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
1999-04-06 01:02:24 +04:00
|
|
|
NS_IMETHODIMP
|
1999-04-13 22:15:27 +04:00
|
|
|
nsThreadPool::ProcessPendingRequests()
|
1999-04-06 01:02:24 +04:00
|
|
|
{
|
1999-04-13 22:15:27 +04:00
|
|
|
nsresult rv;
|
2000-04-21 09:12:57 +04:00
|
|
|
nsAutoCMonitor mon(this);
|
1999-04-13 22:15:27 +04:00
|
|
|
|
1999-05-13 08:56:04 +04:00
|
|
|
while (PR_TRUE) {
|
|
|
|
PRUint32 cnt;
|
|
|
|
rv = mRequests->Count(&cnt);
|
|
|
|
if (NS_FAILED(rv) || cnt == 0)
|
|
|
|
break;
|
|
|
|
|
2000-04-21 09:12:57 +04:00
|
|
|
rv = mon.Wait();
|
|
|
|
if (NS_FAILED(rv)) { // our thread was interrupted!
|
1999-04-06 01:02:24 +04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2000-04-21 09:12:57 +04:00
|
|
|
#ifdef DEBUG
|
|
|
|
PRUint32 requestCount;
|
|
|
|
(void)mRequests->Count(&requestCount);
|
|
|
|
NS_ASSERTION(requestCount == 0, "not all requests processed");
|
|
|
|
#endif
|
1999-04-13 22:15:27 +04:00
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsThreadPool::Shutdown()
|
|
|
|
{
|
|
|
|
nsresult rv = NS_OK;
|
1999-05-13 08:56:04 +04:00
|
|
|
PRUint32 count = 0;
|
1999-04-13 22:15:27 +04:00
|
|
|
PRUint32 i;
|
1999-04-06 05:42:01 +04:00
|
|
|
|
1999-08-11 12:13:07 +04:00
|
|
|
#if defined(PR_LOGGING)
|
1999-09-21 00:54:23 +04:00
|
|
|
nsCOMPtr<nsIThread> th;
|
|
|
|
nsIThread::GetCurrent(getter_AddRefs(th));
|
1999-08-11 12:13:07 +04:00
|
|
|
#endif
|
|
|
|
PR_LOG(nsIThreadLog, PR_LOG_DEBUG,
|
1999-10-06 12:51:48 +04:00
|
|
|
("nsIThreadPool thread %p shutting down\n", th.get()));
|
1999-08-11 12:13:07 +04:00
|
|
|
|
1999-04-06 05:42:01 +04:00
|
|
|
mShuttingDown = PR_TRUE;
|
2000-04-21 09:12:57 +04:00
|
|
|
rv = ProcessPendingRequests();
|
|
|
|
NS_ASSERTION(NS_SUCCEEDED(rv), "ProcessPendingRequests failed");
|
|
|
|
// keep trying... don't bail with an error here
|
|
|
|
|
1999-04-06 01:02:24 +04:00
|
|
|
// then interrupt the threads and join them
|
1999-05-13 08:56:04 +04:00
|
|
|
rv = mThreads->Count(&count);
|
|
|
|
NS_ASSERTION(NS_SUCCEEDED(rv), "Count failed");
|
2000-04-21 09:12:57 +04:00
|
|
|
if (NS_FAILED(rv)) return rv;
|
1999-04-06 01:02:24 +04:00
|
|
|
for (i = 0; i < count; i++) {
|
1999-06-02 04:07:54 +04:00
|
|
|
nsIThread* thread = (nsIThread*)(mThreads->ElementAt(0));
|
1999-04-13 22:15:27 +04:00
|
|
|
|
|
|
|
// we don't care about the error from Interrupt, because the
|
|
|
|
// thread may have already terminated its event loop
|
|
|
|
(void)thread->Interrupt();
|
|
|
|
|
1999-04-06 01:02:24 +04:00
|
|
|
rv = thread->Join();
|
1999-04-13 22:15:27 +04:00
|
|
|
// don't break out of the loop because of an error here
|
|
|
|
NS_ASSERTION(NS_SUCCEEDED(rv), "Join failed");
|
|
|
|
|
|
|
|
NS_RELEASE(thread);
|
1999-04-06 05:42:01 +04:00
|
|
|
rv = mThreads->RemoveElementAt(0);
|
1999-04-13 22:15:27 +04:00
|
|
|
// don't break out of the loop because of an error here
|
|
|
|
NS_ASSERTION(NS_SUCCEEDED(rv), "RemoveElementAt failed");
|
1999-04-06 01:02:24 +04:00
|
|
|
}
|
2000-04-21 09:12:57 +04:00
|
|
|
mThreads = nsnull;
|
1999-04-06 01:02:24 +04:00
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
1999-10-02 03:30:06 +04:00
|
|
|
NS_IMETHODIMP
|
2000-04-21 09:12:57 +04:00
|
|
|
nsThreadPool::Init(PRUint32 minThreadCount,
|
|
|
|
PRUint32 maxThreadCount,
|
|
|
|
PRUint32 stackSize,
|
1999-10-02 03:30:06 +04:00
|
|
|
PRThreadPriority priority,
|
|
|
|
PRThreadScope scope)
|
|
|
|
{
|
2000-03-06 00:01:33 +03:00
|
|
|
nsresult rv;
|
2000-04-21 09:12:57 +04:00
|
|
|
|
|
|
|
mStackSize = stackSize;
|
|
|
|
mPriority = priority;
|
|
|
|
mScope = scope;
|
|
|
|
NS_ASSERTION(minThreadCount > 0 && minThreadCount <= maxThreadCount, "bad min/max values");
|
|
|
|
mMinThreads = minThreadCount;
|
|
|
|
mMaxThreads = maxThreadCount;
|
2000-03-06 04:13:20 +03:00
|
|
|
|
1999-10-02 03:30:06 +04:00
|
|
|
rv = NS_NewISupportsArray(getter_AddRefs(mThreads));
|
|
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
|
|
|
|
rv = NS_NewISupportsArray(getter_AddRefs(mRequests));
|
|
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
|
|
|
|
mRequestMonitor = PR_NewMonitor();
|
|
|
|
if (mRequestMonitor == nsnull)
|
|
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
|
2000-04-21 09:12:57 +04:00
|
|
|
return rv;
|
|
|
|
}
|
1999-10-02 03:30:06 +04:00
|
|
|
|
2000-03-06 00:01:33 +03:00
|
|
|
|
2000-04-21 09:12:57 +04:00
|
|
|
nsresult
|
|
|
|
nsThreadPool::AddThread()
|
|
|
|
{
|
2000-04-21 09:32:59 +04:00
|
|
|
nsresult rv;
|
2000-04-21 09:12:57 +04:00
|
|
|
nsAutoCMonitor mon(this);
|
2000-03-06 00:01:33 +03:00
|
|
|
|
2000-04-21 09:12:57 +04:00
|
|
|
#ifdef DEBUG
|
|
|
|
PRUint32 cnt;
|
2000-04-21 09:32:59 +04:00
|
|
|
rv = mThreads->Count(&cnt);
|
2000-04-21 09:12:57 +04:00
|
|
|
if (NS_FAILED(rv)) return rv;
|
2000-03-06 00:01:33 +03:00
|
|
|
|
2000-04-21 09:12:57 +04:00
|
|
|
if (cnt >= mMaxThreads)
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
#endif
|
2000-03-06 04:13:20 +03:00
|
|
|
|
2000-04-21 09:12:57 +04:00
|
|
|
nsThreadPoolRunnable* runnable = new nsThreadPoolRunnable(this);
|
|
|
|
if (runnable == nsnull)
|
|
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
NS_ADDREF(runnable);
|
|
|
|
|
|
|
|
nsIThread* thread;
|
|
|
|
rv = NS_NewThread(&thread,
|
|
|
|
runnable,
|
|
|
|
mStackSize,
|
|
|
|
PR_JOINABLE_THREAD, /* needed for Shutdown */
|
|
|
|
mPriority,
|
|
|
|
mScope);
|
|
|
|
NS_RELEASE(runnable);
|
|
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
|
|
|
|
PR_LOG(nsIThreadLog, PR_LOG_DEBUG,
|
|
|
|
("nsIThreadPool adding new thread %p (%d total)\n",
|
|
|
|
thread, cnt + 1));
|
|
|
|
|
|
|
|
// wait for worker thread to be ready
|
|
|
|
rv = mon.Wait();
|
|
|
|
|
|
|
|
if (NS_SUCCEEDED(rv))
|
|
|
|
rv = mThreads->AppendElement(thread) ? NS_OK : NS_ERROR_FAILURE;
|
|
|
|
|
|
|
|
NS_RELEASE(thread);
|
1999-10-02 03:30:06 +04:00
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
1999-05-26 05:38:36 +04:00
|
|
|
NS_COM nsresult
|
1999-04-02 13:20:44 +04:00
|
|
|
NS_NewThreadPool(nsIThreadPool* *result,
|
|
|
|
PRUint32 minThreads, PRUint32 maxThreads,
|
|
|
|
PRUint32 stackSize,
|
|
|
|
PRThreadPriority priority,
|
1999-04-13 22:15:27 +04:00
|
|
|
PRThreadScope scope)
|
1999-04-02 13:20:44 +04:00
|
|
|
{
|
|
|
|
nsresult rv;
|
2000-04-21 09:12:57 +04:00
|
|
|
nsThreadPool* pool = new nsThreadPool();
|
1999-04-02 13:20:44 +04:00
|
|
|
if (pool == nsnull)
|
|
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
1999-09-21 00:54:23 +04:00
|
|
|
NS_ADDREF(pool);
|
1999-04-02 13:20:44 +04:00
|
|
|
|
2000-04-21 09:12:57 +04:00
|
|
|
rv = pool->Init(minThreads, maxThreads, stackSize, priority, scope);
|
1999-04-02 13:20:44 +04:00
|
|
|
if (NS_FAILED(rv)) {
|
1999-09-21 00:54:23 +04:00
|
|
|
NS_RELEASE(pool);
|
1999-04-02 13:20:44 +04:00
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
*result = pool;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
nsThreadPoolRunnable::nsThreadPoolRunnable(nsThreadPool* pool)
|
2000-04-21 09:12:57 +04:00
|
|
|
: mPool(pool)
|
1999-04-02 13:20:44 +04:00
|
|
|
{
|
|
|
|
NS_INIT_REFCNT();
|
|
|
|
}
|
|
|
|
|
|
|
|
nsThreadPoolRunnable::~nsThreadPoolRunnable()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2000-04-21 09:12:57 +04:00
|
|
|
NS_IMPL_THREADSAFE_ISUPPORTS1(nsThreadPoolRunnable, nsIRunnable)
|
1999-04-02 13:20:44 +04:00
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsThreadPoolRunnable::Run()
|
|
|
|
{
|
1999-04-06 01:02:24 +04:00
|
|
|
nsresult rv = NS_OK;
|
|
|
|
nsIRunnable* request;
|
1999-04-02 13:20:44 +04:00
|
|
|
|
1999-04-06 01:02:24 +04:00
|
|
|
// let the thread pool know we're ready
|
2000-04-21 09:12:57 +04:00
|
|
|
{
|
|
|
|
nsAutoCMonitor mon(mPool);
|
|
|
|
mon.Notify();
|
|
|
|
}
|
1999-04-02 13:20:44 +04:00
|
|
|
|
2000-04-21 09:12:57 +04:00
|
|
|
nsCOMPtr<nsIThread> currentThread;
|
|
|
|
nsIThread::GetCurrent(getter_AddRefs(currentThread));
|
|
|
|
|
|
|
|
while ((request = mPool->GetRequest(currentThread)) != nsnull) {
|
1999-06-15 08:57:07 +04:00
|
|
|
PR_LOG(nsIThreadLog, PR_LOG_DEBUG,
|
2000-04-21 09:12:57 +04:00
|
|
|
("nsIThreadPool thread %p running %p\n",
|
|
|
|
currentThread.get(), request));
|
1999-04-06 01:02:24 +04:00
|
|
|
rv = request->Run();
|
1999-04-02 13:20:44 +04:00
|
|
|
NS_ASSERTION(NS_SUCCEEDED(rv), "runnable failed");
|
1999-04-06 01:02:24 +04:00
|
|
|
|
1999-06-15 08:57:07 +04:00
|
|
|
// let the thread pool know we've finished a run
|
2000-04-21 09:12:57 +04:00
|
|
|
{
|
|
|
|
nsAutoCMonitor mon(mPool);
|
|
|
|
mon.Notify();
|
|
|
|
}
|
1999-08-11 12:13:07 +04:00
|
|
|
PR_LOG(nsIThreadLog, PR_LOG_DEBUG,
|
|
|
|
("nsIThreadPool thread %p completed %p status=%x\n",
|
2000-04-21 09:12:57 +04:00
|
|
|
currentThread.get(), request, rv));
|
1999-08-11 12:13:07 +04:00
|
|
|
NS_RELEASE(request);
|
1999-04-02 13:20:44 +04:00
|
|
|
}
|
1999-06-15 08:57:07 +04:00
|
|
|
PR_LOG(nsIThreadLog, PR_LOG_DEBUG,
|
2000-04-21 09:12:57 +04:00
|
|
|
("nsIThreadPool thread %p quitting %p\n",
|
|
|
|
currentThread.get(), this));
|
1999-04-06 01:02:24 +04:00
|
|
|
return rv;
|
1999-04-02 13:20:44 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|