mirror of
https://github.com/gryf/tagbar.git
synced 2025-12-17 19:40:27 +01:00
Add tests to repository
This commit is contained in:
588
tests/cpp/DeadlockDetector.h
Normal file
588
tests/cpp/DeadlockDetector.h
Normal file
@@ -0,0 +1,588 @@
|
||||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: sw=4 ts=4 et :
|
||||
* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla 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/MPL/
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* The Original Code is mozilla.org code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Netscape Communications Corporation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 1998
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Chris Jones <jones.chris.g@gmail.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either of the GNU General Public License Version 2 or later (the "GPL"),
|
||||
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
#ifndef mozilla_DeadlockDetector_h
|
||||
#define mozilla_DeadlockDetector_h
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "plhash.h"
|
||||
#include "prlock.h"
|
||||
|
||||
#include "nsTArray.h"
|
||||
|
||||
#ifdef NS_TRACE_MALLOC
|
||||
# include "nsTraceMalloc.h"
|
||||
#endif // ifdef NS_TRACE_MALLOC
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
|
||||
// FIXME bug 456272: split this off into a convenience API on top of
|
||||
// nsStackWalk?
|
||||
class NS_COM_GLUE CallStack
|
||||
{
|
||||
private:
|
||||
#ifdef NS_TRACE_MALLOC
|
||||
typedef nsTMStackTraceID callstack_id;
|
||||
// needs to be a macro to avoid disturbing the backtrace
|
||||
# define NS_GET_BACKTRACE() NS_TraceMallocGetStackTrace()
|
||||
#else
|
||||
typedef void* callstack_id;
|
||||
# define NS_GET_BACKTRACE() 0
|
||||
#endif // ifdef NS_TRACE_MALLOC
|
||||
|
||||
callstack_id mCallStack;
|
||||
|
||||
public:
|
||||
/**
|
||||
* CallStack
|
||||
* *ALWAYS* *ALWAYS* *ALWAYS* call this with no arguments. This
|
||||
* constructor takes an argument *ONLY* so that |GET_BACKTRACE()|
|
||||
* can be evaluated in the stack frame of the caller, rather than
|
||||
* that of the constructor.
|
||||
*
|
||||
* *BEWARE*: this means that calling this constructor with no
|
||||
* arguments is not the same as a "default, do-nothing"
|
||||
* constructor: it *will* construct a backtrace. This can cause
|
||||
* unexpected performance issues.
|
||||
*/
|
||||
CallStack(const callstack_id aCallStack = NS_GET_BACKTRACE()) :
|
||||
mCallStack(aCallStack)
|
||||
{
|
||||
}
|
||||
CallStack(const CallStack& aFrom) :
|
||||
mCallStack(aFrom.mCallStack)
|
||||
{
|
||||
}
|
||||
CallStack& operator=(const CallStack& aFrom)
|
||||
{
|
||||
mCallStack = aFrom.mCallStack;
|
||||
return *this;
|
||||
}
|
||||
bool operator==(const CallStack& aOther) const
|
||||
{
|
||||
return mCallStack == aOther.mCallStack;
|
||||
}
|
||||
bool operator!=(const CallStack& aOther) const
|
||||
{
|
||||
return mCallStack != aOther.mCallStack;
|
||||
}
|
||||
|
||||
// FIXME bug 456272: if this is split off,
|
||||
// NS_TraceMallocPrintStackTrace should be modified to print into
|
||||
// an nsACString
|
||||
void Print(FILE* f) const
|
||||
{
|
||||
#ifdef NS_TRACE_MALLOC
|
||||
if (this != &kNone && mCallStack) {
|
||||
NS_TraceMallocPrintStackTrace(f, mCallStack);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
fputs(" [stack trace unavailable]\n", f);
|
||||
}
|
||||
|
||||
/** The "null" callstack. */
|
||||
static const CallStack kNone;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* DeadlockDetector
|
||||
*
|
||||
* The following is an approximate description of how the deadlock detector
|
||||
* works.
|
||||
*
|
||||
* The deadlock detector ensures that all blocking resources are
|
||||
* acquired according to a partial order P. One type of blocking
|
||||
* resource is a lock. If a lock l1 is acquired (locked) before l2,
|
||||
* then we say that |l1 <_P l2|. The detector flags an error if two
|
||||
* locks l1 and l2 have an inconsistent ordering in P; that is, if
|
||||
* both |l1 <_P l2| and |l2 <_P l1|. This is a potential error
|
||||
* because a thread acquiring l1,l2 according to the first order might
|
||||
* race with a thread acquiring them according to the second order.
|
||||
* If this happens under the right conditions, then the acquisitions
|
||||
* will deadlock.
|
||||
*
|
||||
* This deadlock detector doesn't know at compile-time what P is. So,
|
||||
* it tries to discover the order at run time. More precisely, it
|
||||
* finds <i>some</i> order P, then tries to find chains of resource
|
||||
* acquisitions that violate P. An example acquisition sequence, and
|
||||
* the orders they impose, is
|
||||
* l1.lock() // current chain: [ l1 ]
|
||||
* // order: { }
|
||||
*
|
||||
* l2.lock() // current chain: [ l1, l2 ]
|
||||
* // order: { l1 <_P l2 }
|
||||
*
|
||||
* l3.lock() // current chain: [ l1, l2, l3 ]
|
||||
* // order: { l1 <_P l2, l2 <_P l3, l1 <_P l3 }
|
||||
* // (note: <_P is transitive, so also |l1 <_P l3|)
|
||||
*
|
||||
* l2.unlock() // current chain: [ l1, l3 ]
|
||||
* // order: { l1 <_P l2, l2 <_P l3, l1 <_P l3 }
|
||||
* // (note: it's OK, but weird, that l2 was unlocked out
|
||||
* // of order. we still have l1 <_P l3).
|
||||
*
|
||||
* l2.lock() // current chain: [ l1, l3, l2 ]
|
||||
* // order: { l1 <_P l2, l2 <_P l3, l1 <_P l3,
|
||||
* l3 <_P l2 (!!!) }
|
||||
* BEEP BEEP! Here the detector will flag a potential error, since
|
||||
* l2 and l3 were used inconsistently (and potentially in ways that
|
||||
* would deadlock).
|
||||
*/
|
||||
template <typename T>
|
||||
class DeadlockDetector
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* ResourceAcquisition
|
||||
* Consists simply of a resource and the calling context from
|
||||
* which it was acquired. We pack this information together so
|
||||
* that it can be returned back to the caller when a potential
|
||||
* deadlock has been found.
|
||||
*/
|
||||
struct ResourceAcquisition
|
||||
{
|
||||
const T* mResource;
|
||||
CallStack mCallContext;
|
||||
|
||||
ResourceAcquisition(
|
||||
const T* aResource,
|
||||
const CallStack aCallContext=CallStack::kNone) :
|
||||
mResource(aResource),
|
||||
mCallContext(aCallContext)
|
||||
{
|
||||
}
|
||||
ResourceAcquisition(const ResourceAcquisition& aFrom) :
|
||||
mResource(aFrom.mResource),
|
||||
mCallContext(aFrom.mCallContext)
|
||||
{
|
||||
}
|
||||
ResourceAcquisition& operator=(const ResourceAcquisition& aFrom)
|
||||
{
|
||||
mResource = aFrom.mResource;
|
||||
mCallContext = aFrom.mCallContext;
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
typedef nsTArray<ResourceAcquisition> ResourceAcquisitionArray;
|
||||
|
||||
private:
|
||||
typedef nsTArray<PLHashEntry*> HashEntryArray;
|
||||
typedef typename HashEntryArray::index_type index_type;
|
||||
typedef typename HashEntryArray::size_type size_type;
|
||||
enum {
|
||||
NoIndex = HashEntryArray::NoIndex
|
||||
};
|
||||
|
||||
/**
|
||||
* Value type for the ordering table. Contains the other
|
||||
* resources on which an ordering constraint |key < other|
|
||||
* exists. The catch is that we also store the calling context at
|
||||
* which the other resource was acquired; this improves the
|
||||
* quality of error messages when potential deadlock is detected.
|
||||
*/
|
||||
struct OrderingEntry
|
||||
{
|
||||
OrderingEntry() :
|
||||
mFirstSeen(CallStack::kNone),
|
||||
mOrderedLT() // FIXME bug 456272: set to empirical
|
||||
{ // dep size?
|
||||
}
|
||||
~OrderingEntry()
|
||||
{
|
||||
}
|
||||
|
||||
CallStack mFirstSeen; // first site from which the resource appeared
|
||||
HashEntryArray mOrderedLT; // this <_o Other
|
||||
};
|
||||
|
||||
static void* TableAlloc(void* /*pool*/, PRSize size)
|
||||
{
|
||||
return operator new(size);
|
||||
}
|
||||
static void TableFree(void* /*pool*/, void* item)
|
||||
{
|
||||
operator delete(item);
|
||||
}
|
||||
static PLHashEntry* EntryAlloc(void* /*pool*/, const void* key)
|
||||
{
|
||||
return new PLHashEntry;
|
||||
}
|
||||
static void EntryFree(void* /*pool*/, PLHashEntry* entry, PRUintn flag)
|
||||
{
|
||||
delete static_cast<T*>(const_cast<void*>(entry->key));
|
||||
delete static_cast<OrderingEntry*>(entry->value);
|
||||
entry->value = 0;
|
||||
if (HT_FREE_ENTRY == flag)
|
||||
delete entry;
|
||||
}
|
||||
static PLHashNumber HashKey(const void* aKey)
|
||||
{
|
||||
return NS_PTR_TO_INT32(aKey) >> 2;
|
||||
}
|
||||
static const PLHashAllocOps kAllocOps;
|
||||
|
||||
// Hash table "interface" the rest of the code should use
|
||||
|
||||
PLHashEntry** GetEntry(const T* aKey)
|
||||
{
|
||||
return PL_HashTableRawLookup(mOrdering, HashKey(aKey), aKey);
|
||||
}
|
||||
|
||||
void PutEntry(T* aKey)
|
||||
{
|
||||
PL_HashTableAdd(mOrdering, aKey, new OrderingEntry());
|
||||
}
|
||||
|
||||
// XXX need these helper methods because OrderingEntry doesn't have
|
||||
// XXX access to underlying PLHashEntry
|
||||
|
||||
/**
|
||||
* Add the order |aFirst <_o aSecond|.
|
||||
*
|
||||
* WARNING: this does not check whether it's sane to add this
|
||||
* order. In the "best" bad case, when this order already exists,
|
||||
* adding it anyway may unnecessarily result in O(n^2) space. In
|
||||
* the "worst" bad case, adding it anyway will cause
|
||||
* |InTransitiveClosure()| to diverge.
|
||||
*/
|
||||
void AddOrder(PLHashEntry* aLT, PLHashEntry* aGT)
|
||||
{
|
||||
static_cast<OrderingEntry*>(aLT->value)->mOrderedLT
|
||||
.InsertElementSorted(aGT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true iff the order |aFirst < aSecond| has been
|
||||
* *explicitly* added.
|
||||
*
|
||||
* Does not consider transitivity.
|
||||
*/
|
||||
bool IsOrdered(const PLHashEntry* aFirst, const PLHashEntry* aSecond)
|
||||
const
|
||||
{
|
||||
return NoIndex !=
|
||||
static_cast<const OrderingEntry*>(aFirst->value)->mOrderedLT
|
||||
.BinaryIndexOf(aSecond);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a pointer to the array of all elements "that" for
|
||||
* which the order |this < that| has been explicitly added.
|
||||
*
|
||||
* NOTE: this does *not* consider transitive orderings.
|
||||
*/
|
||||
PLHashEntry* const* GetOrders(const PLHashEntry* aEntry) const
|
||||
{
|
||||
return static_cast<const OrderingEntry*>(aEntry->value)->mOrderedLT
|
||||
.Elements();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the number of elements "that" for which the order
|
||||
* |this < that| has been explicitly added.
|
||||
*
|
||||
* NOTE: this does *not* consider transitive orderings.
|
||||
*/
|
||||
size_type NumOrders(const PLHashEntry* aEntry) const
|
||||
{
|
||||
return static_cast<const OrderingEntry*>(aEntry->value)->mOrderedLT
|
||||
.Length();
|
||||
}
|
||||
|
||||
/** Make a ResourceAcquisition out of |aEntry|. */
|
||||
ResourceAcquisition MakeResourceAcquisition(const PLHashEntry* aEntry)
|
||||
const
|
||||
{
|
||||
return ResourceAcquisition(
|
||||
static_cast<const T*>(aEntry->key),
|
||||
static_cast<const OrderingEntry*>(aEntry->value)->mFirstSeen);
|
||||
}
|
||||
|
||||
// Throwaway RAII lock to make the following code safer.
|
||||
struct PRAutoLock
|
||||
{
|
||||
PRAutoLock(PRLock* aLock) : mLock(aLock) { PR_Lock(mLock); }
|
||||
~PRAutoLock() { PR_Unlock(mLock); }
|
||||
PRLock* mLock;
|
||||
};
|
||||
|
||||
public:
|
||||
static const PRUint32 kDefaultNumBuckets;
|
||||
|
||||
/**
|
||||
* DeadlockDetector
|
||||
* Create a new deadlock detector.
|
||||
*
|
||||
* @param aNumResourcesGuess Guess at approximate number of resources
|
||||
* that will be checked.
|
||||
*/
|
||||
DeadlockDetector(PRUint32 aNumResourcesGuess = kDefaultNumBuckets)
|
||||
{
|
||||
mOrdering = PL_NewHashTable(aNumResourcesGuess,
|
||||
HashKey,
|
||||
PL_CompareValues, PL_CompareValues,
|
||||
&kAllocOps, 0);
|
||||
if (!mOrdering)
|
||||
NS_RUNTIMEABORT("couldn't initialize resource ordering table");
|
||||
|
||||
mLock = PR_NewLock();
|
||||
if (!mLock)
|
||||
NS_RUNTIMEABORT("couldn't allocate deadlock detector lock");
|
||||
}
|
||||
|
||||
/**
|
||||
* ~DeadlockDetector
|
||||
*
|
||||
* *NOT* thread safe.
|
||||
*/
|
||||
~DeadlockDetector()
|
||||
{
|
||||
PL_HashTableDestroy(mOrdering);
|
||||
PR_DestroyLock(mLock);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add
|
||||
* Make the deadlock detector aware of |aResource|.
|
||||
*
|
||||
* WARNING: The deadlock detector owns |aResource|.
|
||||
*
|
||||
* Thread safe.
|
||||
*
|
||||
* @param aResource Resource to make deadlock detector aware of.
|
||||
*/
|
||||
void Add(T* aResource)
|
||||
{
|
||||
PRAutoLock _(mLock);
|
||||
PutEntry(aResource);
|
||||
}
|
||||
|
||||
// Nb: implementing a Remove() method makes the detector "more
|
||||
// unsound." By removing a resource from the orderings, deadlocks
|
||||
// may be missed that would otherwise have been found. However,
|
||||
// removing resources possibly reduces the # of false positives,
|
||||
// and additionally saves space. So it's a trade off; we have
|
||||
// chosen to err on the side of caution and not implement Remove().
|
||||
|
||||
/**
|
||||
* CheckAcquisition This method is called after acquiring |aLast|,
|
||||
* but before trying to acquire |aProposed| from |aCallContext|.
|
||||
* It determines whether actually trying to acquire |aProposed|
|
||||
* will create problems. It is OK if |aLast| is NULL; this is
|
||||
* interpreted as |aProposed| being the thread's first acquisition
|
||||
* of its current chain.
|
||||
*
|
||||
* Iff acquiring |aProposed| may lead to deadlock for some thread
|
||||
* interleaving (including the current one!), the cyclical
|
||||
* dependency from which this was deduced is returned. Otherwise,
|
||||
* 0 is returned.
|
||||
*
|
||||
* If a potential deadlock is detected and a resource cycle is
|
||||
* returned, it is the *caller's* responsibility to free it.
|
||||
*
|
||||
* Thread safe.
|
||||
*
|
||||
* @param aLast Last resource acquired by calling thread (or 0).
|
||||
* @param aProposed Resource calling thread proposes to acquire.
|
||||
* @param aCallContext Calling context whence acquisiton request came.
|
||||
*/
|
||||
ResourceAcquisitionArray* CheckAcquisition(const T* aLast,
|
||||
const T* aProposed,
|
||||
const CallStack& aCallContext)
|
||||
{
|
||||
NS_ASSERTION(aProposed, "null resource");
|
||||
PRAutoLock _(mLock);
|
||||
|
||||
PLHashEntry* second = *GetEntry(aProposed);
|
||||
OrderingEntry* e = static_cast<OrderingEntry*>(second->value);
|
||||
if (CallStack::kNone == e->mFirstSeen)
|
||||
e->mFirstSeen = aCallContext;
|
||||
|
||||
if (!aLast)
|
||||
// don't check if |0 < proposed|; just vamoose
|
||||
return 0;
|
||||
|
||||
PLHashEntry* first = *GetEntry(aLast);
|
||||
|
||||
// this is the crux of the deadlock detector algorithm
|
||||
|
||||
if (first == second) {
|
||||
// reflexive deadlock. fastpath b/c InTransitiveClosure is
|
||||
// not applicable here.
|
||||
ResourceAcquisitionArray* cycle = new ResourceAcquisitionArray();
|
||||
if (!cycle)
|
||||
NS_RUNTIMEABORT("can't allocate dep. cycle array");
|
||||
cycle->AppendElement(MakeResourceAcquisition(first));
|
||||
cycle->AppendElement(ResourceAcquisition(aProposed,
|
||||
aCallContext));
|
||||
return cycle;
|
||||
}
|
||||
if (InTransitiveClosure(first, second)) {
|
||||
// we've already established |last < proposed|. all is well.
|
||||
return 0;
|
||||
}
|
||||
if (InTransitiveClosure(second, first)) {
|
||||
// the order |proposed < last| has been deduced, perhaps
|
||||
// transitively. we're attempting to violate that
|
||||
// constraint by acquiring resources in the order
|
||||
// |last < proposed|, and thus we may deadlock under the
|
||||
// right conditions.
|
||||
ResourceAcquisitionArray* cycle = GetDeductionChain(second, first);
|
||||
// show how acquiring |proposed| would complete the cycle
|
||||
cycle->AppendElement(ResourceAcquisition(aProposed,
|
||||
aCallContext));
|
||||
return cycle;
|
||||
}
|
||||
// |last|, |proposed| are unordered according to our
|
||||
// poset. this is fine, but we now need to add this
|
||||
// ordering constraint.
|
||||
AddOrder(first, second);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true iff |aTarget| is in the transitive closure of |aStart|
|
||||
* over the ordering relation `<_this'.
|
||||
*
|
||||
* @precondition |aStart != aTarget|
|
||||
*/
|
||||
bool InTransitiveClosure(const PLHashEntry* aStart,
|
||||
const PLHashEntry* aTarget) const
|
||||
{
|
||||
if (IsOrdered(aStart, aTarget))
|
||||
return true;
|
||||
|
||||
index_type i = 0;
|
||||
size_type len = NumOrders(aStart);
|
||||
for (const PLHashEntry* const* it = GetOrders(aStart);
|
||||
i < len; ++i, ++it)
|
||||
if (InTransitiveClosure(*it, aTarget))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an array of all resource acquisitions
|
||||
* aStart <_this r1 <_this r2 <_ ... <_ aTarget
|
||||
* from which |aStart <_this aTarget| was deduced, including
|
||||
* |aStart| and |aTarget|.
|
||||
*
|
||||
* Nb: there may be multiple deductions of |aStart <_this
|
||||
* aTarget|. This function returns the first ordering found by
|
||||
* depth-first search.
|
||||
*
|
||||
* Nb: |InTransitiveClosure| could be replaced by this function.
|
||||
* However, this one is more expensive because we record the DFS
|
||||
* search stack on the heap whereas the other doesn't.
|
||||
*
|
||||
* @precondition |aStart != aTarget|
|
||||
*/
|
||||
ResourceAcquisitionArray* GetDeductionChain(
|
||||
const PLHashEntry* aStart,
|
||||
const PLHashEntry* aTarget)
|
||||
{
|
||||
ResourceAcquisitionArray* chain = new ResourceAcquisitionArray();
|
||||
if (!chain)
|
||||
NS_RUNTIMEABORT("can't allocate dep. cycle array");
|
||||
chain->AppendElement(MakeResourceAcquisition(aStart));
|
||||
|
||||
NS_ASSERTION(GetDeductionChain_Helper(aStart, aTarget, chain),
|
||||
"GetDeductionChain called when there's no deadlock");
|
||||
return chain;
|
||||
}
|
||||
|
||||
// precondition: |aStart != aTarget|
|
||||
// invariant: |aStart| is the last element in |aChain|
|
||||
bool GetDeductionChain_Helper(const PLHashEntry* aStart,
|
||||
const PLHashEntry* aTarget,
|
||||
ResourceAcquisitionArray* aChain)
|
||||
{
|
||||
if (IsOrdered(aStart, aTarget)) {
|
||||
aChain->AppendElement(MakeResourceAcquisition(aTarget));
|
||||
return true;
|
||||
}
|
||||
|
||||
index_type i = 0;
|
||||
size_type len = NumOrders(aStart);
|
||||
for (const PLHashEntry* const* it = GetOrders(aStart);
|
||||
i < len; ++i, ++it) {
|
||||
aChain->AppendElement(MakeResourceAcquisition(*it));
|
||||
if (GetDeductionChain_Helper(*it, aTarget, aChain))
|
||||
return true;
|
||||
aChain->RemoveElementAt(aChain->Length() - 1);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* The partial order on resource acquisitions used by the deadlock
|
||||
* detector.
|
||||
*/
|
||||
PLHashTable* mOrdering; // T* -> PLHashEntry<OrderingEntry>
|
||||
|
||||
/**
|
||||
* Protects contentious methods.
|
||||
* Nb: can't use mozilla::Mutex since we are used as its deadlock
|
||||
* detector.
|
||||
*/
|
||||
PRLock* mLock;
|
||||
|
||||
DeadlockDetector(const DeadlockDetector& aDD);
|
||||
DeadlockDetector& operator=(const DeadlockDetector& aDD);
|
||||
};
|
||||
|
||||
|
||||
template<typename T>
|
||||
const PLHashAllocOps DeadlockDetector<T>::kAllocOps = {
|
||||
DeadlockDetector<T>::TableAlloc, DeadlockDetector<T>::TableFree,
|
||||
DeadlockDetector<T>::EntryAlloc, DeadlockDetector<T>::EntryFree
|
||||
};
|
||||
|
||||
|
||||
template<typename T>
|
||||
// FIXME bug 456272: tune based on average workload
|
||||
const PRUint32 DeadlockDetector<T>::kDefaultNumBuckets = 64;
|
||||
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // ifndef mozilla_DeadlockDetector_h
|
||||
597
tests/cpp/ceded-test.cpp
Normal file
597
tests/cpp/ceded-test.cpp
Normal file
@@ -0,0 +1,597 @@
|
||||
/* Test file for C++ language.
|
||||
* Attempt to include as many aspects of the C++ language as possible.
|
||||
* Do not include things tested in test.c since that shares the
|
||||
* same language.
|
||||
*
|
||||
* $Id: test.cpp,v 1.22 2008/05/17 20:12:55 zappo Exp $
|
||||
*
|
||||
*/
|
||||
|
||||
/* An include test */
|
||||
#include <stdio.h>
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include "c++-test.hh"
|
||||
|
||||
#include <c++-test.hh>
|
||||
|
||||
double var1 = 1.2;
|
||||
|
||||
int simple1(int a) {
|
||||
|
||||
}
|
||||
|
||||
struct foo1 {
|
||||
int test;
|
||||
};
|
||||
|
||||
struct foo2 : public foo1 {
|
||||
const int foo21(int a, int b);
|
||||
const int foo22(int a, int b) { return 1 }
|
||||
};
|
||||
|
||||
/* Classes */
|
||||
class class1 {
|
||||
private:
|
||||
int var11;
|
||||
struct foo1 var12;
|
||||
public:
|
||||
int p_var11;
|
||||
struct foo p_var12;
|
||||
};
|
||||
|
||||
class i_class1 : public class1 {
|
||||
private:
|
||||
int var11;
|
||||
struct foo var12;
|
||||
public:
|
||||
int p_var11;
|
||||
struct foo p_var12;
|
||||
};
|
||||
|
||||
class class2 {
|
||||
private:
|
||||
int var21;
|
||||
struct foo var22;
|
||||
public:
|
||||
int p_var21;
|
||||
struct foo p_var22;
|
||||
};
|
||||
|
||||
class i_class2 : public class1, public class2 {
|
||||
private:
|
||||
int var21;
|
||||
struct foo var22;
|
||||
protected:
|
||||
int pt_var21;
|
||||
public:
|
||||
int p_var21;
|
||||
struct foo p_var22;
|
||||
};
|
||||
|
||||
class class3 {
|
||||
/* A class with strange things in it */
|
||||
public:
|
||||
class3(); /* A constructor */
|
||||
enum embedded_foo_enum {
|
||||
a, b, c
|
||||
} embed1;
|
||||
struct embedded_bar_struct {
|
||||
int a;
|
||||
int b;
|
||||
} embed2;
|
||||
class embedded_baz_class {
|
||||
embedded_baz_class();
|
||||
~embedded_baz_class();
|
||||
} embed3;
|
||||
~class3(); /* destructor */
|
||||
|
||||
/* Methods */
|
||||
int method_for_class3(int a, char b);
|
||||
|
||||
int inline_method(int c) { return c; }
|
||||
|
||||
/* Operators */
|
||||
class3& operator^= (const class3& something);
|
||||
|
||||
/* Funny declmods */
|
||||
const class3 * const method_const_ptr_ptr(const int * const argconst) const = 0;
|
||||
};
|
||||
|
||||
class3::class3()
|
||||
{
|
||||
/* Constructor outside the definition. */
|
||||
}
|
||||
|
||||
int class3::method_for_class3(int a, char b)
|
||||
{
|
||||
}
|
||||
|
||||
int class3::method1_for_class3( int a, int &b)
|
||||
{
|
||||
int cvariablename;
|
||||
class3 fooy[];
|
||||
class3 moose = new class3;
|
||||
|
||||
// Complktion testing line should find external members.
|
||||
a = fooy[1].me ;
|
||||
b = cv ;
|
||||
|
||||
if (fooy.emb) {
|
||||
simple1(c);
|
||||
}
|
||||
|
||||
cos(10);
|
||||
abs(10);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
char class3::method2_for_class3( int a, int b) throw ( exception1 )
|
||||
{
|
||||
return 'a';
|
||||
}
|
||||
|
||||
void *class3::method3_for_class3( int a, int b) throw ( exception1, exception2 )
|
||||
{
|
||||
int q = a;
|
||||
return "Moose";
|
||||
}
|
||||
|
||||
void *class3::method31_for_class3( int a, int b) throw ( )
|
||||
{
|
||||
int q = a;
|
||||
return "Moose";
|
||||
}
|
||||
|
||||
void *class3::method4_for_class3( int a, int b) reentrant
|
||||
{
|
||||
class3 ct;
|
||||
|
||||
ct.method5_for_class3(1,a);
|
||||
|
||||
pritf();
|
||||
}
|
||||
|
||||
/*
|
||||
* A method on class3.
|
||||
*/
|
||||
void *class3::method5_for_class3( int a, int b) const
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
* Namespace parsing tests
|
||||
*/
|
||||
namespace NS {
|
||||
class class_in_namespace {
|
||||
int equiv(const NS::class_in_namespace *) const;
|
||||
};
|
||||
}
|
||||
|
||||
int NS::class_in_namespace::equiv(const NS::class_in_namespace *cin) const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Stuff Klaus found.
|
||||
// Inheritance w/out a specifying for public.
|
||||
class class4 : class1 {
|
||||
// Pure virtual methods.
|
||||
void virtual print () const = 0;
|
||||
|
||||
public:
|
||||
// The whacky constructor type
|
||||
class4()
|
||||
try : class1(args)
|
||||
{
|
||||
// constructor body
|
||||
}
|
||||
catch ()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
class class5 : public virtual class4 {
|
||||
// Virtual inheritance
|
||||
};
|
||||
|
||||
class class6 : class1 {
|
||||
// Mutable
|
||||
mutable int i;
|
||||
};
|
||||
|
||||
/* Namespaces */
|
||||
namespace namespace1 {
|
||||
void ns_method1() { }
|
||||
|
||||
class n_class1 {
|
||||
public:
|
||||
void method11(int a) { }
|
||||
};
|
||||
|
||||
/* This shouldn't parse due to missing semicolon. */
|
||||
class _n_class2 : public n_class1 {
|
||||
void n_c2_method1(int a, int b) { }
|
||||
};
|
||||
|
||||
// Macros in the namespace
|
||||
#define NSMACRO 1
|
||||
|
||||
// Template in the namespace
|
||||
template<class T> T nsti1(const Foo& foo);
|
||||
template<> int nsti1<int>(const Foo& foo);
|
||||
|
||||
}
|
||||
|
||||
namespace namespace2 {
|
||||
|
||||
using namespace1::n_class1;
|
||||
|
||||
}
|
||||
|
||||
/* Initializers */
|
||||
void tinitializers1(): inita1(False),
|
||||
inita2(False)
|
||||
{
|
||||
inita1= 1;
|
||||
}
|
||||
|
||||
/* How about Extern C type things. */
|
||||
int funny_prototype(int ,int b,float c)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
extern "C"
|
||||
int extern_c_1(int a, int b)
|
||||
{
|
||||
|
||||
funny_prototype(1,2,3.4);
|
||||
|
||||
printf("Moose", );
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
||||
int extern_c_2(int a, int b)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Some operator stuff
|
||||
class Action
|
||||
{
|
||||
// Problems!! operator() and operator[] can not be parsed with semantic
|
||||
// 1.4.2 but with latest c.by
|
||||
virtual void operator()(int i, char *p ) = 0;
|
||||
virtual String& operator[]() = 0;
|
||||
virtual void operator!() = 0;
|
||||
virtual void operator->() = 0;
|
||||
virtual T& operator+=();
|
||||
virtual T& operator*();
|
||||
virtual T& operator*=();
|
||||
};
|
||||
|
||||
// class with namespace qualified parents
|
||||
class Multiinherit : public virtual POA::Parent,
|
||||
public virtual POA::Parent1,
|
||||
Parent
|
||||
{
|
||||
private:
|
||||
int i;
|
||||
|
||||
public:
|
||||
Multiinherit();
|
||||
~Multiinherit();
|
||||
|
||||
// method with a list of qualified exceptions
|
||||
void* throwtest()
|
||||
throw(Exception0,
|
||||
Testnamespace::Exception1,
|
||||
Testnamespace::Excpetion2,
|
||||
Testnamespace::testnamespace1::Exception3);
|
||||
|
||||
};
|
||||
|
||||
void*
|
||||
Multiinherit::throwtest()
|
||||
throw (Exception0,
|
||||
Testnamespace::Exception1,
|
||||
Testnamespace::Excpetion2,
|
||||
Testnamespace::testnamespace1::Exception3)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Jens Rock <jens.rock@asamnet.de>: Nested classes or structs defined
|
||||
// outside of the containing class/struct.
|
||||
class container
|
||||
{
|
||||
public:
|
||||
struct contained;
|
||||
container();
|
||||
~container();
|
||||
};
|
||||
|
||||
struct container::contained
|
||||
{
|
||||
public:
|
||||
contained();
|
||||
~contained();
|
||||
};
|
||||
|
||||
/*
|
||||
* Ok, how about some template stuff.
|
||||
*/
|
||||
template <class CT, class container = vector<CT> >
|
||||
const CT& max (const CT& a, const CT& b)
|
||||
{
|
||||
return a < b ? b : a;
|
||||
}
|
||||
|
||||
// Arne Schmitz found this one
|
||||
std::vector<int> &a, &b, &c;
|
||||
|
||||
class TemplateUsingClass
|
||||
{
|
||||
typedef TestClassMap::iterator iterator;
|
||||
typedef map<long, long> TestClassMap;
|
||||
|
||||
// typedefs with const and volatile
|
||||
typedef const map<long, long> const_TestClassMap;
|
||||
typedef TestClassMap<string>::iterator volatile volatile_iterator;
|
||||
|
||||
map<int, int> mapclassvarthingy;
|
||||
};
|
||||
|
||||
template<class T> T ti1(const Foo& foo);
|
||||
template<> int ti1<int>(const Foo& foo);
|
||||
|
||||
|
||||
// -----------------------------------
|
||||
// Now some namespace and related stuff
|
||||
// -----------------------------------
|
||||
|
||||
using CORBA::LEX::get_token;
|
||||
using Namespace1;
|
||||
|
||||
using namespace POA::std;
|
||||
using namespace Test;
|
||||
|
||||
|
||||
|
||||
namespace Parser
|
||||
{
|
||||
namespace
|
||||
{
|
||||
using Lexer::get_test;
|
||||
string str = "";
|
||||
}
|
||||
|
||||
namespace XXX
|
||||
{
|
||||
|
||||
class Foobar : public virtual POA::Parent,
|
||||
public virtual POA::Parent1,
|
||||
private POA::list<fact>,
|
||||
private map<string>
|
||||
{
|
||||
int i;
|
||||
list <shared_ptr<item> >::const_iterator l;
|
||||
public:
|
||||
|
||||
Foobar();
|
||||
~Foobar();
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
void test_function(int i);
|
||||
|
||||
};
|
||||
|
||||
// unnamed namespaces - even nested
|
||||
namespace
|
||||
{
|
||||
namespace
|
||||
{
|
||||
using Lexer::get_test;
|
||||
string str = "";
|
||||
class FooClass
|
||||
{
|
||||
FooClass();
|
||||
};
|
||||
}
|
||||
|
||||
// some builtin types
|
||||
long long ll = 0;
|
||||
long double d = 0.0;
|
||||
unsigned test;
|
||||
unsigned long int **uli = 0;
|
||||
signed si = 0;
|
||||
signed short ss = 0;
|
||||
short int i = 0;
|
||||
long int li = 0;
|
||||
|
||||
// expressions with namespace/class-qualifyiers
|
||||
ORB_var cGlobalOrb = ORB::_nil();
|
||||
ORB_var1 cGlobalOrb1 = ORB::_test;
|
||||
|
||||
class Testclass
|
||||
{
|
||||
#define TEST 0
|
||||
ini i;
|
||||
|
||||
public:
|
||||
|
||||
Testclass();
|
||||
~Testclass();
|
||||
};
|
||||
|
||||
static void test_function(unsigned int i);
|
||||
|
||||
};
|
||||
|
||||
|
||||
// outside method implementations which should be grouped to type Test
|
||||
XXX&
|
||||
Test::waiting()
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
void
|
||||
Test::print()
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// outside method implementations with namespaces which should be grouped to
|
||||
// their complete (incl. namespace) types
|
||||
void*
|
||||
Parser::XXX::Foobar::wait(int i, const char const * const * p)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
void*
|
||||
Namespace1::Test::wait1(int i)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int
|
||||
Namespace1::Test::waiting(int i)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// a class with some outside implementations which should all be grouped to
|
||||
// this class declaration
|
||||
class ClassWithExternals
|
||||
{
|
||||
private:
|
||||
int i;
|
||||
|
||||
public:
|
||||
ClassWithExternals();
|
||||
~ClassWithExternals();
|
||||
void non_nil();
|
||||
};
|
||||
|
||||
|
||||
// Foobar is not displayed; seems that semantic tries to add this to the class
|
||||
// Foobar but can not find/display it, because contained in the namespace above.
|
||||
void
|
||||
Foobar::non_nil()
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// are correctly grouped to the ClassWithExternals class
|
||||
void
|
||||
ClassWithExternals::non_nil()
|
||||
{
|
||||
String s = "lödfjg dlfgkdlfkgjdl";
|
||||
return;
|
||||
}
|
||||
|
||||
ClassWithExternals::ClassWithExternals()
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
void
|
||||
ClassWithExternals::~ClassWithExternals()
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// -------------------------------
|
||||
// Now some macro and define stuff
|
||||
// -------------------------------
|
||||
|
||||
#define TEST 0
|
||||
#define TEST1 "String"
|
||||
|
||||
// The first backslash makes this macro unmatched syntax with semantic 1.4.2!
|
||||
// With flexing \+newline as nothing all is working fine!
|
||||
#define MZK_ENTER(METHOD) \
|
||||
{ \
|
||||
CzkMethodLog lMethodLog(METHOD,"Framework");\
|
||||
}
|
||||
|
||||
#define ZK_ASSERTM(METHOD,ASSERTION,MESSAGE) \
|
||||
{ if(!(ASSERTION))\
|
||||
{\
|
||||
std::ostringstream lMesgStream; \
|
||||
lMesgStream << "Assertion failed: " \
|
||||
<< MESSAGE; \
|
||||
CzkLogManager::doLog(CzkLogManager::FATAL,"",METHOD, \
|
||||
"Assert",lMesgStream); \
|
||||
assert(ASSERTION);\
|
||||
}\
|
||||
}
|
||||
|
||||
// Test if not newline-backslashes are handled correctly
|
||||
string s = "My \"quoted\" string";
|
||||
|
||||
// parsed fine as macro
|
||||
#define FOO (arg) method(arg, "foo");
|
||||
|
||||
// With semantic 1.4.2 this parsed as macro BAR *and* function method.
|
||||
// With latest c.bnf at least one-liner macros can be parsed correctly.
|
||||
#define BAR (arg) CzkMessageLog method(arg, "bar");
|
||||
|
||||
// some const and volatile stuff
|
||||
char * p1 = "Hello"; // 1. variable Pointer, variable Data
|
||||
const char * p2 = "Hello"; // 2. variable pointer, constant data
|
||||
char * const p3 = "Hello"; // 3. constant pointer, variable data
|
||||
const char * const p4 = "Hello"; // 4. constant pointer, constant data
|
||||
|
||||
// Case 2 and 4 can exchange first "const" and "char"
|
||||
char const * p21 = "Hello"; // variable pointer, constant data
|
||||
char const * const p41 = "Hello"; // constant pointer, constant data
|
||||
|
||||
char volatile a = 0; // a volatile char
|
||||
void foo(bar const &arg); // a reference to a const bar
|
||||
int foobar(bar const * const p); // a const pointer to a const bar
|
||||
int foobar(bar const volatile * const p); // a const pointer to a const bar
|
||||
int foobar3(char* p); // a const pointer to a const bar
|
||||
|
||||
// Should not be parsed because this is invalid code
|
||||
int const & const r3 = i;
|
||||
|
||||
boolean i = 0;
|
||||
boolean & r1 = i;
|
||||
boolean const & r2 = i;
|
||||
|
||||
// const * sequences can be very long in C++ ;-)
|
||||
char const * const * const * const * ppp;
|
||||
|
||||
// complex function declarationen with named pointer-arguments
|
||||
const char** foobar1(volatile char const * const **p);
|
||||
const char** foobar11(volatile Test::Namespace::Char<char*> const * const **p);
|
||||
|
||||
// complex function declarationen with unnamed pointer-arguments
|
||||
const char* foobar2(const char***);
|
||||
const char* foobar21(const Test::Namespace::Char<char>***);
|
||||
|
||||
// string literal parsing even with wchar_t
|
||||
char const *p = "string1";
|
||||
char const *q = "string1" "str\"ing2" "string3";
|
||||
wchar_t testc = L'a';
|
||||
|
||||
wchar_t const *wp = L"string with a \" in it";
|
||||
wchar_t const *wq = L"string \n\t\"test" L"string2";
|
||||
wchar_t const *wr = L"string L";
|
||||
8
tests/cpp/issue82.cpp
Normal file
8
tests/cpp/issue82.cpp
Normal file
@@ -0,0 +1,8 @@
|
||||
#include <pd/http/client.H>
|
||||
|
||||
namespace phantom { namespace io_stream { namespace proto_http {
|
||||
namespace handler_bts {
|
||||
|
||||
} // namespace handler_bts
|
||||
|
||||
}}} // namespace phantom::io_stream::proto_http
|
||||
14392
tests/cpp/jstracer.cpp
Normal file
14392
tests/cpp/jstracer.cpp
Normal file
File diff suppressed because it is too large
Load Diff
195
tests/cpp/jstracer_part.cpp
Normal file
195
tests/cpp/jstracer_part.cpp
Normal file
@@ -0,0 +1,195 @@
|
||||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=4 sw=4 et tw=99:
|
||||
*
|
||||
* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla 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/MPL/
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* The Original Code is Mozilla SpiderMonkey JavaScript 1.9 code, released
|
||||
* May 28, 2008.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Brendan Eich <brendan@mozilla.org>
|
||||
*
|
||||
* Contributor(s):
|
||||
* Andreas Gal <gal@mozilla.com>
|
||||
* Mike Shaver <shaver@mozilla.org>
|
||||
* David Anderson <danderson@mozilla.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either of the GNU General Public License Version 2 or later (the "GPL"),
|
||||
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
|
||||
#include "nanojit/nanojit.h"
|
||||
|
||||
using namespace nanojit;
|
||||
|
||||
|
||||
void*
|
||||
nanojit::Allocator::allocChunk(size_t nbytes)
|
||||
{
|
||||
VMAllocator *vma = (VMAllocator*)this;
|
||||
JS_ASSERT(!vma->outOfMemory());
|
||||
void *p = malloc(nbytes);
|
||||
if (!p) {
|
||||
JS_ASSERT(nbytes < sizeof(vma->mReserve));
|
||||
vma->mOutOfMemory = true;
|
||||
p = (void*) &vma->mReserve[0];
|
||||
}
|
||||
vma->mSize += nbytes;
|
||||
return p;
|
||||
}
|
||||
|
||||
void
|
||||
nanojit::Allocator::freeChunk(void *p) {
|
||||
VMAllocator *vma = (VMAllocator*)this;
|
||||
if (p != &vma->mReserve[0])
|
||||
free(p);
|
||||
}
|
||||
|
||||
void
|
||||
nanojit::Allocator::postReset() {
|
||||
VMAllocator *vma = (VMAllocator*)this;
|
||||
vma->mOutOfMemory = false;
|
||||
vma->mSize = 0;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
nanojit::StackFilter::getTops(LIns* guard, int& spTop, int& rpTop)
|
||||
{
|
||||
VMSideExit* e = (VMSideExit*)guard->record()->exit;
|
||||
spTop = e->sp_adj;
|
||||
rpTop = e->rp_adj;
|
||||
}
|
||||
|
||||
class AdjustCallerGlobalTypesVisitor : public SlotVisitorBase
|
||||
{
|
||||
TraceRecorder &mRecorder;
|
||||
JSContext *mCx;
|
||||
nanojit::LirBuffer *mLirbuf;
|
||||
nanojit::LirWriter *mLir;
|
||||
JSTraceType *mTypeMap;
|
||||
public:
|
||||
AdjustCallerGlobalTypesVisitor(TraceRecorder &recorder,
|
||||
JSTraceType *typeMap) :
|
||||
mRecorder(recorder),
|
||||
mCx(mRecorder.cx),
|
||||
mLirbuf(mRecorder.lirbuf),
|
||||
mLir(mRecorder.lir),
|
||||
mTypeMap(typeMap)
|
||||
{}
|
||||
|
||||
JSTraceType* getTypeMap()
|
||||
{
|
||||
return mTypeMap;
|
||||
}
|
||||
|
||||
JS_REQUIRES_STACK JS_ALWAYS_INLINE void
|
||||
visitGlobalSlot(jsval *vp, unsigned n, unsigned slot) {
|
||||
LIns *ins = mRecorder.get(vp);
|
||||
bool isPromote = isPromoteInt(ins);
|
||||
if (isPromote && *mTypeMap == TT_DOUBLE) {
|
||||
mLir->insStorei(mRecorder.get(vp), mLirbuf->state,
|
||||
mRecorder.nativeGlobalOffset(vp));
|
||||
|
||||
/*
|
||||
* Aggressively undo speculation so the inner tree will compile
|
||||
* if this fails.
|
||||
*/
|
||||
oracle.markGlobalSlotUndemotable(mCx, slot);
|
||||
}
|
||||
JS_ASSERT(!(!isPromote && *mTypeMap == TT_INT32));
|
||||
++mTypeMap;
|
||||
}
|
||||
};
|
||||
|
||||
class AdjustCallerStackTypesVisitor : public SlotVisitorBase
|
||||
{
|
||||
TraceRecorder &mRecorder;
|
||||
JSContext *mCx;
|
||||
nanojit::LirBuffer *mLirbuf;
|
||||
nanojit::LirWriter *mLir;
|
||||
unsigned mSlotnum;
|
||||
JSTraceType *mTypeMap;
|
||||
public:
|
||||
AdjustCallerStackTypesVisitor(TraceRecorder &recorder,
|
||||
JSTraceType *typeMap) :
|
||||
mRecorder(recorder),
|
||||
mCx(mRecorder.cx),
|
||||
mLirbuf(mRecorder.lirbuf),
|
||||
mLir(mRecorder.lir),
|
||||
mSlotnum(0),
|
||||
mTypeMap(typeMap)
|
||||
{}
|
||||
|
||||
JSTraceType* getTypeMap()
|
||||
{
|
||||
return mTypeMap;
|
||||
}
|
||||
|
||||
JS_REQUIRES_STACK JS_ALWAYS_INLINE bool
|
||||
visitStackSlots(jsval *vp, size_t count, JSStackFrame* fp) {
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
LIns *ins = mRecorder.get(vp);
|
||||
bool isPromote = isPromoteInt(ins);
|
||||
if (isPromote && *mTypeMap == TT_DOUBLE) {
|
||||
mLir->insStorei(mRecorder.get(vp), mLirbuf->sp,
|
||||
-mRecorder.treeInfo->nativeStackBase +
|
||||
mRecorder.nativeStackOffset(vp));
|
||||
|
||||
/*
|
||||
* Aggressively undo speculation so the inner tree will compile
|
||||
* if this fails.
|
||||
*/
|
||||
oracle.markStackSlotUndemotable(mCx, mSlotnum);
|
||||
}
|
||||
JS_ASSERT(!(!isPromote && *mTypeMap == TT_INT32));
|
||||
++vp;
|
||||
++mTypeMap;
|
||||
++mSlotnum;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
#if defined NJ_VERBOSE
|
||||
void
|
||||
nanojit::LirNameMap::formatGuard(LIns *i, char *out)
|
||||
{
|
||||
VMSideExit *x;
|
||||
|
||||
x = (VMSideExit *)i->record()->exit;
|
||||
sprintf(out,
|
||||
"%s: %s %s -> pc=%p imacpc=%p sp%+ld rp%+ld (GuardID=%03d)",
|
||||
formatRef(i),
|
||||
lirNames[i->opcode()],
|
||||
i->oprnd1() ? formatRef(i->oprnd1()) : "",
|
||||
(void *)x->pc,
|
||||
(void *)x->imacpc,
|
||||
(long int)x->sp_adj,
|
||||
(long int)x->rp_adj,
|
||||
i->record()->profGuardID);
|
||||
}
|
||||
#endif
|
||||
|
||||
15
tests/cpp/mlprototype.cpp
Normal file
15
tests/cpp/mlprototype.cpp
Normal file
@@ -0,0 +1,15 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsThreadClassInfo::GetClassDescription(
|
||||
char **result,
|
||||
int foo,
|
||||
bool blah
|
||||
)
|
||||
{
|
||||
*result = nsnull;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
int foo;
|
||||
6
tests/cpp/multiline.cpp
Normal file
6
tests/cpp/multiline.cpp
Normal file
@@ -0,0 +1,6 @@
|
||||
void*
|
||||
Foo::bar(int i,
|
||||
const char const * const * p)
|
||||
{
|
||||
return;
|
||||
}
|
||||
3728
tests/cpp/nsCycleCollector.cpp
Normal file
3728
tests/cpp/nsCycleCollector.cpp
Normal file
File diff suppressed because it is too large
Load Diff
310
tests/cpp/nsIOThreadPool.cpp
Normal file
310
tests/cpp/nsIOThreadPool.cpp
Normal file
@@ -0,0 +1,310 @@
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla 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/MPL/
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* The Original Code is Mozilla.
|
||||
*
|
||||
* The Initial Developer of the Original Code is IBM Corporation.
|
||||
* Portions created by IBM Corporation are Copyright (C) 2003
|
||||
* IBM Corporation. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* IBM Corp.
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#include "nsIEventTarget.h"
|
||||
#include "nsIServiceManager.h"
|
||||
#include "nsIObserverService.h"
|
||||
#include "nsIObserver.h"
|
||||
#include "nsAutoLock.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "prclist.h"
|
||||
#include "prlog.h"
|
||||
|
||||
#if defined(PR_LOGGING)
|
||||
//
|
||||
// NSPR_LOG_MODULES=nsIOThreadPool:5
|
||||
//
|
||||
static PRLogModuleInfo *gIOThreadPoolLog = nsnull;
|
||||
#endif
|
||||
#define LOG(args) PR_LOG(gIOThreadPoolLog, PR_LOG_DEBUG, args)
|
||||
|
||||
// this number specifies the maximum number of threads.
|
||||
#define MAX_THREADS 4
|
||||
|
||||
// this number specifies how long to wait before killing an idle thread. it's
|
||||
// important to pick a large enough value here to minimize thread churn.
|
||||
#define IDLE_TIMEOUT PR_SecondsToInterval(60)
|
||||
|
||||
#define PLEVENT_FROM_LINK(_link) \
|
||||
((PLEvent*) ((char*) (_link) - offsetof(PLEvent, link)))
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// pool of joinable threads used for general purpose i/o tasks
|
||||
//
|
||||
// the main entry point to this class is nsIEventTarget. events posted to
|
||||
// the thread pool are dispatched on one of the threads. a variable number
|
||||
// of threads are maintained. the threads die off if they remain idle for
|
||||
// more than THREAD_IDLE_TIMEOUT. the thread pool shuts down when it receives
|
||||
// the "xpcom-shutdown" event.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
class nsIOThreadPool : public nsIEventTarget
|
||||
, public nsIObserver
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIEVENTTARGET
|
||||
NS_DECL_NSIOBSERVER
|
||||
|
||||
nsresult Init();
|
||||
void Shutdown();
|
||||
|
||||
private:
|
||||
virtual ~nsIOThreadPool();
|
||||
|
||||
PR_STATIC_CALLBACK(void) ThreadFunc(void *);
|
||||
|
||||
// mLock protects all (exceptions during Init and Shutdown)
|
||||
PRLock *mLock;
|
||||
PRCondVar *mIdleThreadCV; // notified to wake up an idle thread
|
||||
PRCondVar *mExitThreadCV; // notified when a thread exits
|
||||
PRUint32 mNumThreads; // number of active + idle threads
|
||||
PRUint32 mNumIdleThreads; // number of idle threads
|
||||
PRCList mEventQ; // queue of PLEvent structs
|
||||
PRBool mShutdown; // set to true if shutting down
|
||||
};
|
||||
|
||||
NS_IMPL_THREADSAFE_ISUPPORTS2(nsIOThreadPool, nsIEventTarget, nsIObserver)
|
||||
|
||||
nsresult
|
||||
nsIOThreadPool::Init()
|
||||
{
|
||||
#if defined(PR_LOGGING)
|
||||
if (!gIOThreadPoolLog)
|
||||
gIOThreadPoolLog = PR_NewLogModule("nsIOThreadPool");
|
||||
#endif
|
||||
|
||||
mNumThreads = 0;
|
||||
mNumIdleThreads = 0;
|
||||
mShutdown = PR_FALSE;
|
||||
|
||||
mLock = PR_NewLock();
|
||||
if (!mLock)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
mIdleThreadCV = PR_NewCondVar(mLock);
|
||||
if (!mIdleThreadCV)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
mExitThreadCV = PR_NewCondVar(mLock);
|
||||
if (!mExitThreadCV)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
PR_INIT_CLIST(&mEventQ);
|
||||
|
||||
// we want to shutdown the i/o thread pool at xpcom-shutdown time...
|
||||
nsCOMPtr<nsIObserverService> os = do_GetService("@mozilla.org/observer-service;1");
|
||||
if (os)
|
||||
os->AddObserver(this, "xpcom-shutdown", PR_FALSE);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsIOThreadPool::~nsIOThreadPool()
|
||||
{
|
||||
LOG(("Destroying nsIOThreadPool @%p\n", this));
|
||||
|
||||
#ifdef DEBUG
|
||||
NS_ASSERTION(PR_CLIST_IS_EMPTY(&mEventQ), "leaking events");
|
||||
NS_ASSERTION(mNumThreads == 0, "leaking thread(s)");
|
||||
#endif
|
||||
|
||||
if (mIdleThreadCV)
|
||||
PR_DestroyCondVar(mIdleThreadCV);
|
||||
if (mExitThreadCV)
|
||||
PR_DestroyCondVar(mExitThreadCV);
|
||||
if (mLock)
|
||||
PR_DestroyLock(mLock);
|
||||
}
|
||||
|
||||
void
|
||||
nsIOThreadPool::Shutdown()
|
||||
{
|
||||
LOG(("nsIOThreadPool::Shutdown\n"));
|
||||
|
||||
// synchronize with background threads...
|
||||
{
|
||||
nsAutoLock lock(mLock);
|
||||
mShutdown = PR_TRUE;
|
||||
|
||||
PR_NotifyAllCondVar(mIdleThreadCV);
|
||||
|
||||
while (mNumThreads != 0)
|
||||
PR_WaitCondVar(mExitThreadCV, PR_INTERVAL_NO_TIMEOUT);
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsIOThreadPool::PostEvent(PLEvent *event)
|
||||
{
|
||||
LOG(("nsIOThreadPool::PostEvent [event=%p]\n", event));
|
||||
|
||||
nsAutoLock lock(mLock);
|
||||
|
||||
// if we are shutting down, then prevent additional events from being
|
||||
// added to the queue...
|
||||
if (mShutdown)
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
|
||||
nsresult rv = NS_OK;
|
||||
|
||||
PR_APPEND_LINK(&event->link, &mEventQ);
|
||||
|
||||
// now, look for an available idle thread...
|
||||
if (mNumIdleThreads)
|
||||
PR_NotifyCondVar(mIdleThreadCV); // wake up an idle thread
|
||||
|
||||
// or, try to create a new thread unless we have reached our maximum...
|
||||
else if (mNumThreads < MAX_THREADS) {
|
||||
NS_ADDREF_THIS(); // the thread owns a reference to us
|
||||
mNumThreads++;
|
||||
PRThread *thread = PR_CreateThread(PR_USER_THREAD,
|
||||
ThreadFunc,
|
||||
this,
|
||||
PR_PRIORITY_NORMAL,
|
||||
PR_GLOBAL_THREAD,
|
||||
PR_UNJOINABLE_THREAD,
|
||||
0);
|
||||
if (!thread) {
|
||||
NS_RELEASE_THIS();
|
||||
mNumThreads--;
|
||||
rv = NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
}
|
||||
// else, we expect one of the active threads to process the event queue.
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsIOThreadPool::IsOnCurrentThread(PRBool *result)
|
||||
{
|
||||
// no one should be calling this method. if this assertion gets hit,
|
||||
// then we need to think carefully about what this method should be
|
||||
// returning.
|
||||
NS_NOTREACHED("nsIOThreadPool::IsOnCurrentThread");
|
||||
|
||||
// fudging this a bit since we actually cover several threads...
|
||||
*result = PR_FALSE;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsIOThreadPool::Observe(nsISupports *, const char *topic, const PRUnichar *)
|
||||
{
|
||||
NS_ASSERTION(strcmp(topic, "xpcom-shutdown") == 0, "unexpected topic");
|
||||
Shutdown();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
nsIOThreadPool::ThreadFunc(void *arg)
|
||||
{
|
||||
nsIOThreadPool *pool = (nsIOThreadPool *) arg;
|
||||
|
||||
LOG(("entering ThreadFunc\n"));
|
||||
|
||||
{
|
||||
nsAutoLock lock(pool->mLock);
|
||||
|
||||
for (;;) {
|
||||
PRIntervalTime start = PR_IntervalNow(), timeout = IDLE_TIMEOUT;
|
||||
//
|
||||
// wait for one or more of the following to occur:
|
||||
// (1) the event queue has an event to process
|
||||
// (2) the shutdown flag has been set
|
||||
// (3) the thread has been idle for too long
|
||||
//
|
||||
// PR_WaitCondVar will return when any of these conditions is true.
|
||||
//
|
||||
while (PR_CLIST_IS_EMPTY(&pool->mEventQ) && !pool->mShutdown) {
|
||||
pool->mNumIdleThreads++;
|
||||
PR_WaitCondVar(pool->mIdleThreadCV, timeout);
|
||||
pool->mNumIdleThreads--;
|
||||
|
||||
PRIntervalTime delta = PR_IntervalNow() - start;
|
||||
if (delta >= timeout)
|
||||
break;
|
||||
timeout -= delta;
|
||||
start += delta;
|
||||
}
|
||||
|
||||
// if the queue is still empty, then kill this thread (either we
|
||||
// are shutting down or the thread exceeded the idle timeout)...
|
||||
if (PR_CLIST_IS_EMPTY(&pool->mEventQ))
|
||||
break;
|
||||
|
||||
// handle one event at a time: we don't want this one thread to hog
|
||||
// all the events while other threads may be able to help out ;-)
|
||||
do {
|
||||
PLEvent *event = PLEVENT_FROM_LINK(PR_LIST_HEAD(&pool->mEventQ));
|
||||
PR_REMOVE_AND_INIT_LINK(&event->link);
|
||||
|
||||
LOG(("event:%p\n", event));
|
||||
|
||||
// release lock!
|
||||
lock.unlock();
|
||||
PL_HandleEvent(event);
|
||||
lock.lock();
|
||||
}
|
||||
while (!PR_CLIST_IS_EMPTY(&pool->mEventQ));
|
||||
}
|
||||
|
||||
// thread is going away...
|
||||
pool->mNumThreads--;
|
||||
PR_NotifyCondVar(pool->mExitThreadCV);
|
||||
}
|
||||
|
||||
// release our reference to the pool
|
||||
NS_RELEASE(pool);
|
||||
|
||||
LOG(("leaving ThreadFunc\n"));
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
NS_METHOD
|
||||
net_NewIOThreadPool(nsISupports *outer, REFNSIID iid, void **result)
|
||||
{
|
||||
nsIOThreadPool *pool = new nsIOThreadPool();
|
||||
if (!pool)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
NS_ADDREF(pool);
|
||||
nsresult rv = pool->Init();
|
||||
if (NS_SUCCEEDED(rv))
|
||||
rv = pool->QueryInterface(iid, result);
|
||||
NS_RELEASE(pool);
|
||||
return rv;
|
||||
}
|
||||
6873
tests/cpp/nsTextFrameThebes.cpp
Normal file
6873
tests/cpp/nsTextFrameThebes.cpp
Normal file
File diff suppressed because it is too large
Load Diff
702
tests/cpp/nsThread.cpp
Normal file
702
tests/cpp/nsThread.cpp
Normal file
@@ -0,0 +1,702 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla 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/MPL/
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* The Original Code is Mozilla code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Google Inc.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2006
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Darin Fisher <darin@meer.net>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#include "timelog.h"
|
||||
|
||||
#include "nsThread.h"
|
||||
#include "nsThreadManager.h"
|
||||
#include "nsIClassInfoImpl.h"
|
||||
#include "nsIProgrammingLanguage.h"
|
||||
#include "nsAutoLock.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "prlog.h"
|
||||
#include "nsThreadUtilsInternal.h"
|
||||
|
||||
#ifdef PR_LOGGING
|
||||
static PRLogModuleInfo *sLog = PR_NewLogModule("nsThread");
|
||||
#endif
|
||||
#define LOG(args) PR_LOG(sLog, PR_LOG_DEBUG, args)
|
||||
|
||||
NS_DECL_CI_INTERFACE_GETTER(nsThread)
|
||||
|
||||
nsIThreadObserver* nsThread::sGlobalObserver;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Because we do not have our own nsIFactory, we have to implement nsIClassInfo
|
||||
// somewhat manually.
|
||||
|
||||
class nsThreadClassInfo : public nsIClassInfo {
|
||||
public:
|
||||
NS_DECL_ISUPPORTS_INHERITED // no mRefCnt
|
||||
NS_DECL_NSICLASSINFO
|
||||
|
||||
nsThreadClassInfo() {}
|
||||
};
|
||||
|
||||
static nsThreadClassInfo sThreadClassInfo;
|
||||
|
||||
NS_IMETHODIMP_(nsrefcnt) nsThreadClassInfo::AddRef() { return 2; }
|
||||
NS_IMETHODIMP_(nsrefcnt) nsThreadClassInfo::Release() { return 1; }
|
||||
NS_IMPL_QUERY_INTERFACE1(nsThreadClassInfo, nsIClassInfo)
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsThreadClassInfo::GetInterfaces(PRUint32 *count, nsIID ***array)
|
||||
{
|
||||
return NS_CI_INTERFACE_GETTER_NAME(nsThread)(count, array);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsThreadClassInfo::GetHelperForLanguage(PRUint32 lang, nsISupports **result)
|
||||
{
|
||||
*result = nsnull;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsThreadClassInfo::GetContractID(char **result)
|
||||
{
|
||||
*result = nsnull;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsThreadClassInfo::GetClassDescription(char **result)
|
||||
{
|
||||
*result = nsnull;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsThreadClassInfo::GetClassID(nsCID **result)
|
||||
{
|
||||
*result = nsnull;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsThreadClassInfo::GetImplementationLanguage(PRUint32 *result)
|
||||
{
|
||||
*result = nsIProgrammingLanguage::CPLUSPLUS;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsThreadClassInfo::GetFlags(PRUint32 *result)
|
||||
{
|
||||
*result = THREADSAFE;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsThreadClassInfo::GetClassIDNoAlloc(nsCID *result)
|
||||
{
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
NS_IMPL_THREADSAFE_ADDREF(nsThread)
|
||||
NS_IMPL_THREADSAFE_RELEASE(nsThread)
|
||||
NS_INTERFACE_MAP_BEGIN(nsThread)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIThread)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIThreadInternal)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIEventTarget)
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupportsPriority)
|
||||
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIThread)
|
||||
if (aIID.Equals(NS_GET_IID(nsIClassInfo))) {
|
||||
foundInterface = static_cast<nsIClassInfo*>(&sThreadClassInfo);
|
||||
} else
|
||||
NS_INTERFACE_MAP_END
|
||||
NS_IMPL_CI_INTERFACE_GETTER4(nsThread, nsIThread, nsIThreadInternal,
|
||||
nsIEventTarget, nsISupportsPriority)
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
class nsThreadStartupEvent : public nsRunnable {
|
||||
public:
|
||||
// Create a new thread startup object.
|
||||
static nsThreadStartupEvent *Create() {
|
||||
nsThreadStartupEvent *startup = new nsThreadStartupEvent();
|
||||
if (startup && startup->mMon)
|
||||
return startup;
|
||||
// Allocation failure
|
||||
delete startup;
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
// This method does not return until the thread startup object is in the
|
||||
// completion state.
|
||||
void Wait() {
|
||||
if (mInitialized) // Maybe avoid locking...
|
||||
return;
|
||||
nsAutoMonitor mon(mMon);
|
||||
while (!mInitialized)
|
||||
mon.Wait();
|
||||
}
|
||||
|
||||
// This method needs to be public to support older compilers (xlC_r on AIX).
|
||||
// It should be called directly as this class type is reference counted.
|
||||
virtual ~nsThreadStartupEvent() {
|
||||
if (mMon)
|
||||
nsAutoMonitor::DestroyMonitor(mMon);
|
||||
}
|
||||
|
||||
private:
|
||||
NS_IMETHOD Run() {
|
||||
nsAutoMonitor mon(mMon);
|
||||
mInitialized = PR_TRUE;
|
||||
mon.Notify();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsThreadStartupEvent()
|
||||
: mMon(nsAutoMonitor::NewMonitor("xpcom.threadstartup"))
|
||||
, mInitialized(PR_FALSE) {
|
||||
}
|
||||
|
||||
PRMonitor *mMon;
|
||||
PRBool mInitialized;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// This event is responsible for notifying nsThread::Shutdown that it is time
|
||||
// to call PR_JoinThread.
|
||||
class nsThreadShutdownAckEvent : public nsRunnable {
|
||||
public:
|
||||
nsThreadShutdownAckEvent(nsThreadShutdownContext *ctx)
|
||||
: mShutdownContext(ctx) {
|
||||
}
|
||||
NS_IMETHOD Run() {
|
||||
mShutdownContext->shutdownAck = PR_TRUE;
|
||||
return NS_OK;
|
||||
}
|
||||
private:
|
||||
nsThreadShutdownContext *mShutdownContext;
|
||||
};
|
||||
|
||||
// This event is responsible for setting mShutdownContext
|
||||
class nsThreadShutdownEvent : public nsRunnable {
|
||||
public:
|
||||
nsThreadShutdownEvent(nsThread *thr, nsThreadShutdownContext *ctx)
|
||||
: mThread(thr), mShutdownContext(ctx) {
|
||||
}
|
||||
NS_IMETHOD Run() {
|
||||
mThread->mShutdownContext = mShutdownContext;
|
||||
return NS_OK;
|
||||
}
|
||||
private:
|
||||
nsRefPtr<nsThread> mThread;
|
||||
fprintf(logfp, "%s.%09ld: New Thread (%p)\n", out.str, out.nsec, (void *)self);
|
||||
|
||||
// Inform the ThreadManager
|
||||
nsThreadManager::get()->RegisterCurrentThread(self);
|
||||
|
||||
// Wait for and process startup event
|
||||
nsCOMPtr<nsIRunnable> event;
|
||||
if (!self->GetEvent(PR_TRUE, getter_AddRefs(event))) {
|
||||
NS_WARNING("failed waiting for thread startup event");
|
||||
return;
|
||||
}
|
||||
event->Run(); // unblocks nsThread::Init
|
||||
event = nsnull;
|
||||
|
||||
// Now, process incoming events...
|
||||
while (!self->ShuttingDown())
|
||||
NS_ProcessNextEvent(self);
|
||||
|
||||
// Do NS_ProcessPendingEvents but with special handling to set
|
||||
// mEventsAreDoomed atomically with the removal of the last event. The key
|
||||
// invariant here is that we will never permit PutEvent to succeed if the
|
||||
// event would be left in the queue after our final call to
|
||||
// NS_ProcessPendingEvents.
|
||||
while (PR_TRUE) {
|
||||
{
|
||||
nsAutoLock lock(self->mLock);
|
||||
if (!self->mEvents->HasPendingEvent()) {
|
||||
// No events in the queue, so we will stop now. Don't let any more
|
||||
// events be added, since they won't be processed. It is critical
|
||||
// that no PutEvent can occur between testing that the event queue is
|
||||
// empty and setting mEventsAreDoomed!
|
||||
self->mEventsAreDoomed = PR_TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
NS_ProcessPendingEvents(self);
|
||||
}
|
||||
|
||||
// Inform the threadmanager that this thread is going away
|
||||
nsThreadManager::get()->UnregisterCurrentThread(self);
|
||||
|
||||
// Dispatch shutdown ACK
|
||||
event = new nsThreadShutdownAckEvent(self->mShutdownContext);
|
||||
self->mShutdownContext->joiningThread->Dispatch(event, NS_DISPATCH_NORMAL);
|
||||
|
||||
// Release any observer of the thread here.
|
||||
self->SetObserver(nsnull);
|
||||
|
||||
NS_RELEASE(self);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
nsThread::nsThread()
|
||||
: mLock(PR_NewLock())
|
||||
, mEvents(&mEventsRoot)
|
||||
, mPriority(PRIORITY_NORMAL)
|
||||
, mThread(nsnull)
|
||||
, mRunningEvent(0)
|
||||
, mShutdownContext(nsnull)
|
||||
, mShutdownRequired(PR_FALSE)
|
||||
, mEventsAreDoomed(PR_FALSE)
|
||||
{
|
||||
}
|
||||
|
||||
nsThread::~nsThread()
|
||||
{
|
||||
if (mLock)
|
||||
PR_DestroyLock(mLock);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsThread::Init()
|
||||
{
|
||||
NS_ENSURE_TRUE(mLock, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
struct logtime out;
|
||||
get_log_time(&out);
|
||||
fprintf(logfp, "%s.%09ld: Thread (%p) Init() start\n", out.str, out.nsec, (void *)this);
|
||||
|
||||
// spawn thread and wait until it is fully setup
|
||||
nsRefPtr<nsThreadStartupEvent> startup = nsThreadStartupEvent::Create();
|
||||
NS_ENSURE_TRUE(startup, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
NS_ADDREF_THIS();
|
||||
|
||||
mShutdownRequired = PR_TRUE;
|
||||
|
||||
// ThreadFunc is responsible for setting mThread
|
||||
PRThread *thr = PR_CreateThread(PR_USER_THREAD, ThreadFunc, this,
|
||||
PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
|
||||
PR_JOINABLE_THREAD, 0);
|
||||
if (!thr) {
|
||||
NS_RELEASE_THIS();
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
// ThreadFunc will wait for this event to be run before it tries to access
|
||||
// mThread. By delaying insertion of this event into the queue, we ensure
|
||||
// that mThread is set properly.
|
||||
{
|
||||
nsAutoLock lock(mLock);
|
||||
mEvents->PutEvent(startup);
|
||||
}
|
||||
|
||||
// Wait for thread to call ThreadManager::SetupCurrentThread, which completes
|
||||
// initialization of ThreadFunc.
|
||||
startup->Wait();
|
||||
|
||||
get_log_time(&out);
|
||||
fprintf(logfp, "%s.%09ld: Thread (%p) Init() end\n", out.str, out.nsec, (void *)this);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsThread::InitCurrentThread()
|
||||
{
|
||||
NS_ENSURE_TRUE(mLock, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
mThread = PR_GetCurrentThread();
|
||||
|
||||
nsThreadManager::get()->RegisterCurrentThread(this);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsThread::PutEvent(nsIRunnable *event)
|
||||
{
|
||||
{
|
||||
nsAutoLock lock(mLock);
|
||||
if (mEventsAreDoomed) {
|
||||
NS_WARNING("An event was posted to a thread that will never run it (rejected)");
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
if (!mEvents->PutEvent(event))
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIThreadObserver> obs = GetObserver();
|
||||
if (obs)
|
||||
obs->OnDispatchedEvent(this);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// nsIEventTarget
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsThread::Dispatch(nsIRunnable *event, PRUint32 flags)
|
||||
{
|
||||
LOG(("THRD(%p) Dispatch [%p %x]\n", this, event, flags));
|
||||
|
||||
NS_ENSURE_ARG_POINTER(event);
|
||||
|
||||
if (flags & DISPATCH_SYNC) {
|
||||
nsThread *thread = nsThreadManager::get()->GetCurrentThread();
|
||||
NS_ENSURE_STATE(thread);
|
||||
|
||||
// XXX we should be able to do something better here... we should
|
||||
// be able to monitor the slot occupied by this event and use
|
||||
// that to tell us when the event has been processed.
|
||||
|
||||
nsRefPtr<nsThreadSyncDispatch> wrapper =
|
||||
new nsThreadSyncDispatch(thread, event);
|
||||
if (!wrapper)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
nsresult rv = PutEvent(wrapper);
|
||||
// Don't wait for the event to finish if we didn't dispatch it...
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
while (wrapper->IsPending())
|
||||
NS_ProcessNextEvent(thread);
|
||||
return rv;
|
||||
}
|
||||
|
||||
NS_ASSERTION(flags == NS_DISPATCH_NORMAL, "unexpected dispatch flags");
|
||||
return PutEvent(event);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsThread::IsOnCurrentThread(PRBool *result)
|
||||
{
|
||||
*result = (PR_GetCurrentThread() == mThread);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// nsIThread
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsThread::GetPRThread(PRThread **result)
|
||||
{
|
||||
*result = mThread;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsThread::Shutdown()
|
||||
{
|
||||
LOG(("THRD(%p) shutdown\n", this));
|
||||
|
||||
// XXX If we make this warn, then we hit that warning at xpcom shutdown while
|
||||
// shutting down a thread in a thread pool. That happens b/c the thread
|
||||
// in the thread pool is already shutdown by the thread manager.
|
||||
if (!mThread)
|
||||
return NS_OK;
|
||||
|
||||
NS_ENSURE_STATE(mThread != PR_GetCurrentThread());
|
||||
|
||||
// Prevent multiple calls to this method
|
||||
{
|
||||
nsAutoLock lock(mLock);
|
||||
if (!mShutdownRequired)
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
mShutdownRequired = PR_FALSE;
|
||||
}
|
||||
|
||||
nsThreadShutdownContext context;
|
||||
context.joiningThread = nsThreadManager::get()->GetCurrentThread();
|
||||
context.shutdownAck = PR_FALSE;
|
||||
|
||||
// Set mShutdownContext and wake up the thread in case it is waiting for
|
||||
// events to process.
|
||||
nsCOMPtr<nsIRunnable> event = new nsThreadShutdownEvent(this, &context);
|
||||
if (!event)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
// XXXroc What if posting the event fails due to OOM?
|
||||
PutEvent(event);
|
||||
|
||||
// We could still end up with other events being added after the shutdown
|
||||
// task, but that's okay because we process pending events in ThreadFunc
|
||||
// after setting mShutdownContext just before exiting.
|
||||
|
||||
// Process events on the current thread until we receive a shutdown ACK.
|
||||
while (!context.shutdownAck)
|
||||
NS_ProcessNextEvent(context.joiningThread);
|
||||
|
||||
// Now, it should be safe to join without fear of dead-locking.
|
||||
|
||||
PR_JoinThread(mThread);
|
||||
mThread = nsnull;
|
||||
|
||||
#ifdef DEBUG
|
||||
{
|
||||
nsAutoLock lock(mLock);
|
||||
NS_ASSERTION(!mObserver, "Should have been cleared at shutdown!");
|
||||
}
|
||||
#endif
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsThread::HasPendingEvents(PRBool *result)
|
||||
{
|
||||
NS_ENSURE_STATE(PR_GetCurrentThread() == mThread);
|
||||
|
||||
*result = mEvents->GetEvent(PR_FALSE, nsnull);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsThread::ProcessNextEvent(PRBool mayWait, PRBool *result)
|
||||
{
|
||||
struct logtime out;
|
||||
get_log_time(&out);
|
||||
fprintf(logfp, "%s.%09ld: Thread (%p) ProcessNextEvent [%u %u]\n",
|
||||
out.str,
|
||||
out.nsec,
|
||||
(void *)this,
|
||||
mayWait,
|
||||
mRunningEvent);
|
||||
|
||||
LOG(("THRD(%p) ProcessNextEvent [%u %u]\n", this, mayWait, mRunningEvent));
|
||||
|
||||
NS_ENSURE_STATE(PR_GetCurrentThread() == mThread);
|
||||
|
||||
PRBool notifyGlobalObserver = (sGlobalObserver != nsnull);
|
||||
if (notifyGlobalObserver)
|
||||
sGlobalObserver->OnProcessNextEvent(this, mayWait && !ShuttingDown(),
|
||||
mRunningEvent);
|
||||
|
||||
nsCOMPtr<nsIThreadObserver> obs = mObserver;
|
||||
if (obs)
|
||||
obs->OnProcessNextEvent(this, mayWait && !ShuttingDown(), mRunningEvent);
|
||||
|
||||
++mRunningEvent;
|
||||
|
||||
nsresult rv = NS_OK;
|
||||
|
||||
{
|
||||
// Scope for |event| to make sure that its destructor fires while
|
||||
// mRunningEvent has been incremented, since that destructor can
|
||||
// also do work.
|
||||
|
||||
// If we are shutting down, then do not wait for new events.
|
||||
nsCOMPtr<nsIRunnable> event;
|
||||
mEvents->GetEvent(mayWait && !ShuttingDown(), getter_AddRefs(event));
|
||||
|
||||
*result = (event.get() != nsnull);
|
||||
|
||||
if (event) {
|
||||
get_log_time(&out);
|
||||
fprintf(logfp, "%s.%09ld: Thread (%p) running [%p]\n",
|
||||
out.str,
|
||||
out.nsec,
|
||||
(void *)this,
|
||||
(void *)event.get());
|
||||
LOG(("THRD(%p) running [%p]\n", this, event.get()));
|
||||
event->Run();
|
||||
} else if (mayWait) {
|
||||
NS_ASSERTION(ShuttingDown(),
|
||||
"This should only happen when shutting down");
|
||||
rv = NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
get_log_time(&out);
|
||||
fprintf(logfp, "%s.%09ld: Thread (%p) running finished [%p]\n",
|
||||
out.str,
|
||||
out.nsec,
|
||||
(void *)this,
|
||||
(void *)event.get());
|
||||
}
|
||||
|
||||
--mRunningEvent;
|
||||
if (obs)
|
||||
obs->AfterProcessNextEvent(this, mRunningEvent);
|
||||
|
||||
if (notifyGlobalObserver && sGlobalObserver)
|
||||
sGlobalObserver->AfterProcessNextEvent(this, mRunningEvent);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// nsISupportsPriority
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsThread::GetPriority(PRInt32 *priority)
|
||||
{
|
||||
*priority = mPriority;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsThread::SetPriority(PRInt32 priority)
|
||||
{
|
||||
NS_ENSURE_STATE(mThread);
|
||||
|
||||
// NSPR defines the following four thread priorities:
|
||||
// PR_PRIORITY_LOW
|
||||
// PR_PRIORITY_NORMAL
|
||||
// PR_PRIORITY_HIGH
|
||||
// PR_PRIORITY_URGENT
|
||||
// We map the priority values defined on nsISupportsPriority to these values.
|
||||
|
||||
mPriority = priority;
|
||||
|
||||
PRThreadPriority pri;
|
||||
if (mPriority <= PRIORITY_HIGHEST) {
|
||||
pri = PR_PRIORITY_URGENT;
|
||||
} else if (mPriority < PRIORITY_NORMAL) {
|
||||
pri = PR_PRIORITY_HIGH;
|
||||
} else if (mPriority > PRIORITY_NORMAL) {
|
||||
pri = PR_PRIORITY_LOW;
|
||||
} else {
|
||||
pri = PR_PRIORITY_NORMAL;
|
||||
}
|
||||
PR_SetThreadPriority(mThread, pri);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsThread::AdjustPriority(PRInt32 delta)
|
||||
{
|
||||
return SetPriority(mPriority + delta);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// nsIThreadInternal
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsThread::GetObserver(nsIThreadObserver **obs)
|
||||
{
|
||||
nsAutoLock lock(mLock);
|
||||
NS_IF_ADDREF(*obs = mObserver);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsThread::SetObserver(nsIThreadObserver *obs)
|
||||
{
|
||||
NS_ENSURE_STATE(PR_GetCurrentThread() == mThread);
|
||||
|
||||
nsAutoLock lock(mLock);
|
||||
mObserver = obs;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsThread::PushEventQueue(nsIThreadEventFilter *filter)
|
||||
{
|
||||
nsChainedEventQueue *queue = new nsChainedEventQueue(filter);
|
||||
if (!queue || !queue->IsInitialized()) {
|
||||
delete queue;
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
nsAutoLock lock(mLock);
|
||||
queue->mNext = mEvents;
|
||||
mEvents = queue;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsThread::PopEventQueue()
|
||||
{
|
||||
nsAutoLock lock(mLock);
|
||||
|
||||
// Make sure we do not pop too many!
|
||||
NS_ENSURE_STATE(mEvents != &mEventsRoot);
|
||||
|
||||
nsChainedEventQueue *queue = mEvents;
|
||||
mEvents = mEvents->mNext;
|
||||
|
||||
nsCOMPtr<nsIRunnable> event;
|
||||
while (queue->GetEvent(PR_FALSE, getter_AddRefs(event)))
|
||||
mEvents->PutEvent(event);
|
||||
|
||||
delete queue;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
PRBool
|
||||
nsThread::nsChainedEventQueue::PutEvent(nsIRunnable *event)
|
||||
{
|
||||
PRBool val;
|
||||
if (!mFilter || mFilter->AcceptEvent(event)) {
|
||||
val = mQueue.PutEvent(event);
|
||||
} else {
|
||||
val = mNext->PutEvent(event);
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsThreadSyncDispatch::Run()
|
||||
{
|
||||
if (mSyncTask) {
|
||||
mSyncTask->Run();
|
||||
mSyncTask = nsnull;
|
||||
// unblock the origin thread
|
||||
mOrigin->Dispatch(this, NS_DISPATCH_NORMAL);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
NS_SetGlobalThreadObserver(nsIThreadObserver* aObserver)
|
||||
{
|
||||
if (aObserver && nsThread::sGlobalObserver) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
if (!NS_IsMainThread()) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
nsThread::sGlobalObserver = aObserver;
|
||||
return NS_OK;
|
||||
}
|
||||
170
tests/cpp/nsThread.h
Normal file
170
tests/cpp/nsThread.h
Normal file
@@ -0,0 +1,170 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla 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/MPL/
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* The Original Code is Mozilla code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Google Inc.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2006
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Darin Fisher <darin@meer.net>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#ifndef nsThread_h__
|
||||
#define nsThread_h__
|
||||
|
||||
#include "nsIThreadInternal.h"
|
||||
#include "nsISupportsPriority.h"
|
||||
#include "nsEventQueue.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "nsString.h"
|
||||
#include "nsAutoLock.h"
|
||||
#include "nsAutoPtr.h"
|
||||
|
||||
// A native thread
|
||||
class nsThread : public nsIThreadInternal, public nsISupportsPriority
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIEVENTTARGET
|
||||
NS_DECL_NSITHREAD
|
||||
NS_DECL_NSITHREADINTERNAL
|
||||
NS_DECL_NSISUPPORTSPRIORITY
|
||||
|
||||
nsThread();
|
||||
|
||||
// Initialize this as a wrapper for a new PRThread.
|
||||
nsresult Init();
|
||||
|
||||
// Initialize this as a wrapper for the current PRThread.
|
||||
nsresult InitCurrentThread();
|
||||
|
||||
// The PRThread corresponding to this thread.
|
||||
PRThread *GetPRThread() { return mThread; }
|
||||
|
||||
// If this flag is true, then the nsThread was created using
|
||||
// nsIThreadManager::NewThread.
|
||||
PRBool ShutdownRequired() { return mShutdownRequired; }
|
||||
|
||||
// The global thread observer
|
||||
static nsIThreadObserver* sGlobalObserver;
|
||||
|
||||
private:
|
||||
friend class nsThreadShutdownEvent;
|
||||
|
||||
~nsThread();
|
||||
|
||||
PRBool ShuttingDown() { return mShutdownContext != nsnull; }
|
||||
|
||||
static void ThreadFunc(void *arg);
|
||||
|
||||
// Helper
|
||||
already_AddRefed<nsIThreadObserver> GetObserver() {
|
||||
nsIThreadObserver *obs;
|
||||
nsThread::GetObserver(&obs);
|
||||
return already_AddRefed<nsIThreadObserver>(obs);
|
||||
}
|
||||
|
||||
// Wrappers for event queue methods:
|
||||
PRBool GetEvent(PRBool mayWait, nsIRunnable **event) {
|
||||
return mEvents->GetEvent(mayWait, event);
|
||||
}
|
||||
nsresult PutEvent(nsIRunnable *event);
|
||||
|
||||
// Wrapper for nsEventQueue that supports chaining.
|
||||
class nsChainedEventQueue {
|
||||
public:
|
||||
nsChainedEventQueue(nsIThreadEventFilter *filter = nsnull)
|
||||
: mNext(nsnull), mFilter(filter) {
|
||||
}
|
||||
|
||||
PRBool IsInitialized() {
|
||||
return mQueue.IsInitialized();
|
||||
}
|
||||
|
||||
PRBool GetEvent(PRBool mayWait, nsIRunnable **event) {
|
||||
return mQueue.GetEvent(mayWait, event);
|
||||
}
|
||||
|
||||
PRBool PutEvent(nsIRunnable *event);
|
||||
|
||||
PRBool HasPendingEvent() {
|
||||
return mQueue.HasPendingEvent();
|
||||
}
|
||||
|
||||
class nsChainedEventQueue *mNext;
|
||||
private:
|
||||
nsCOMPtr<nsIThreadEventFilter> mFilter;
|
||||
nsEventQueue mQueue;
|
||||
};
|
||||
|
||||
// This lock protects access to mObserver, mEvents and mEventsAreDoomed.
|
||||
// All of those fields are only modified on the thread itself (never from
|
||||
// another thread). This means that we can avoid holding the lock while
|
||||
// using mObserver and mEvents on the thread itself. When calling PutEvent
|
||||
// on mEvents, we have to hold the lock to synchronize with PopEventQueue.
|
||||
PRLock *mLock;
|
||||
|
||||
nsCOMPtr<nsIThreadObserver> mObserver;
|
||||
|
||||
nsChainedEventQueue *mEvents; // never null
|
||||
nsChainedEventQueue mEventsRoot;
|
||||
|
||||
PRInt32 mPriority;
|
||||
PRThread *mThread;
|
||||
PRUint32 mRunningEvent; // counter
|
||||
|
||||
struct nsThreadShutdownContext *mShutdownContext;
|
||||
|
||||
PRPackedBool mShutdownRequired;
|
||||
PRPackedBool mShutdownPending;
|
||||
// Set to true when events posted to this thread will never run.
|
||||
PRPackedBool mEventsAreDoomed;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
class nsThreadSyncDispatch : public nsRunnable {
|
||||
public:
|
||||
nsThreadSyncDispatch(nsIThread *origin, nsIRunnable *task)
|
||||
: mOrigin(origin), mSyncTask(task) {
|
||||
}
|
||||
|
||||
PRBool IsPending() {
|
||||
return mSyncTask != nsnull;
|
||||
}
|
||||
|
||||
private:
|
||||
NS_DECL_NSIRUNNABLE
|
||||
|
||||
nsCOMPtr<nsIThread> mOrigin;
|
||||
nsCOMPtr<nsIRunnable> mSyncTask;
|
||||
};
|
||||
|
||||
#endif // nsThread_h__
|
||||
46
tests/cpp/nsThread_part.cpp
Normal file
46
tests/cpp/nsThread_part.cpp
Normal file
@@ -0,0 +1,46 @@
|
||||
namespace {
|
||||
struct nsThreadShutdownContext {
|
||||
nsThread *joiningThread;
|
||||
PRBool shutdownAck;
|
||||
union {
|
||||
int a;
|
||||
int b;
|
||||
union {
|
||||
char f;
|
||||
char g;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
struct teststruct {
|
||||
char foo;
|
||||
union {
|
||||
int x;
|
||||
int y;
|
||||
};
|
||||
};
|
||||
|
||||
namespace
|
||||
{
|
||||
short int i = 0;
|
||||
|
||||
class Testclass
|
||||
{
|
||||
int j;
|
||||
|
||||
public:
|
||||
|
||||
Testclass();
|
||||
~Testclass();
|
||||
};
|
||||
|
||||
namespace
|
||||
{
|
||||
string str = "";
|
||||
class FooClass
|
||||
{
|
||||
FooClass();
|
||||
};
|
||||
}
|
||||
};
|
||||
1023
tests/cpp/nsXPComInit.cpp
Normal file
1023
tests/cpp/nsXPComInit.cpp
Normal file
File diff suppressed because it is too large
Load Diff
558
tests/cpp/proxytests.cpp
Normal file
558
tests/cpp/proxytests.cpp
Normal file
@@ -0,0 +1,558 @@
|
||||
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla 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/MPL/
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* The Original Code is mozilla.org code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Netscape Communications Corporation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 1998
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Pierre Phaneuf <pp@ludusdesign.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either of the GNU General Public License Version 2 or later (the "GPL"),
|
||||
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "nsXPCOM.h"
|
||||
#include "nsXPCOMCIDInternal.h"
|
||||
#include "nsIComponentManager.h"
|
||||
#include "nsIComponentRegistrar.h"
|
||||
#include "nsIServiceManager.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsCOMArray.h"
|
||||
|
||||
#include "nscore.h"
|
||||
#include "nspr.h"
|
||||
#include "prmon.h"
|
||||
|
||||
#include "nsITestProxy.h"
|
||||
|
||||
#include "nsIRunnable.h"
|
||||
#include "nsIProxyObjectManager.h"
|
||||
#include "nsIThreadPool.h"
|
||||
#include "nsXPCOMCIDInternal.h"
|
||||
#include "nsComponentManagerUtils.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "nsThreadUtils.h"
|
||||
|
||||
#include "prlog.h"
|
||||
#ifdef PR_LOGGING
|
||||
static PRLogModuleInfo *sLog = PR_NewLogModule("Test");
|
||||
#define LOG(args) PR_LOG(sLog, PR_LOG_DEBUG, args)
|
||||
#else
|
||||
#define LOG(args) printf args
|
||||
#endif
|
||||
|
||||
namespace proxytests {
|
||||
|
||||
static nsresult
|
||||
GetThreadFromPRThread(PRThread *prthread, nsIThread **result)
|
||||
{
|
||||
LOG(("TEST: GetThreadFromPRThread [%p]\n", prthread));
|
||||
|
||||
nsCOMPtr<nsIThreadManager> tm = do_GetService(NS_THREADMANAGER_CONTRACTID);
|
||||
NS_ENSURE_STATE(tm);
|
||||
return tm->GetThreadFromPRThread(prthread, result);
|
||||
}
|
||||
|
||||
/***************************************************************************/
|
||||
/* nsTestXPCFoo */
|
||||
/***************************************************************************/
|
||||
class nsTestXPCFoo : public nsITestProxy
|
||||
{
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_IMETHOD Test(PRInt32 p1, PRInt32 p2, PRInt32* retval);
|
||||
NS_IMETHOD Test2();
|
||||
NS_IMETHOD Test3(nsISupports *p1, nsISupports **p2);
|
||||
|
||||
nsTestXPCFoo();
|
||||
};
|
||||
|
||||
nsTestXPCFoo::nsTestXPCFoo()
|
||||
{
|
||||
NS_ADDREF_THIS();
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS1(nsTestXPCFoo, nsITestProxy)
|
||||
|
||||
NS_IMETHODIMP nsTestXPCFoo::Test(PRInt32 p1, PRInt32 p2, PRInt32* retval)
|
||||
{
|
||||
LOG(("TEST: Thread (%d) Test Called successfully! Party on...\n", p1));
|
||||
*retval = p1+p2;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
NS_IMETHODIMP nsTestXPCFoo::Test2()
|
||||
{
|
||||
LOG(("TEST: The quick brown netscape jumped over the old lazy ie..\n"));
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsTestXPCFoo::Test3(nsISupports *p1, nsISupports **p2)
|
||||
{
|
||||
if (p1 != nsnull)
|
||||
{
|
||||
nsITestProxy *test;
|
||||
|
||||
p1->QueryInterface(NS_GET_IID(nsITestProxy), (void**)&test);
|
||||
|
||||
test->Test2();
|
||||
PRInt32 a;
|
||||
test->Test( 1, 2, &a);
|
||||
LOG(("TEST: \n1+2=%d\n",a));
|
||||
}
|
||||
|
||||
|
||||
*p2 = new nsTestXPCFoo();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/***************************************************************************/
|
||||
/* nsTestXPCFoo2 */
|
||||
/***************************************************************************/
|
||||
class nsTestXPCFoo2 : public nsITestProxy
|
||||
{
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_IMETHOD Test(PRInt32 p1, PRInt32 p2, PRInt32* retval);
|
||||
NS_IMETHOD Test2();
|
||||
NS_IMETHOD Test3(nsISupports *p1, nsISupports **p2);
|
||||
|
||||
nsTestXPCFoo2();
|
||||
};
|
||||
|
||||
nsTestXPCFoo2::nsTestXPCFoo2()
|
||||
{
|
||||
NS_ADDREF_THIS();
|
||||
}
|
||||
|
||||
NS_IMPL_THREADSAFE_ISUPPORTS1(nsTestXPCFoo2, nsITestProxy)
|
||||
|
||||
NS_IMETHODIMP nsTestXPCFoo2::Test(PRInt32 p1, PRInt32 p2, PRInt32* retval)
|
||||
{
|
||||
LOG(("TEST: calling back to caller!\n"));
|
||||
|
||||
nsCOMPtr<nsIProxyObjectManager> manager =
|
||||
do_GetService(NS_XPCOMPROXY_CONTRACTID);
|
||||
|
||||
LOG(("TEST: ProxyObjectManager: %p \n", (void *) manager.get()));
|
||||
|
||||
PR_ASSERT(manager);
|
||||
|
||||
nsCOMPtr<nsIThread> thread;
|
||||
GetThreadFromPRThread((PRThread *) p1, getter_AddRefs(thread));
|
||||
NS_ENSURE_STATE(thread);
|
||||
|
||||
nsCOMPtr<nsITestProxy> proxyObject;
|
||||
manager->GetProxyForObject(thread, NS_GET_IID(nsITestProxy), this, NS_PROXY_SYNC, (void**)&proxyObject);
|
||||
proxyObject->Test3(nsnull, nsnull);
|
||||
|
||||
LOG(("TEST: Deleting Proxy Object\n"));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
NS_IMETHODIMP nsTestXPCFoo2::Test2()
|
||||
{
|
||||
LOG(("TEST: nsTestXPCFoo2::Test2() called\n"));
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
NS_IMETHODIMP nsTestXPCFoo2::Test3(nsISupports *p1, nsISupports **p2)
|
||||
{
|
||||
LOG(("TEST: Got called"));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#if 0
|
||||
struct ArgsStruct {
|
||||
nsIThread* thread;
|
||||
PRInt32 threadNumber;
|
||||
};
|
||||
|
||||
|
||||
|
||||
// This will create two objects both descendants of a single IID.
|
||||
void TestCase_TwoClassesOneInterface(void *arg)
|
||||
{
|
||||
ArgsStruct *argsStruct = (ArgsStruct*) arg;
|
||||
|
||||
|
||||
nsCOMPtr<nsIProxyObjectManager> manager =
|
||||
do_GetService(NS_XPCOMPROXY_CONTRACTID);
|
||||
|
||||
printf("ProxyObjectManager: %p \n", (void *) manager.get());
|
||||
|
||||
PR_ASSERT(manager);
|
||||
|
||||
nsITestProxy *proxyObject;
|
||||
nsITestProxy *proxyObject2;
|
||||
|
||||
nsTestXPCFoo* foo = new nsTestXPCFoo();
|
||||
nsTestXPCFoo2* foo2 = new nsTestXPCFoo2();
|
||||
|
||||
PR_ASSERT(foo);
|
||||
PR_ASSERT(foo2);
|
||||
|
||||
|
||||
manager->GetProxyForObject(argsStruct->thread, NS_GET_IID(nsITestProxy), foo, NS_PROXY_SYNC, (void**)&proxyObject);
|
||||
|
||||
manager->GetProxyForObject(argsStruct->thread, NS_GET_IID(nsITestProxy), foo2, NS_PROXY_SYNC, (void**)&proxyObject2);
|
||||
|
||||
|
||||
|
||||
if (proxyObject && proxyObject2)
|
||||
{
|
||||
// release ownership of the real object.
|
||||
|
||||
PRInt32 a;
|
||||
nsresult rv;
|
||||
PRInt32 threadNumber = argsStruct->threadNumber;
|
||||
|
||||
printf("Deleting real Object (%d)\n", threadNumber);
|
||||
NS_RELEASE(foo);
|
||||
|
||||
printf("Deleting real Object 2 (%d)\n", threadNumber);
|
||||
NS_RELEASE(foo2);
|
||||
|
||||
|
||||
printf("Thread (%d) Prior to calling proxyObject->Test.\n", threadNumber);
|
||||
rv = proxyObject->Test(threadNumber, 0, &a);
|
||||
printf("Thread (%d) error: %d.\n", threadNumber, rv);
|
||||
|
||||
|
||||
printf("Thread (%d) Prior to calling proxyObject->Test2.\n", threadNumber);
|
||||
rv = proxyObject->Test2();
|
||||
printf("Thread (%d) error: %d.\n", threadNumber, rv);
|
||||
|
||||
printf("Thread (%d) Prior to calling proxyObject2->Test2.\n", threadNumber);
|
||||
rv = proxyObject2->Test2();
|
||||
printf("Thread (%d) proxyObject2 error: %d.\n", threadNumber, rv);
|
||||
|
||||
printf("Deleting Proxy Object (%d)\n", threadNumber );
|
||||
NS_RELEASE(proxyObject);
|
||||
|
||||
printf("Deleting Proxy Object 2 (%d)\n", threadNumber );
|
||||
NS_RELEASE(proxyObject2);
|
||||
}
|
||||
|
||||
PR_Sleep( PR_MillisecondsToInterval(1000) ); // If your thread goes away, your stack goes away. Only use ASYNC on calls that do not have out parameters
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
void TestCase_NestedLoop(nsIThread *thread, PRInt32 index)
|
||||
{
|
||||
nsCOMPtr<nsIProxyObjectManager> manager =
|
||||
do_GetService(NS_XPCOMPROXY_CONTRACTID);
|
||||
|
||||
LOG(("TEST: ProxyObjectManager: %p\n", (void *) manager.get()));
|
||||
|
||||
PR_ASSERT(manager);
|
||||
|
||||
nsITestProxy *proxyObject;
|
||||
nsTestXPCFoo2* foo = new nsTestXPCFoo2();
|
||||
|
||||
PR_ASSERT(foo);
|
||||
|
||||
|
||||
manager->GetProxyForObject(thread, NS_GET_IID(nsITestProxy), foo, NS_PROXY_SYNC, (void**)&proxyObject);
|
||||
|
||||
if (proxyObject)
|
||||
{
|
||||
// release ownership of the real object.
|
||||
|
||||
nsresult rv;
|
||||
|
||||
LOG(("TEST: Deleting real Object (%d)\n", index));
|
||||
NS_RELEASE(foo);
|
||||
|
||||
PRInt32 retval;
|
||||
|
||||
LOG(("TEST: Getting EventThread...\n"));
|
||||
|
||||
//nsCOMPtr<nsIThread> curThread = do_GetCurrentThread();
|
||||
PRThread *curThread = PR_GetCurrentThread();
|
||||
if (curThread)
|
||||
{
|
||||
LOG(("TEST: Thread (%d) Prior to calling proxyObject->Test.\n", index));
|
||||
rv = proxyObject->Test(NS_PTR_TO_INT32((void*)curThread), 0, &retval); // XXX broken on 64-bit arch
|
||||
LOG(("TEST: Thread (%d) proxyObject error: %x.\n", index, rv));
|
||||
|
||||
LOG(("TEST: Deleting Proxy Object (%d)\n", index));
|
||||
NS_RELEASE(proxyObject);
|
||||
}
|
||||
|
||||
PR_Sleep( PR_MillisecondsToInterval(1000) ); // If your thread goes away, your stack goes away. Only use ASYNC on calls that do not have out parameters
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#if 0
|
||||
void TestCase_nsISupports(void *arg)
|
||||
{
|
||||
|
||||
ArgsStruct *argsStruct = (ArgsStruct*) arg;
|
||||
|
||||
nsCOMPtr<nsIProxyObjectManager> manager =
|
||||
do_GetService(NS_XPCOMPROXY_CONTRACTID);
|
||||
|
||||
PR_ASSERT(manager);
|
||||
|
||||
nsITestProxy *proxyObject;
|
||||
nsTestXPCFoo* foo = new nsTestXPCFoo();
|
||||
|
||||
PR_ASSERT(foo);
|
||||
|
||||
manager->GetProxyForObject(argsStruct->thread, NS_GET_IID(nsITestProxy), foo, NS_PROXY_SYNC, (void**)&proxyObject);
|
||||
|
||||
if (proxyObject != nsnull)
|
||||
{
|
||||
nsISupports *bISupports = nsnull, *cISupports = nsnull;
|
||||
|
||||
proxyObject->Test3(foo, &bISupports);
|
||||
proxyObject->Test3(bISupports, &cISupports);
|
||||
|
||||
nsITestProxy *test;
|
||||
bISupports->QueryInterface(NS_GET_IID(nsITestProxy), (void**)&test);
|
||||
|
||||
test->Test2();
|
||||
|
||||
NS_RELEASE(foo);
|
||||
NS_RELEASE(proxyObject);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/***************************************************************************/
|
||||
/* ProxyTest */
|
||||
/***************************************************************************/
|
||||
|
||||
class ProxyTest : public nsIRunnable
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
ProxyTest(PRThread *eventLoopThread, PRInt32 index)
|
||||
: mEventLoopThread(eventLoopThread)
|
||||
, mIndex(index)
|
||||
{}
|
||||
|
||||
NS_IMETHOD Run()
|
||||
{
|
||||
//TestCase_TwoClassesOneInterface(arg);
|
||||
//TestCase_nsISupports(arg);
|
||||
nsCOMPtr<nsIThread> thread;
|
||||
GetThreadFromPRThread(mEventLoopThread, getter_AddRefs(thread));
|
||||
TestCase_NestedLoop(thread, mIndex);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
PRThread *mEventLoopThread;
|
||||
PRInt32 mIndex;
|
||||
};
|
||||
NS_IMPL_THREADSAFE_ISUPPORTS1(ProxyTest, nsIRunnable)
|
||||
|
||||
class TestSyncProxyToSelf : public nsIRunnable
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
NS_IMETHOD Run()
|
||||
{
|
||||
LOG(("TEST: Verifing calling Proxy on eventQ thread.\n"));
|
||||
|
||||
nsCOMPtr<nsIThread> thread = do_GetCurrentThread();
|
||||
|
||||
nsITestProxy *proxyObject;
|
||||
nsTestXPCFoo *foo = new nsTestXPCFoo();
|
||||
NS_ENSURE_STATE(foo);
|
||||
|
||||
nsCOMPtr<nsIProxyObjectManager> manager =
|
||||
do_GetService(NS_XPCOMPROXY_CONTRACTID);
|
||||
|
||||
manager->GetProxyForObject(thread,
|
||||
NS_GET_IID(nsITestProxy), foo,
|
||||
NS_PROXY_SYNC, (void**)&proxyObject);
|
||||
|
||||
PRInt32 a;
|
||||
proxyObject->Test(1, 2, &a);
|
||||
proxyObject->Test2();
|
||||
|
||||
NS_RELEASE(proxyObject);
|
||||
delete foo;
|
||||
|
||||
LOG(("TEST: End of Verification calling Proxy on eventQ thread.\n"));
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
};
|
||||
NS_IMPL_THREADSAFE_ISUPPORTS1(TestSyncProxyToSelf, nsIRunnable)
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Test to make sure we can call methods on a "main thread only" object from
|
||||
// a background thread.
|
||||
|
||||
class MainThreadOnly : public nsIRunnable {
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_IMETHOD Run() {
|
||||
NS_ASSERTION(NS_IsMainThread(), "method called on wrong thread");
|
||||
*mNumRuns -= 1;
|
||||
return NS_OK;
|
||||
}
|
||||
MainThreadOnly(PRUint32 *numRuns) : mNumRuns(numRuns) {}
|
||||
~MainThreadOnly() {
|
||||
NS_ASSERTION(NS_IsMainThread(), "method called on wrong thread");
|
||||
}
|
||||
PRBool IsDone() { return mNumRuns == 0; }
|
||||
private:
|
||||
PRUint32 *mNumRuns;
|
||||
};
|
||||
NS_IMPL_ISUPPORTS1(MainThreadOnly, nsIRunnable) // not threadsafe!
|
||||
|
||||
static nsresult
|
||||
RunApartmentTest()
|
||||
{
|
||||
LOG(("RunApartmentTest: start\n"));
|
||||
|
||||
const PRUint32 numDispatched = 160;
|
||||
|
||||
PRUint32 numCompleted = 0;
|
||||
nsCOMPtr<nsIRunnable> obj = new MainThreadOnly(&numCompleted);
|
||||
|
||||
nsCOMPtr<nsIProxyObjectManager> manager =
|
||||
do_GetService(NS_XPCOMPROXY_CONTRACTID);
|
||||
|
||||
nsCOMPtr<nsIRunnable> objProxy;
|
||||
manager->GetProxyForObject(NS_PROXY_TO_CURRENT_THREAD,
|
||||
NS_GET_IID(nsIRunnable),
|
||||
obj,
|
||||
NS_PROXY_ASYNC,
|
||||
getter_AddRefs(objProxy));
|
||||
nsCOMPtr<nsIThread> thread;
|
||||
NS_NewThread(getter_AddRefs(thread));
|
||||
|
||||
obj = nsnull;
|
||||
|
||||
nsCOMPtr<nsIThreadPool> pool = do_CreateInstance(NS_THREADPOOL_CONTRACTID);
|
||||
|
||||
pool->SetThreadLimit(8);
|
||||
for (PRUint32 i = 0; i < numDispatched; ++i)
|
||||
pool->Dispatch(objProxy, NS_DISPATCH_NORMAL);
|
||||
|
||||
objProxy = nsnull;
|
||||
|
||||
nsCOMPtr<nsIThread> curThread = do_GetCurrentThread();
|
||||
while (numCompleted < numDispatched) {
|
||||
NS_ProcessNextEvent(curThread);
|
||||
}
|
||||
|
||||
pool->Shutdown();
|
||||
|
||||
LOG(("RunApartmentTest: end\n"));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
using namespace proxytests;
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
int numberOfThreads = 1;
|
||||
|
||||
if (argc > 1)
|
||||
numberOfThreads = atoi(argv[1]);
|
||||
|
||||
NS_InitXPCOM2(nsnull, nsnull, nsnull);
|
||||
|
||||
// Scope code so everything is destroyed before we run call NS_ShutdownXPCOM
|
||||
{
|
||||
nsCOMPtr<nsIComponentRegistrar> registrar;
|
||||
NS_GetComponentRegistrar(getter_AddRefs(registrar));
|
||||
registrar->AutoRegister(nsnull);
|
||||
|
||||
RunApartmentTest();
|
||||
|
||||
nsCOMPtr<nsIThread> eventLoopThread;
|
||||
NS_NewThread(getter_AddRefs(eventLoopThread));
|
||||
|
||||
nsCOMPtr<nsIRunnable> test = new TestSyncProxyToSelf();
|
||||
eventLoopThread->Dispatch(test, NS_DISPATCH_NORMAL);
|
||||
|
||||
PRThread *eventLoopPRThread;
|
||||
eventLoopThread->GetPRThread(&eventLoopPRThread);
|
||||
PR_ASSERT(eventLoopPRThread);
|
||||
|
||||
LOG(("TEST: Spawn Threads:\n"));
|
||||
nsCOMArray<nsIThread> threads;
|
||||
for (PRInt32 spawn = 0; spawn < numberOfThreads; spawn++)
|
||||
{
|
||||
test = new ProxyTest(eventLoopPRThread, spawn);
|
||||
|
||||
nsCOMPtr<nsIThread> thread;
|
||||
NS_NewThread(getter_AddRefs(thread), test);
|
||||
|
||||
threads.AppendObject(thread);
|
||||
|
||||
LOG(("TEST: \tThread (%d) spawned\n", spawn));
|
||||
|
||||
PR_Sleep( PR_MillisecondsToInterval(250) );
|
||||
}
|
||||
|
||||
LOG(("TEST: All Threads Spawned.\n"));
|
||||
|
||||
LOG(("TEST: Wait for threads.\n"));
|
||||
for (PRInt32 i = 0; i < numberOfThreads; i++)
|
||||
{
|
||||
LOG(("TEST: Thread (%d) Join...\n", i));
|
||||
nsresult rv = threads[i]->Shutdown();
|
||||
LOG(("TEST: Thread (%d) Joined. (error: %x).\n", i, rv));
|
||||
}
|
||||
|
||||
LOG(("TEST: Shutting down event loop thread\n"));
|
||||
eventLoopThread->Shutdown();
|
||||
}
|
||||
|
||||
LOG(("TEST: Calling Cleanup.\n"));
|
||||
NS_ShutdownXPCOM(nsnull);
|
||||
|
||||
LOG(("TEST: Return zero.\n"));
|
||||
return 0;
|
||||
}
|
||||
33
tests/cpp/test.cpp
Normal file
33
tests/cpp/test.cpp
Normal file
@@ -0,0 +1,33 @@
|
||||
namespace Parser
|
||||
{
|
||||
namespace XXX
|
||||
{
|
||||
class Foobar
|
||||
{
|
||||
class Blah
|
||||
{
|
||||
class Bar
|
||||
{
|
||||
class Lalala
|
||||
{
|
||||
public:
|
||||
Foobar();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
void*
|
||||
Parser::XXX::Foobar::wait(int i, const char const * const * p)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
void
|
||||
Foobar::non_nil()
|
||||
{
|
||||
return;
|
||||
}
|
||||
12
tests/cpp/testsubclass.cpp
Normal file
12
tests/cpp/testsubclass.cpp
Normal file
@@ -0,0 +1,12 @@
|
||||
int animal::moose::getFeet() //^2^
|
||||
{
|
||||
return fFeet;
|
||||
}
|
||||
|
||||
void animal::moose::doNothing() //^3^
|
||||
{
|
||||
animal::moose foo();
|
||||
|
||||
fFeet = N// -15-
|
||||
; // #15# ( "NAME1" "NAME2" "NAME3" )
|
||||
}
|
||||
Reference in New Issue
Block a user