Back to index

lightning-sunbird  0.9+nobinonly
xpcthreadcontext.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
00002  * vim: set ts=8 sw=4 et tw=80:
00003  *
00004  * ***** BEGIN LICENSE BLOCK *****
00005  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00006  *
00007  * The contents of this file are subject to the Mozilla Public License Version
00008  * 1.1 (the "License"); you may not use this file except in compliance with
00009  * the License. You may obtain a copy of the License at
00010  * http://www.mozilla.org/MPL/
00011  *
00012  * Software distributed under the License is distributed on an "AS IS" basis,
00013  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00014  * for the specific language governing rights and limitations under the
00015  * License.
00016  *
00017  * The Original Code is Mozilla Communicator client code, released
00018  * March 31, 1998.
00019  *
00020  * The Initial Developer of the Original Code is
00021  * Netscape Communications Corporation.
00022  * Portions created by the Initial Developer are Copyright (C) 1998
00023  * the Initial Developer. All Rights Reserved.
00024  *
00025  * Contributor(s):
00026  *   John Bandhauer <jband@netscape.com> (original author)
00027  *
00028  * Alternatively, the contents of this file may be used under the terms of
00029  * either of the GNU General Public License Version 2 or later (the "GPL"),
00030  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00031  * in which case the provisions of the GPL or the LGPL are applicable instead
00032  * of those above. If you wish to allow use of your version of this file only
00033  * under the terms of either the GPL or the LGPL, and not to allow others to
00034  * use your version of this file under the terms of the MPL, indicate your
00035  * decision by deleting the provisions above and replace them with the notice
00036  * and other provisions required by the GPL or the LGPL. If you do not delete
00037  * the provisions above, a recipient may use your version of this file under
00038  * the terms of any one of the MPL, the GPL or the LGPL.
00039  *
00040  * ***** END LICENSE BLOCK ***** */
00041 
00042 /* Implement global service to track stack of JSContext per thread. */
00043 
00044 #include "xpcprivate.h"
00045 
00046 /***************************************************************************/
00047 
00048 XPCJSContextStack::XPCJSContextStack()
00049     : mStack(),
00050       mSafeJSContext(nsnull),
00051       mOwnSafeJSContext(nsnull)
00052 {
00053     // empty...
00054 }
00055 
00056 XPCJSContextStack::~XPCJSContextStack()
00057 {
00058     if(mOwnSafeJSContext)
00059     {
00060         JS_SetContextThread(mOwnSafeJSContext);
00061         JS_DestroyContext(mOwnSafeJSContext);
00062         mOwnSafeJSContext = nsnull;
00063         SyncJSContexts();
00064     }
00065 }
00066 
00067 void
00068 XPCJSContextStack::SyncJSContexts()
00069 {
00070     nsXPConnect* xpc = nsXPConnect::GetXPConnect();
00071     if(xpc)
00072         xpc->SyncJSContexts();
00073 }
00074 
00075 /* readonly attribute PRInt32 count; */
00076 NS_IMETHODIMP
00077 XPCJSContextStack::GetCount(PRInt32 *aCount)
00078 {
00079     *aCount = mStack.Length();
00080     return NS_OK;
00081 }
00082 
00083 /* JSContext peek (); */
00084 NS_IMETHODIMP
00085 XPCJSContextStack::Peek(JSContext * *_retval)
00086 {
00087     *_retval = mStack.IsEmpty() ? nsnull : mStack[mStack.Length() - 1].cx;
00088     return NS_OK;
00089 }
00090 
00091 /* JSContext pop (); */
00092 NS_IMETHODIMP
00093 XPCJSContextStack::Pop(JSContext * *_retval)
00094 {
00095     NS_ASSERTION(!mStack.IsEmpty(), "ThreadJSContextStack underflow");
00096 
00097     PRUint32 idx = mStack.Length() - 1; // The thing we're popping
00098     NS_ASSERTION(!mStack[idx].frame,
00099                  "Shouldn't have a pending frame to restore on the context "
00100                  "we're popping!");
00101 
00102     if(_retval)
00103         *_retval = mStack[idx].cx;
00104 
00105     mStack.RemoveElementAt(idx);
00106     if(idx > 0)
00107     {
00108         --idx; // Advance to new top of the stack
00109         JSContextAndFrame & e = mStack[idx];
00110         NS_ASSERTION(!e.frame || e.cx, "Shouldn't have frame without a cx!");
00111         if(e.cx && e.frame)
00112         {
00113             JS_RestoreFrameChain(e.cx, e.frame);
00114             e.frame = nsnull;
00115         }
00116     }
00117     return NS_OK;
00118 }
00119 
00120 /* void push (in JSContext cx); */
00121 NS_IMETHODIMP
00122 XPCJSContextStack::Push(JSContext * cx)
00123 {
00124     if(!mStack.AppendElement(cx))
00125         return NS_ERROR_OUT_OF_MEMORY;
00126     if(mStack.Length() > 1)
00127     {
00128         JSContextAndFrame & e = mStack[mStack.Length() - 2];
00129         if(e.cx && e.cx != cx)
00130             e.frame = JS_SaveFrameChain(e.cx);
00131     }
00132     return NS_OK;
00133 }
00134 
00135 #ifdef DEBUG
00136 JSBool 
00137 XPCJSContextStack::DEBUG_StackHasJSContext(JSContext*  aJSContext)
00138 {
00139     for(PRUint32 i = 0; i < mStack.Length(); i++)
00140         if(aJSContext == mStack[i].cx)
00141             return JS_TRUE;
00142     return JS_FALSE;
00143 }
00144 #endif
00145 
00146 JS_STATIC_DLL_CALLBACK(JSBool)
00147 SafeGlobalResolve(JSContext *cx, JSObject *obj, jsval id)
00148 {
00149     JSBool resolved;
00150     return JS_ResolveStandardClass(cx, obj, id, &resolved);
00151 }
00152 
00153 static JSClass global_class = {
00154     "global_for_XPCJSContextStack_SafeJSContext", 0,
00155     JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
00156     JS_EnumerateStub, SafeGlobalResolve, JS_ConvertStub, JS_FinalizeStub,
00157     JSCLASS_NO_OPTIONAL_MEMBERS
00158 };
00159 
00160 /* attribute JSContext safeJSContext; */
00161 NS_IMETHODIMP
00162 XPCJSContextStack::GetSafeJSContext(JSContext * *aSafeJSContext)
00163 {
00164     if(!mSafeJSContext)
00165     {
00166         JSRuntime *rt;
00167         XPCJSRuntime* xpcrt;
00168 
00169         nsXPConnect* xpc = nsXPConnect::GetXPConnect();
00170         nsCOMPtr<nsIXPConnect> xpcholder(NS_STATIC_CAST(nsIXPConnect*, xpc));
00171 
00172         if(xpc && (xpcrt = xpc->GetRuntime()) && (rt = xpcrt->GetJSRuntime()))
00173         {
00174             mSafeJSContext = JS_NewContext(rt, 8192);
00175             if(mSafeJSContext)
00176             {
00177                 // scoped JS Request
00178                 AutoJSRequestWithNoCallContext req(mSafeJSContext);
00179                 JSObject *glob;
00180                 glob = JS_NewObject(mSafeJSContext, &global_class, NULL, NULL);
00181                 if(!glob || NS_FAILED(xpc->InitClasses(mSafeJSContext, glob)))
00182                 {
00183                     // Explicitly end the request since we are about to kill
00184                     // the JSContext that 'req' will try to use when it
00185                     // goes out of scope.
00186                     req.EndRequest();
00187                     JS_DestroyContext(mSafeJSContext);
00188                     mSafeJSContext = nsnull;
00189                 }
00190                 // Save it off so we can destroy it later, even if
00191                 // mSafeJSContext has been set to another context
00192                 // via SetSafeJSContext.  If we don't get here,
00193                 // then mSafeJSContext must have been set via
00194                 // SetSafeJSContext, and we're not responsible for
00195                 // destroying the passed-in context.
00196                 mOwnSafeJSContext = mSafeJSContext;
00197             }
00198         }
00199     }
00200 
00201     *aSafeJSContext = mSafeJSContext;
00202     return mSafeJSContext ? NS_OK : NS_ERROR_UNEXPECTED;
00203 }
00204 
00205 NS_IMETHODIMP
00206 XPCJSContextStack::SetSafeJSContext(JSContext * aSafeJSContext)
00207 {
00208     if(mOwnSafeJSContext &&
00209        mOwnSafeJSContext == mSafeJSContext &&
00210        mOwnSafeJSContext != aSafeJSContext)
00211     {
00212         JS_DestroyContext(mOwnSafeJSContext);
00213         mOwnSafeJSContext = nsnull;
00214         SyncJSContexts();
00215     }
00216 
00217     mSafeJSContext = aSafeJSContext;
00218     return NS_OK;
00219 }
00220 
00221 /***************************************************************************/
00222 
00223 /*
00224  * nsXPCThreadJSContextStackImpl holds state that we don't want to lose!
00225  *
00226  * The plan is that once created nsXPCThreadJSContextStackImpl never goes
00227  * away until FreeSingleton is called. We do an intentional extra addref at
00228  * construction to keep it around even if no one is using it.
00229  */
00230 
00231 NS_IMPL_THREADSAFE_ISUPPORTS3(nsXPCThreadJSContextStackImpl,
00232                               nsIThreadJSContextStack,
00233                               nsIJSContextStack,
00234                               nsISupportsWeakReference)
00235 
00236 nsXPCThreadJSContextStackImpl*
00237 nsXPCThreadJSContextStackImpl::gXPCThreadJSContextStack = nsnull;
00238 
00239 nsXPCThreadJSContextStackImpl::nsXPCThreadJSContextStackImpl()
00240 {
00241 }
00242 
00243 nsXPCThreadJSContextStackImpl::~nsXPCThreadJSContextStackImpl()
00244 {
00245     gXPCThreadJSContextStack = nsnull;
00246 }
00247 
00248 //static
00249 nsXPCThreadJSContextStackImpl*
00250 nsXPCThreadJSContextStackImpl::GetSingleton()
00251 {
00252     if(!gXPCThreadJSContextStack)
00253     {
00254         gXPCThreadJSContextStack = new nsXPCThreadJSContextStackImpl();
00255         // hold an extra reference to lock it down
00256         NS_IF_ADDREF(gXPCThreadJSContextStack);
00257     }
00258     NS_IF_ADDREF(gXPCThreadJSContextStack);
00259 
00260     return gXPCThreadJSContextStack;
00261 }
00262 
00263 void
00264 nsXPCThreadJSContextStackImpl::FreeSingleton()
00265 {
00266     nsXPCThreadJSContextStackImpl* tcs = gXPCThreadJSContextStack;
00267     if(tcs)
00268     {
00269         nsrefcnt cnt;
00270         NS_RELEASE2(tcs, cnt);
00271 #ifdef XPC_DUMP_AT_SHUTDOWN
00272         if(0 != cnt)
00273             printf("*** dangling reference to nsXPCThreadJSContextStackImpl: refcnt=%d\n", cnt);
00274 #endif
00275     }
00276 }
00277 
00278 /* readonly attribute PRInt32 Count; */
00279 NS_IMETHODIMP
00280 nsXPCThreadJSContextStackImpl::GetCount(PRInt32 *aCount)
00281 {
00282     if(!aCount)
00283         return NS_ERROR_NULL_POINTER;
00284 
00285     XPCJSContextStack* myStack = GetStackForCurrentThread();
00286 
00287     if(!myStack)
00288     {
00289         *aCount = 0;
00290         return NS_ERROR_FAILURE;
00291     }
00292 
00293     return myStack->GetCount(aCount);
00294 }
00295 
00296 /* JSContext Peek (); */
00297 NS_IMETHODIMP
00298 nsXPCThreadJSContextStackImpl::Peek(JSContext * *_retval)
00299 {
00300     if(!_retval)
00301         return NS_ERROR_NULL_POINTER;
00302 
00303     XPCJSContextStack* myStack = GetStackForCurrentThread();
00304 
00305     if(!myStack)
00306     {
00307         *_retval = nsnull;
00308         return NS_ERROR_FAILURE;
00309     }
00310 
00311     return myStack->Peek(_retval);
00312 }
00313 
00314 /* JSContext Pop (); */
00315 NS_IMETHODIMP
00316 nsXPCThreadJSContextStackImpl::Pop(JSContext * *_retval)
00317 {
00318     XPCJSContextStack* myStack = GetStackForCurrentThread();
00319 
00320     if(!myStack)
00321     {
00322         if(_retval)
00323             *_retval = nsnull;
00324         return NS_ERROR_FAILURE;
00325     }
00326 
00327     return myStack->Pop(_retval);
00328 }
00329 
00330 /* void Push (in JSContext cx); */
00331 NS_IMETHODIMP
00332 nsXPCThreadJSContextStackImpl::Push(JSContext * cx)
00333 {
00334     XPCJSContextStack* myStack = GetStackForCurrentThread();
00335 
00336     if(!myStack)
00337         return NS_ERROR_FAILURE;
00338 
00339     return myStack->Push(cx);
00340 }
00341 
00342 /* readonly attribute JSContext SafeJSContext; */
00343 NS_IMETHODIMP
00344 nsXPCThreadJSContextStackImpl::GetSafeJSContext(JSContext * *aSafeJSContext)
00345 {
00346     NS_ASSERTION(aSafeJSContext, "loser!");
00347 
00348     XPCJSContextStack* myStack = GetStackForCurrentThread();
00349 
00350     if(!myStack)
00351     {
00352         *aSafeJSContext = nsnull;
00353         return NS_ERROR_FAILURE;
00354     }
00355 
00356     return myStack->GetSafeJSContext(aSafeJSContext);
00357 }
00358 
00359 
00360 NS_IMETHODIMP
00361 nsXPCThreadJSContextStackImpl::SetSafeJSContext(JSContext * aSafeJSContext)
00362 {
00363     XPCJSContextStack* myStack = GetStackForCurrentThread();
00364 
00365     if(!myStack)
00366         return NS_ERROR_FAILURE;
00367 
00368     return myStack->SetSafeJSContext(aSafeJSContext);
00369 }
00370 
00371 /***************************************************************************/
00372 
00373 PRUintn           XPCPerThreadData::gTLSIndex = BAD_TLS_INDEX;
00374 PRLock*           XPCPerThreadData::gLock     = nsnull;
00375 XPCPerThreadData* XPCPerThreadData::gThreads  = nsnull;
00376 
00377 static jsuword
00378 GetThreadStackLimit()
00379 {
00380     int stackDummy;
00381     jsuword stackLimit, currentStackAddr = (jsuword)&stackDummy;
00382 
00383     const jsuword kStackSize = 0x80000;   // 512k
00384 
00385 #if JS_STACK_GROWTH_DIRECTION < 0
00386     stackLimit = (currentStackAddr > kStackSize)
00387                  ? currentStackAddr - kStackSize
00388                  : 0;
00389 #else
00390     stackLimit = (currentStackAddr + kStackSize > currentStackAddr)
00391                  ? currentStackAddr + kStackSize
00392                  : (jsuword) -1;
00393 #endif
00394 
00395   return stackLimit;
00396 }
00397 
00398 XPCPerThreadData::XPCPerThreadData()
00399     :   mJSContextStack(new XPCJSContextStack()),
00400         mNextThread(nsnull),
00401         mCallContext(nsnull),
00402         mResolveName(0),
00403         mResolvingWrapper(nsnull),
00404         mMostRecentJSContext(nsnull),
00405         mMostRecentXPCContext(nsnull),
00406         mExceptionManager(nsnull),
00407         mException(nsnull),
00408         mExceptionManagerNotAvailable(JS_FALSE),
00409         mAutoRoots(nsnull),
00410         mStackLimit(GetThreadStackLimit())
00411 #ifdef XPC_CHECK_WRAPPER_THREADSAFETY
00412       , mWrappedNativeThreadsafetyReportDepth(0)
00413 #endif
00414 {
00415     if(gLock)
00416     {
00417         nsAutoLock lock(gLock);
00418         mNextThread = gThreads;
00419         gThreads = this;
00420     }
00421 }
00422 
00423 void
00424 XPCPerThreadData::Cleanup()
00425 {
00426     while(mAutoRoots)
00427         mAutoRoots->Unlink();
00428     NS_IF_RELEASE(mExceptionManager);
00429     NS_IF_RELEASE(mException);
00430     delete mJSContextStack;
00431     mJSContextStack = nsnull;
00432 
00433     if(mCallContext)
00434         mCallContext->SystemIsBeingShutDown();
00435 }
00436 
00437 XPCPerThreadData::~XPCPerThreadData()
00438 {
00439     Cleanup();
00440 
00441     // Unlink 'this' from the list of threads.
00442     if(gLock)
00443     {
00444         nsAutoLock lock(gLock);
00445         if(gThreads == this)
00446             gThreads = mNextThread;
00447         else
00448         {
00449             XPCPerThreadData* cur = gThreads;
00450             while(cur)
00451             {
00452                 if(cur->mNextThread == this)
00453                 {
00454                     cur->mNextThread = mNextThread;
00455                     break;
00456                 }
00457                 cur = cur->mNextThread;
00458             }
00459         }
00460     }
00461 
00462     if(gLock && !gThreads)
00463     {
00464         PR_DestroyLock(gLock);
00465         gLock = nsnull;
00466     }
00467 }
00468 
00469 PR_STATIC_CALLBACK(void)
00470 xpc_ThreadDataDtorCB(void* ptr)
00471 {
00472     XPCPerThreadData* data = (XPCPerThreadData*) ptr;
00473     if(data)
00474         delete data;
00475 }
00476 
00477 void XPCPerThreadData::MarkAutoRootsBeforeJSFinalize(JSContext* cx)
00478 {
00479 #ifdef XPC_TRACK_AUTOMARKINGPTR_STATS
00480     {
00481         static int maxLength = 0;
00482         int length = 0;
00483         for(AutoMarkingPtr* p = mAutoRoots; p; p = p->GetNext())
00484             length++;
00485         if(length > maxLength)
00486             maxLength = length;
00487         printf("XPC gc on thread %x with %d AutoMarkingPtrs (%d max so far)\n",
00488                this, length, maxLength);
00489     }
00490 #endif
00491 
00492     if(mAutoRoots)
00493         mAutoRoots->MarkBeforeJSFinalize(cx);
00494 }
00495 
00496 void XPCPerThreadData::MarkAutoRootsAfterJSFinalize()
00497 {
00498     if(mAutoRoots)
00499         mAutoRoots->MarkAfterJSFinalize();
00500 }
00501 
00502 // static
00503 XPCPerThreadData*
00504 XPCPerThreadData::GetData()
00505 {
00506     XPCPerThreadData* data;
00507 
00508     if(!gLock)
00509     {
00510         gLock = PR_NewLock();
00511         if(!gLock)
00512             return nsnull;
00513     }
00514 
00515     if(gTLSIndex == BAD_TLS_INDEX)
00516     {
00517         nsAutoLock lock(gLock);
00518         // check again now that we have the lock...
00519         if(gTLSIndex == BAD_TLS_INDEX)
00520         {
00521             if(PR_FAILURE ==
00522                PR_NewThreadPrivateIndex(&gTLSIndex, xpc_ThreadDataDtorCB))
00523             {
00524                 NS_ASSERTION(0, "PR_NewThreadPrivateIndex failed!");
00525                 gTLSIndex = BAD_TLS_INDEX;
00526                 return nsnull;
00527             }
00528         }
00529     }
00530 
00531     data = (XPCPerThreadData*) PR_GetThreadPrivate(gTLSIndex);
00532     if(!data)
00533     {
00534         data = new XPCPerThreadData();
00535         if(!data || !data->IsValid())
00536         {
00537             NS_ASSERTION(0, "new XPCPerThreadData() failed!");
00538             if(data)
00539                 delete data;
00540             return nsnull;
00541         }
00542         if(PR_FAILURE == PR_SetThreadPrivate(gTLSIndex, data))
00543         {
00544             NS_ASSERTION(0, "PR_SetThreadPrivate failed!");
00545             delete data;
00546             return nsnull;
00547         }
00548     }
00549     return data;
00550 }
00551 
00552 // static
00553 void
00554 XPCPerThreadData::CleanupAllThreads()
00555 {
00556     // I've questioned the sense of cleaning up other threads' data from the
00557     // start. But I got talked into it. Now I see that we *can't* do all the
00558     // cleaup while holding this lock. So, we are going to go to the trouble
00559     // to copy out the data that needs to be cleaned up *outside* of
00560     // the lock. Yuk!
00561 
00562     XPCJSContextStack** stacks = nsnull;
00563     int count = 0;
00564     int i;
00565 
00566     if(gLock)
00567     {
00568         nsAutoLock lock(gLock);
00569 
00570         for(XPCPerThreadData* cur = gThreads; cur; cur = cur->mNextThread)
00571             count++;
00572 
00573         stacks = (XPCJSContextStack**) new XPCJSContextStack*[count] ;
00574         if(stacks)
00575         {
00576             i = 0;
00577             for(XPCPerThreadData* cur = gThreads; cur; cur = cur->mNextThread)
00578             {
00579                 stacks[i++] = cur->mJSContextStack;
00580                 cur->mJSContextStack = nsnull;
00581                 cur->Cleanup();
00582             }
00583         }
00584     }
00585 
00586     if(stacks)
00587     {
00588         for(i = 0; i < count; i++)
00589             delete stacks[i];
00590         delete [] stacks;
00591     }
00592 
00593     if(gTLSIndex != BAD_TLS_INDEX)
00594         PR_SetThreadPrivate(gTLSIndex, nsnull);
00595 }
00596 
00597 // static
00598 XPCPerThreadData*
00599 XPCPerThreadData::IterateThreads(XPCPerThreadData** iteratorp)
00600 {
00601     *iteratorp = (*iteratorp == nsnull) ? gThreads : (*iteratorp)->mNextThread;
00602     return *iteratorp;
00603 }
00604 
00605 NS_IMPL_ISUPPORTS1(nsXPCJSContextStackIterator, nsIJSContextStackIterator)
00606 
00607 NS_IMETHODIMP
00608 nsXPCJSContextStackIterator::Reset(nsIJSContextStack *aStack)
00609 {
00610     // XXX This is pretty ugly.
00611     nsXPCThreadJSContextStackImpl *impl =
00612         NS_STATIC_CAST(nsXPCThreadJSContextStackImpl*, aStack);
00613     XPCJSContextStack *stack = impl->GetStackForCurrentThread();
00614     if(!stack)
00615         return NS_ERROR_FAILURE;
00616     mStack = stack->GetStack();
00617     if(mStack->IsEmpty())
00618         mStack = nsnull;
00619     else
00620         mPosition = mStack->Length() - 1;
00621 
00622     return NS_OK;
00623 }
00624 
00625 NS_IMETHODIMP
00626 nsXPCJSContextStackIterator::Done(PRBool *aDone)
00627 {
00628     *aDone = !mStack;
00629     return NS_OK;
00630 }
00631 
00632 NS_IMETHODIMP
00633 nsXPCJSContextStackIterator::Prev(JSContext **aContext)
00634 {
00635     if(!mStack)
00636         return NS_ERROR_NOT_INITIALIZED;
00637 
00638     *aContext = mStack->ElementAt(mPosition).cx;
00639 
00640     if(mPosition == 0)
00641         mStack = nsnull;
00642     else
00643         --mPosition;
00644     
00645     return NS_OK;
00646 }
00647