LCOV - code coverage report
Current view: directory - xpcom/glue - DeadlockDetector.h (source / functions) Found Hit Coverage
Test: app.info Lines: 3 0 0.0 %
Date: 2012-06-02 Functions: 1 0 0.0 %

       1                 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
       2                 :  * vim: sw=4 ts=4 et :
       3                 :  * ***** BEGIN LICENSE BLOCK *****
       4                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
       5                 :  *
       6                 :  * The contents of this file are subject to the Mozilla Public License Version
       7                 :  * 1.1 (the "License"); you may not use this file except in compliance with
       8                 :  * the License. You may obtain a copy of the License at
       9                 :  * http://www.mozilla.org/MPL/
      10                 :  *
      11                 :  * Software distributed under the License is distributed on an "AS IS" basis,
      12                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
      13                 :  * for the specific language governing rights and limitations under the
      14                 :  * License.
      15                 :  *
      16                 :  * The Original Code is mozilla.org code.
      17                 :  *
      18                 :  * The Initial Developer of the Original Code is
      19                 :  * Netscape Communications Corporation.
      20                 :  * Portions created by the Initial Developer are Copyright (C) 1998
      21                 :  * the Initial Developer. All Rights Reserved.
      22                 :  *
      23                 :  * Contributor(s):
      24                 :  *   Chris Jones <jones.chris.g@gmail.com>
      25                 :  *
      26                 :  * Alternatively, the contents of this file may be used under the terms of
      27                 :  * either of the GNU General Public License Version 2 or later (the "GPL"),
      28                 :  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      29                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      30                 :  * of those above. If you wish to allow use of your version of this file only
      31                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      32                 :  * use your version of this file under the terms of the MPL, indicate your
      33                 :  * decision by deleting the provisions above and replace them with the notice
      34                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      35                 :  * the provisions above, a recipient may use your version of this file under
      36                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      37                 :  *
      38                 :  * ***** END LICENSE BLOCK ***** */
      39                 : #ifndef mozilla_DeadlockDetector_h
      40                 : #define mozilla_DeadlockDetector_h
      41                 : 
      42                 : #include "mozilla/Attributes.h"
      43                 : 
      44                 : #include <stdlib.h>
      45                 : 
      46                 : #include "plhash.h"
      47                 : #include "prlock.h"
      48                 : 
      49                 : #include "nsTArray.h"
      50                 : 
      51                 : #ifdef NS_TRACE_MALLOC
      52                 : #  include "nsTraceMalloc.h"
      53                 : #endif  // ifdef NS_TRACE_MALLOC
      54                 : 
      55                 : namespace mozilla {
      56                 : 
      57                 : 
      58                 : // FIXME bug 456272: split this off into a convenience API on top of
      59                 : // nsStackWalk?
      60                 : class NS_COM_GLUE CallStack
      61                 : {
      62                 : private:
      63                 : #ifdef NS_TRACE_MALLOC
      64                 :     typedef nsTMStackTraceID callstack_id;
      65                 :     // needs to be a macro to avoid disturbing the backtrace
      66                 : #   define NS_GET_BACKTRACE() NS_TraceMallocGetStackTrace()
      67                 : #else
      68                 :     typedef void* callstack_id;
      69                 : #   define NS_GET_BACKTRACE() 0
      70                 : #endif  // ifdef NS_TRACE_MALLOC
      71                 : 
      72                 :     callstack_id mCallStack;
      73                 : 
      74                 : public:
      75                 :     /**
      76                 :      * CallStack
      77                 :      * *ALWAYS* *ALWAYS* *ALWAYS* call this with no arguments.  This
      78                 :      * constructor takes an argument *ONLY* so that |GET_BACKTRACE()|
      79                 :      * can be evaluated in the stack frame of the caller, rather than
      80                 :      * that of the constructor.
      81                 :      *
      82                 :      * *BEWARE*: this means that calling this constructor with no
      83                 :      * arguments is not the same as a "default, do-nothing"
      84                 :      * constructor: it *will* construct a backtrace.  This can cause
      85                 :      * unexpected performance issues.
      86                 :      */
      87               0 :     CallStack(const callstack_id aCallStack = NS_GET_BACKTRACE()) :
      88               0 :         mCallStack(aCallStack)
      89                 :     {
      90               0 :     }
      91                 :     CallStack(const CallStack& aFrom) :
      92                 :         mCallStack(aFrom.mCallStack)
      93                 :     {
      94                 :     }
      95                 :     CallStack& operator=(const CallStack& aFrom)
      96                 :     {
      97                 :         mCallStack = aFrom.mCallStack;
      98                 :         return *this;
      99                 :     }
     100                 :     bool operator==(const CallStack& aOther) const
     101                 :     {
     102                 :         return mCallStack == aOther.mCallStack;
     103                 :     }
     104                 :     bool operator!=(const CallStack& aOther) const
     105                 :     {
     106                 :         return mCallStack != aOther.mCallStack;
     107                 :     }
     108                 : 
     109                 :     // FIXME bug 456272: if this is split off,
     110                 :     // NS_TraceMallocPrintStackTrace should be modified to print into
     111                 :     // an nsACString
     112                 :     void Print(FILE* f) const
     113                 :     {
     114                 : #ifdef NS_TRACE_MALLOC
     115                 :         if (this != &kNone && mCallStack) {
     116                 :             NS_TraceMallocPrintStackTrace(f, mCallStack);
     117                 :             return;
     118                 :         }
     119                 : #endif
     120                 :         fputs("  [stack trace unavailable]\n", f);
     121                 :     }
     122                 : 
     123                 :     /** The "null" callstack. */
     124                 :     static const CallStack kNone;
     125                 : };
     126                 : 
     127                 : 
     128                 : /**
     129                 :  * DeadlockDetector
     130                 :  *
     131                 :  * The following is an approximate description of how the deadlock detector
     132                 :  * works.
     133                 :  *
     134                 :  * The deadlock detector ensures that all blocking resources are
     135                 :  * acquired according to a partial order P.  One type of blocking
     136                 :  * resource is a lock.  If a lock l1 is acquired (locked) before l2,
     137                 :  * then we say that |l1 <_P l2|.  The detector flags an error if two
     138                 :  * locks l1 and l2 have an inconsistent ordering in P; that is, if
     139                 :  * both |l1 <_P l2| and |l2 <_P l1|.  This is a potential error
     140                 :  * because a thread acquiring l1,l2 according to the first order might
     141                 :  * race with a thread acquiring them according to the second order.
     142                 :  * If this happens under the right conditions, then the acquisitions
     143                 :  * will deadlock.
     144                 :  *
     145                 :  * This deadlock detector doesn't know at compile-time what P is.  So,
     146                 :  * it tries to discover the order at run time.  More precisely, it
     147                 :  * finds <i>some</i> order P, then tries to find chains of resource
     148                 :  * acquisitions that violate P.  An example acquisition sequence, and
     149                 :  * the orders they impose, is
     150                 :  *   l1.lock()   // current chain: [ l1 ]
     151                 :  *               // order: { }
     152                 :  *
     153                 :  *   l2.lock()   // current chain: [ l1, l2 ]
     154                 :  *               // order: { l1 <_P l2 }
     155                 :  *
     156                 :  *   l3.lock()   // current chain: [ l1, l2, l3 ]
     157                 :  *               // order: { l1 <_P l2, l2 <_P l3, l1 <_P l3 }
     158                 :  *               // (note: <_P is transitive, so also |l1 <_P l3|)
     159                 :  *
     160                 :  *   l2.unlock() // current chain: [ l1, l3 ]
     161                 :  *               // order: { l1 <_P l2, l2 <_P l3, l1 <_P l3 }
     162                 :  *               // (note: it's OK, but weird, that l2 was unlocked out
     163                 :  *               //  of order.  we still have l1 <_P l3).
     164                 :  *
     165                 :  *   l2.lock()   // current chain: [ l1, l3, l2 ]
     166                 :  *               // order: { l1 <_P l2, l2 <_P l3, l1 <_P l3,
     167                 :  *                                      l3 <_P l2 (!!!) }
     168                 :  * BEEP BEEP!  Here the detector will flag a potential error, since
     169                 :  * l2 and l3 were used inconsistently (and potentially in ways that
     170                 :  * would deadlock).
     171                 :  */
     172                 : template <typename T>
     173                 : class DeadlockDetector
     174                 : {
     175                 : public:
     176                 :     /**
     177                 :      * ResourceAcquisition
     178                 :      * Consists simply of a resource and the calling context from
     179                 :      * which it was acquired.  We pack this information together so
     180                 :      * that it can be returned back to the caller when a potential
     181                 :      * deadlock has been found.
     182                 :      */
     183                 :     struct ResourceAcquisition
     184                 :     {
     185                 :         const T* mResource;
     186                 :         CallStack mCallContext;
     187                 : 
     188                 :         ResourceAcquisition(
     189                 :             const T* aResource,
     190                 :             const CallStack aCallContext=CallStack::kNone) :
     191                 :             mResource(aResource),
     192                 :             mCallContext(aCallContext)
     193                 :         {
     194                 :         }
     195                 :         ResourceAcquisition(const ResourceAcquisition& aFrom) :
     196                 :             mResource(aFrom.mResource),
     197                 :             mCallContext(aFrom.mCallContext)
     198                 :         {
     199                 :         }
     200                 :         ResourceAcquisition& operator=(const ResourceAcquisition& aFrom)
     201                 :         {
     202                 :             mResource = aFrom.mResource;
     203                 :             mCallContext = aFrom.mCallContext;
     204                 :             return *this;
     205                 :         }
     206                 :     };
     207                 :     typedef nsTArray<ResourceAcquisition> ResourceAcquisitionArray;
     208                 : 
     209                 : private:
     210                 :     typedef nsTArray<PLHashEntry*> HashEntryArray;
     211                 :     typedef typename HashEntryArray::index_type index_type;
     212                 :     typedef typename HashEntryArray::size_type size_type;
     213                 :     enum {
     214                 :         NoIndex = HashEntryArray::NoIndex
     215                 :     };
     216                 : 
     217                 :     /**
     218                 :      * Value type for the ordering table.  Contains the other
     219                 :      * resources on which an ordering constraint |key < other|
     220                 :      * exists.  The catch is that we also store the calling context at
     221                 :      * which the other resource was acquired; this improves the
     222                 :      * quality of error messages when potential deadlock is detected.
     223                 :      */
     224                 :     struct OrderingEntry
     225                 :     {
     226                 :         OrderingEntry() :
     227                 :             mFirstSeen(CallStack::kNone),
     228                 :             mOrderedLT()        // FIXME bug 456272: set to empirical
     229                 :         {                       // dep size?
     230                 :         }
     231                 :         ~OrderingEntry()
     232                 :         {
     233                 :         }
     234                 : 
     235                 :         CallStack mFirstSeen; // first site from which the resource appeared
     236                 :         HashEntryArray mOrderedLT; // this <_o Other
     237                 :     };
     238                 : 
     239                 :     static void* TableAlloc(void* /*pool*/, PRSize size)
     240                 :     {
     241                 :         return operator new(size);
     242                 :     }
     243                 :     static void TableFree(void* /*pool*/, void* item)
     244                 :     {
     245                 :         operator delete(item);
     246                 :     }
     247                 :     static PLHashEntry* EntryAlloc(void* /*pool*/, const void* key)
     248                 :     {
     249                 :         return new PLHashEntry;
     250                 :     }
     251                 :     static void EntryFree(void* /*pool*/, PLHashEntry* entry, PRUintn flag)
     252                 :     {
     253                 :         delete static_cast<T*>(const_cast<void*>(entry->key));
     254                 :         delete static_cast<OrderingEntry*>(entry->value);
     255                 :         entry->value = 0;
     256                 :         if (HT_FREE_ENTRY == flag)
     257                 :             delete entry;
     258                 :     }
     259                 :     static PLHashNumber HashKey(const void* aKey)
     260                 :     {
     261                 :         return NS_PTR_TO_INT32(aKey) >> 2;
     262                 :     }
     263                 :     static const PLHashAllocOps kAllocOps;
     264                 : 
     265                 :     // Hash table "interface" the rest of the code should use
     266                 : 
     267                 :     PLHashEntry** GetEntry(const T* aKey)
     268                 :     {
     269                 :         return PL_HashTableRawLookup(mOrdering, HashKey(aKey), aKey);
     270                 :     }
     271                 : 
     272                 :     void PutEntry(T* aKey)
     273                 :     {
     274                 :         PL_HashTableAdd(mOrdering, aKey, new OrderingEntry());
     275                 :     }
     276                 : 
     277                 :     // XXX need these helper methods because OrderingEntry doesn't have
     278                 :     // XXX access to underlying PLHashEntry
     279                 : 
     280                 :     /**
     281                 :      * Add the order |aFirst <_o aSecond|.
     282                 :      *
     283                 :      * WARNING: this does not check whether it's sane to add this
     284                 :      * order.  In the "best" bad case, when this order already exists,
     285                 :      * adding it anyway may unnecessarily result in O(n^2) space.  In
     286                 :      * the "worst" bad case, adding it anyway will cause
     287                 :      * |InTransitiveClosure()| to diverge.
     288                 :      */
     289                 :     void AddOrder(PLHashEntry* aLT, PLHashEntry* aGT)
     290                 :     {
     291                 :         static_cast<OrderingEntry*>(aLT->value)->mOrderedLT
     292                 :             .InsertElementSorted(aGT);
     293                 :     }
     294                 : 
     295                 :     /**
     296                 :      * Return true iff the order |aFirst < aSecond| has been
     297                 :      * *explicitly* added.
     298                 :      *
     299                 :      * Does not consider transitivity.
     300                 :      */
     301                 :     bool IsOrdered(const PLHashEntry* aFirst, const PLHashEntry* aSecond)
     302                 :         const
     303                 :     {
     304                 :         return NoIndex !=
     305                 :             static_cast<const OrderingEntry*>(aFirst->value)->mOrderedLT
     306                 :                 .BinaryIndexOf(aSecond);
     307                 :     }
     308                 : 
     309                 :     /**
     310                 :      * Return a pointer to the array of all elements "that" for
     311                 :      * which the order |this < that| has been explicitly added.
     312                 :      *
     313                 :      * NOTE: this does *not* consider transitive orderings.
     314                 :      */
     315                 :     PLHashEntry* const* GetOrders(const PLHashEntry* aEntry) const
     316                 :     {
     317                 :         return static_cast<const OrderingEntry*>(aEntry->value)->mOrderedLT
     318                 :             .Elements();
     319                 :     }
     320                 : 
     321                 :     /**
     322                 :      * Return the number of elements "that" for which the order
     323                 :      * |this < that| has been explicitly added.
     324                 :      *
     325                 :      * NOTE: this does *not* consider transitive orderings.
     326                 :      */
     327                 :     size_type NumOrders(const PLHashEntry* aEntry) const
     328                 :     {
     329                 :         return static_cast<const OrderingEntry*>(aEntry->value)->mOrderedLT
     330                 :             .Length();
     331                 :     }
     332                 : 
     333                 :     /** Make a ResourceAcquisition out of |aEntry|. */
     334                 :     ResourceAcquisition MakeResourceAcquisition(const PLHashEntry* aEntry)
     335                 :         const
     336                 :     {
     337                 :         return ResourceAcquisition(
     338                 :             static_cast<const T*>(aEntry->key),
     339                 :             static_cast<const OrderingEntry*>(aEntry->value)->mFirstSeen);
     340                 :     }
     341                 : 
     342                 :     // Throwaway RAII lock to make the following code safer.
     343                 :     struct PRAutoLock
     344                 :     {
     345                 :         PRAutoLock(PRLock* aLock) : mLock(aLock) { PR_Lock(mLock); }
     346                 :         ~PRAutoLock() { PR_Unlock(mLock); }
     347                 :         PRLock* mLock;
     348                 :     };
     349                 : 
     350                 : public:
     351                 :     static const PRUint32 kDefaultNumBuckets;
     352                 : 
     353                 :     /**
     354                 :      * DeadlockDetector
     355                 :      * Create a new deadlock detector.
     356                 :      *
     357                 :      * @param aNumResourcesGuess Guess at approximate number of resources
     358                 :      *        that will be checked.
     359                 :      */
     360                 :     DeadlockDetector(PRUint32 aNumResourcesGuess = kDefaultNumBuckets)
     361                 :     {
     362                 :         mOrdering = PL_NewHashTable(aNumResourcesGuess,
     363                 :                                     HashKey,
     364                 :                                     PL_CompareValues, PL_CompareValues,
     365                 :                                     &kAllocOps, 0);
     366                 :         if (!mOrdering)
     367                 :             NS_RUNTIMEABORT("couldn't initialize resource ordering table");
     368                 : 
     369                 :         mLock = PR_NewLock();
     370                 :         if (!mLock)
     371                 :             NS_RUNTIMEABORT("couldn't allocate deadlock detector lock");
     372                 :     }
     373                 : 
     374                 :     /**
     375                 :      * ~DeadlockDetector
     376                 :      *
     377                 :      * *NOT* thread safe.
     378                 :      */
     379                 :     ~DeadlockDetector()
     380                 :     {
     381                 :         PL_HashTableDestroy(mOrdering);
     382                 :         PR_DestroyLock(mLock);
     383                 :     }
     384                 : 
     385                 :     /**
     386                 :      * Add
     387                 :      * Make the deadlock detector aware of |aResource|.
     388                 :      *
     389                 :      * WARNING: The deadlock detector owns |aResource|.
     390                 :      *
     391                 :      * Thread safe.
     392                 :      *
     393                 :      * @param aResource Resource to make deadlock detector aware of.
     394                 :      */
     395                 :     void Add(T* aResource)
     396                 :     {
     397                 :         PRAutoLock _(mLock);
     398                 :         PutEntry(aResource);
     399                 :     }
     400                 : 
     401                 :     // Nb: implementing a Remove() method makes the detector "more
     402                 :     // unsound."  By removing a resource from the orderings, deadlocks
     403                 :     // may be missed that would otherwise have been found.  However,
     404                 :     // removing resources possibly reduces the # of false positives,
     405                 :     // and additionally saves space.  So it's a trade off; we have
     406                 :     // chosen to err on the side of caution and not implement Remove().
     407                 : 
     408                 :     /**
     409                 :      * CheckAcquisition This method is called after acquiring |aLast|,
     410                 :      * but before trying to acquire |aProposed| from |aCallContext|.
     411                 :      * It determines whether actually trying to acquire |aProposed|
     412                 :      * will create problems.  It is OK if |aLast| is NULL; this is
     413                 :      * interpreted as |aProposed| being the thread's first acquisition
     414                 :      * of its current chain.
     415                 :      *
     416                 :      * Iff acquiring |aProposed| may lead to deadlock for some thread
     417                 :      * interleaving (including the current one!), the cyclical
     418                 :      * dependency from which this was deduced is returned.  Otherwise,
     419                 :      * 0 is returned.
     420                 :      *
     421                 :      * If a potential deadlock is detected and a resource cycle is
     422                 :      * returned, it is the *caller's* responsibility to free it.
     423                 :      *
     424                 :      * Thread safe.
     425                 :      *
     426                 :      * @param aLast Last resource acquired by calling thread (or 0).
     427                 :      * @param aProposed Resource calling thread proposes to acquire.
     428                 :      * @param aCallContext Calling context whence acquisiton request came.
     429                 :      */
     430                 :     ResourceAcquisitionArray* CheckAcquisition(const T* aLast,
     431                 :                                                const T* aProposed,
     432                 :                                                const CallStack& aCallContext)
     433                 :     {
     434                 :         NS_ASSERTION(aProposed, "null resource");
     435                 :         PRAutoLock _(mLock);
     436                 : 
     437                 :         PLHashEntry* second = *GetEntry(aProposed);
     438                 :         OrderingEntry* e = static_cast<OrderingEntry*>(second->value);
     439                 :         if (CallStack::kNone == e->mFirstSeen)
     440                 :             e->mFirstSeen = aCallContext;
     441                 : 
     442                 :         if (!aLast)
     443                 :             // don't check if |0 < proposed|; just vamoose
     444                 :             return 0;
     445                 : 
     446                 :         PLHashEntry* first = *GetEntry(aLast);
     447                 : 
     448                 :         // this is the crux of the deadlock detector algorithm
     449                 : 
     450                 :         if (first == second) {
     451                 :             // reflexive deadlock.  fastpath b/c InTransitiveClosure is
     452                 :             // not applicable here.
     453                 :             ResourceAcquisitionArray* cycle = new ResourceAcquisitionArray();
     454                 :             if (!cycle)
     455                 :                 NS_RUNTIMEABORT("can't allocate dep. cycle array");
     456                 :             cycle->AppendElement(MakeResourceAcquisition(first));
     457                 :             cycle->AppendElement(ResourceAcquisition(aProposed,
     458                 :                                                      aCallContext));
     459                 :             return cycle;
     460                 :         }
     461                 :         if (InTransitiveClosure(first, second)) {
     462                 :             // we've already established |last < proposed|.  all is well.
     463                 :             return 0;
     464                 :         }
     465                 :         if (InTransitiveClosure(second, first)) {
     466                 :             // the order |proposed < last| has been deduced, perhaps
     467                 :             // transitively.  we're attempting to violate that
     468                 :             // constraint by acquiring resources in the order
     469                 :             // |last < proposed|, and thus we may deadlock under the
     470                 :             // right conditions.
     471                 :             ResourceAcquisitionArray* cycle = GetDeductionChain(second, first);
     472                 :             // show how acquiring |proposed| would complete the cycle
     473                 :             cycle->AppendElement(ResourceAcquisition(aProposed,
     474                 :                                                      aCallContext));
     475                 :             return cycle;
     476                 :         }
     477                 :         // |last|, |proposed| are unordered according to our
     478                 :         // poset.  this is fine, but we now need to add this
     479                 :         // ordering constraint.
     480                 :         AddOrder(first, second);
     481                 :         return 0;
     482                 :     }
     483                 : 
     484                 :     /**
     485                 :      * Return true iff |aTarget| is in the transitive closure of |aStart|
     486                 :      * over the ordering relation `<_this'.
     487                 :      *
     488                 :      * @precondition |aStart != aTarget|
     489                 :      */
     490                 :     bool InTransitiveClosure(const PLHashEntry* aStart,
     491                 :                              const PLHashEntry* aTarget) const
     492                 :     {
     493                 :         if (IsOrdered(aStart, aTarget))
     494                 :             return true;
     495                 : 
     496                 :         index_type i = 0;
     497                 :         size_type len = NumOrders(aStart);
     498                 :         for (const PLHashEntry* const* it = GetOrders(aStart);
     499                 :              i < len; ++i, ++it)
     500                 :             if (InTransitiveClosure(*it, aTarget))
     501                 :                 return true;
     502                 :         return false;
     503                 :     }
     504                 : 
     505                 :     /**
     506                 :      * Return an array of all resource acquisitions
     507                 :      *   aStart <_this r1 <_this r2 <_ ... <_ aTarget
     508                 :      * from which |aStart <_this aTarget| was deduced, including
     509                 :      * |aStart| and |aTarget|.
     510                 :      *
     511                 :      * Nb: there may be multiple deductions of |aStart <_this
     512                 :      * aTarget|.  This function returns the first ordering found by
     513                 :      * depth-first search.
     514                 :      *
     515                 :      * Nb: |InTransitiveClosure| could be replaced by this function.
     516                 :      * However, this one is more expensive because we record the DFS
     517                 :      * search stack on the heap whereas the other doesn't.
     518                 :      *
     519                 :      * @precondition |aStart != aTarget|
     520                 :      */
     521                 :     ResourceAcquisitionArray* GetDeductionChain(
     522                 :         const PLHashEntry* aStart,
     523                 :         const PLHashEntry* aTarget)
     524                 :     {
     525                 :         ResourceAcquisitionArray* chain = new ResourceAcquisitionArray();
     526                 :         if (!chain)
     527                 :             NS_RUNTIMEABORT("can't allocate dep. cycle array");
     528                 :         chain->AppendElement(MakeResourceAcquisition(aStart));
     529                 : 
     530                 :         NS_ASSERTION(GetDeductionChain_Helper(aStart, aTarget, chain),
     531                 :                      "GetDeductionChain called when there's no deadlock");
     532                 :         return chain;
     533                 :     }
     534                 : 
     535                 :     // precondition: |aStart != aTarget|
     536                 :     // invariant: |aStart| is the last element in |aChain|
     537                 :     bool GetDeductionChain_Helper(const PLHashEntry* aStart,
     538                 :                                   const PLHashEntry* aTarget,
     539                 :                                   ResourceAcquisitionArray* aChain)
     540                 :     {
     541                 :         if (IsOrdered(aStart, aTarget)) {
     542                 :             aChain->AppendElement(MakeResourceAcquisition(aTarget));
     543                 :             return true;
     544                 :         }
     545                 : 
     546                 :         index_type i = 0;
     547                 :         size_type len = NumOrders(aStart);
     548                 :         for (const PLHashEntry* const* it = GetOrders(aStart);
     549                 :              i < len; ++i, ++it) {
     550                 :             aChain->AppendElement(MakeResourceAcquisition(*it));
     551                 :             if (GetDeductionChain_Helper(*it, aTarget, aChain))
     552                 :                 return true;
     553                 :             aChain->RemoveElementAt(aChain->Length() - 1);
     554                 :         }
     555                 :         return false;
     556                 :     }
     557                 : 
     558                 :     /**
     559                 :      * The partial order on resource acquisitions used by the deadlock
     560                 :      * detector.
     561                 :      */
     562                 :     PLHashTable* mOrdering;     // T* -> PLHashEntry<OrderingEntry>
     563                 : 
     564                 :     /**
     565                 :      * Protects contentious methods.
     566                 :      * Nb: can't use mozilla::Mutex since we are used as its deadlock
     567                 :      * detector.
     568                 :      */
     569                 :     PRLock* mLock;
     570                 : 
     571                 : private:
     572                 :     DeadlockDetector(const DeadlockDetector& aDD) MOZ_DELETE;
     573                 :     DeadlockDetector& operator=(const DeadlockDetector& aDD) MOZ_DELETE;
     574                 : };
     575                 : 
     576                 : 
     577                 : template<typename T>
     578                 : const PLHashAllocOps DeadlockDetector<T>::kAllocOps = {
     579                 :     DeadlockDetector<T>::TableAlloc, DeadlockDetector<T>::TableFree,
     580                 :     DeadlockDetector<T>::EntryAlloc, DeadlockDetector<T>::EntryFree
     581                 : };
     582                 : 
     583                 : 
     584                 : template<typename T>
     585                 : // FIXME bug 456272: tune based on average workload
     586                 : const PRUint32 DeadlockDetector<T>::kDefaultNumBuckets = 64;
     587                 : 
     588                 : 
     589                 : } // namespace mozilla
     590                 : 
     591                 : #endif // ifndef mozilla_DeadlockDetector_h

Generated by: LCOV version 1.7