xpcom/glue/BlockingResourceBase.cpp

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

     1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
     2  * vim: sw=4 ts=4 et :
     3  * This Source Code Form is subject to the terms of the Mozilla Public
     4  * License, v. 2.0. If a copy of the MPL was not distributed with this
     5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     7 #include "mozilla/BlockingResourceBase.h"
     9 #ifdef DEBUG
    10 #include "nsAutoPtr.h"
    12 #include "mozilla/CondVar.h"
    13 #include "mozilla/ReentrantMonitor.h"
    14 #include "mozilla/Mutex.h"
    16 #ifdef MOZILLA_INTERNAL_API
    17 #include "GeckoProfiler.h"
    18 #endif //MOZILLA_INTERNAL_API
    20 #endif // ifdef DEBUG
    22 namespace mozilla {
    23 //
    24 // BlockingResourceBase implementation
    25 //
    27 // static members
    28 const char* const BlockingResourceBase::kResourceTypeName[] =
    29 {
    30     // needs to be kept in sync with BlockingResourceType
    31     "Mutex", "ReentrantMonitor", "CondVar"
    32 };
    34 #ifdef DEBUG
    36 PRCallOnceType BlockingResourceBase::sCallOnce;
    37 unsigned BlockingResourceBase::sResourceAcqnChainFrontTPI = (unsigned)-1;
    38 BlockingResourceBase::DDT* BlockingResourceBase::sDeadlockDetector;
    40 bool
    41 BlockingResourceBase::DeadlockDetectorEntry::Print(
    42     const DDT::ResourceAcquisition& aFirstSeen,
    43     nsACString& out,
    44     bool aPrintFirstSeenCx) const
    45 {
    46     CallStack lastAcquisition = mAcquisitionContext; // RACY, but benign
    47     bool maybeCurrentlyAcquired = (CallStack::kNone != lastAcquisition);
    48     CallStack printAcquisition =
    49         (aPrintFirstSeenCx || !maybeCurrentlyAcquired) ?
    50             aFirstSeen.mCallContext : lastAcquisition;
    52     fprintf(stderr, "--- %s : %s",
    53             kResourceTypeName[mType], mName);
    54     out += BlockingResourceBase::kResourceTypeName[mType];
    55     out += " : ";
    56     out += mName;
    58     if (maybeCurrentlyAcquired) {
    59         fputs(" (currently acquired)\n", stderr);
    60         out += " (currently acquired)\n";
    61     }
    63     fputs(" calling context\n", stderr);
    64     printAcquisition.Print(stderr);
    66     return maybeCurrentlyAcquired;
    67 }
    70 BlockingResourceBase::BlockingResourceBase(
    71     const char* aName,
    72     BlockingResourceBase::BlockingResourceType aType)
    73 {
    74     // PR_CallOnce guaranatees that InitStatics is called in a
    75     // thread-safe way
    76     if (PR_SUCCESS != PR_CallOnce(&sCallOnce, InitStatics))
    77         NS_RUNTIMEABORT("can't initialize blocking resource static members");
    79     mDDEntry = new BlockingResourceBase::DeadlockDetectorEntry(aName, aType);
    80     mChainPrev = 0;
    81     sDeadlockDetector->Add(mDDEntry);
    82 }
    85 BlockingResourceBase::~BlockingResourceBase()
    86 {
    87     // we don't check for really obviously bad things like freeing
    88     // Mutexes while they're still locked.  it is assumed that the
    89     // base class, or its underlying primitive, will check for such
    90     // stupid mistakes.
    91     mChainPrev = 0;             // racy only for stupidly buggy client code
    92     mDDEntry = 0;               // owned by deadlock detector
    93 }
    96 void
    97 BlockingResourceBase::CheckAcquire(const CallStack& aCallContext)
    98 {
    99     if (eCondVar == mDDEntry->mType) {
   100         NS_NOTYETIMPLEMENTED(
   101             "FIXME bug 456272: annots. to allow CheckAcquire()ing condvars");
   102         return;
   103     }
   105     BlockingResourceBase* chainFront = ResourceChainFront();
   106     nsAutoPtr<DDT::ResourceAcquisitionArray> cycle(
   107         sDeadlockDetector->CheckAcquisition(
   108             chainFront ? chainFront->mDDEntry : 0, mDDEntry,
   109             aCallContext));
   110     if (!cycle)
   111         return;
   113     fputs("###!!! ERROR: Potential deadlock detected:\n", stderr);
   114     nsAutoCString out("Potential deadlock detected:\n");
   115     bool maybeImminent = PrintCycle(cycle, out);
   117     if (maybeImminent) {
   118         fputs("\n###!!! Deadlock may happen NOW!\n\n", stderr);
   119         out.Append("\n###!!! Deadlock may happen NOW!\n\n");
   120     } else {
   121         fputs("\nDeadlock may happen for some other execution\n\n",
   122               stderr);
   123         out.Append("\nDeadlock may happen for some other execution\n\n");
   124     }
   126     // XXX can customize behavior on whether we /think/ deadlock is
   127     // XXX about to happen.  for example:
   128     // XXX   if (maybeImminent)
   129     //           NS_RUNTIMEABORT(out.get());
   130     NS_ERROR(out.get());
   131 }
   134 void
   135 BlockingResourceBase::Acquire(const CallStack& aCallContext)
   136 {
   137     if (eCondVar == mDDEntry->mType) {
   138         NS_NOTYETIMPLEMENTED(
   139             "FIXME bug 456272: annots. to allow Acquire()ing condvars");
   140         return;
   141     }
   142     NS_ASSERTION(mDDEntry->mAcquisitionContext == CallStack::kNone,
   143                  "reacquiring already acquired resource");
   145     ResourceChainAppend(ResourceChainFront());
   146     mDDEntry->mAcquisitionContext = aCallContext;
   147 }
   150 void
   151 BlockingResourceBase::Release()
   152 {
   153     if (eCondVar == mDDEntry->mType) {
   154         NS_NOTYETIMPLEMENTED(
   155             "FIXME bug 456272: annots. to allow Release()ing condvars");
   156         return;
   157     }
   159     BlockingResourceBase* chainFront = ResourceChainFront();
   160     NS_ASSERTION(chainFront
   161                  && CallStack::kNone != mDDEntry->mAcquisitionContext,
   162                  "Release()ing something that hasn't been Acquire()ed");
   164     if (chainFront == this) {
   165         ResourceChainRemove();
   166     }
   167     else {
   168         // not an error, but makes code hard to reason about.
   169         NS_WARNING("Resource acquired at calling context\n");
   170         mDDEntry->mAcquisitionContext.Print(stderr);
   171         NS_WARNING("\nis being released in non-LIFO order; why?");
   173         // remove this resource from wherever it lives in the chain
   174         // we walk backwards in order of acquisition:
   175         //  (1)  ...node<-prev<-curr...
   176         //              /     /
   177         //  (2)  ...prev<-curr...
   178         BlockingResourceBase* curr = chainFront;
   179         BlockingResourceBase* prev = nullptr;
   180         while (curr && (prev = curr->mChainPrev) && (prev != this))
   181             curr = prev;
   182         if (prev == this)
   183             curr->mChainPrev = prev->mChainPrev;
   184     }
   186     mDDEntry->mAcquisitionContext = CallStack::kNone;
   187 }
   190 bool
   191 BlockingResourceBase::PrintCycle(const DDT::ResourceAcquisitionArray* aCycle,
   192                                  nsACString& out)
   193 {
   194     NS_ASSERTION(aCycle->Length() > 1, "need > 1 element for cycle!");
   196     bool maybeImminent = true;
   198     fputs("=== Cyclical dependency starts at\n", stderr);
   199     out += "Cyclical dependency starts at\n";
   201     const DDT::ResourceAcquisition res = aCycle->ElementAt(0);
   202     maybeImminent &= res.mResource->Print(res, out);
   204     DDT::ResourceAcquisitionArray::index_type i;
   205     DDT::ResourceAcquisitionArray::size_type len = aCycle->Length();
   206     const DDT::ResourceAcquisition* it = 1 + aCycle->Elements();
   207     for (i = 1; i < len - 1; ++i, ++it) {
   208         fputs("\n--- Next dependency:\n", stderr);
   209         out += "\nNext dependency:\n";
   211         maybeImminent &= it->mResource->Print(*it, out);
   212     }
   214     fputs("\n=== Cycle completed at\n", stderr);
   215     out += "Cycle completed at\n";
   216     it->mResource->Print(*it, out, true);
   218     return maybeImminent;
   219 }
   222 //
   223 // Debug implementation of (OffTheBooks)Mutex
   224 void
   225 OffTheBooksMutex::Lock()
   226 {
   227     CallStack callContext = CallStack();
   229     CheckAcquire(callContext);
   230     PR_Lock(mLock);
   231     Acquire(callContext);       // protected by mLock
   232 }
   234 void
   235 OffTheBooksMutex::Unlock()
   236 {
   237     Release();                  // protected by mLock
   238     PRStatus status = PR_Unlock(mLock);
   239     NS_ASSERTION(PR_SUCCESS == status, "bad Mutex::Unlock()");
   240 }
   243 //
   244 // Debug implementation of ReentrantMonitor
   245 void
   246 ReentrantMonitor::Enter()
   247 {
   248     BlockingResourceBase* chainFront = ResourceChainFront();
   250     // the code below implements monitor reentrancy semantics
   252     if (this == chainFront) {
   253         // immediately re-entered the monitor: acceptable
   254         PR_EnterMonitor(mReentrantMonitor);
   255         ++mEntryCount;
   256         return;
   257     }
   259     CallStack callContext = CallStack();
   261     // this is sort of a hack around not recording the thread that
   262     // owns this monitor
   263     if (chainFront) {
   264         for (BlockingResourceBase* br = ResourceChainPrev(chainFront);
   265              br;
   266              br = ResourceChainPrev(br)) {
   267             if (br == this) {
   268                 NS_WARNING(
   269                     "Re-entering ReentrantMonitor after acquiring other resources.\n"
   270                     "At calling context\n");
   271                 GetAcquisitionContext().Print(stderr);
   273                 // show the caller why this is potentially bad
   274                 CheckAcquire(callContext);
   276                 PR_EnterMonitor(mReentrantMonitor);
   277                 ++mEntryCount;
   278                 return;
   279             }
   280         }
   281     }
   283     CheckAcquire(callContext);
   284     PR_EnterMonitor(mReentrantMonitor);
   285     NS_ASSERTION(0 == mEntryCount, "ReentrantMonitor isn't free!");
   286     Acquire(callContext);       // protected by mReentrantMonitor
   287     mEntryCount = 1;
   288 }
   290 void
   291 ReentrantMonitor::Exit()
   292 {
   293     if (0 == --mEntryCount)
   294         Release();              // protected by mReentrantMonitor
   295     PRStatus status = PR_ExitMonitor(mReentrantMonitor);
   296     NS_ASSERTION(PR_SUCCESS == status, "bad ReentrantMonitor::Exit()");
   297 }
   299 nsresult
   300 ReentrantMonitor::Wait(PRIntervalTime interval)
   301 {
   302     AssertCurrentThreadIn();
   304     // save monitor state and reset it to empty
   305     int32_t savedEntryCount = mEntryCount;
   306     CallStack savedAcquisitionContext = GetAcquisitionContext();
   307     BlockingResourceBase* savedChainPrev = mChainPrev;
   308     mEntryCount = 0;
   309     SetAcquisitionContext(CallStack::kNone);
   310     mChainPrev = 0;
   312     nsresult rv;
   313 #ifdef MOZILLA_INTERNAL_API
   314     {
   315         GeckoProfilerSleepRAII profiler_sleep;
   316 #endif //MOZILLA_INTERNAL_API
   318     // give up the monitor until we're back from Wait()
   319     rv = PR_Wait(mReentrantMonitor, interval) == PR_SUCCESS ?
   320             NS_OK : NS_ERROR_FAILURE;
   322 #ifdef MOZILLA_INTERNAL_API
   323     }
   324 #endif //MOZILLA_INTERNAL_API
   326     // restore saved state
   327     mEntryCount = savedEntryCount;
   328     SetAcquisitionContext(savedAcquisitionContext);
   329     mChainPrev = savedChainPrev;
   331     return rv;
   332 }
   335 //
   336 // Debug implementation of CondVar
   337 nsresult
   338 CondVar::Wait(PRIntervalTime interval)
   339 {
   340     AssertCurrentThreadOwnsMutex();
   342     // save mutex state and reset to empty
   343     CallStack savedAcquisitionContext = mLock->GetAcquisitionContext();
   344     BlockingResourceBase* savedChainPrev = mLock->mChainPrev;
   345     mLock->SetAcquisitionContext(CallStack::kNone);
   346     mLock->mChainPrev = 0;
   348     // give up mutex until we're back from Wait()
   349     nsresult rv =
   350         PR_WaitCondVar(mCvar, interval) == PR_SUCCESS ?
   351             NS_OK : NS_ERROR_FAILURE;
   353     // restore saved state
   354     mLock->SetAcquisitionContext(savedAcquisitionContext);
   355     mLock->mChainPrev = savedChainPrev;
   357     return rv;
   358 }
   360 #endif // ifdef DEBUG
   363 } // namespace mozilla

mercurial