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.

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

mercurial