icecat: add release icecat-140.10.1-1gnu1 for ecne
This commit is contained in:
parent
a5f93cb214
commit
ff85d7c623
1256 changed files with 63469 additions and 24141 deletions
|
|
@ -1018,18 +1018,40 @@ class FinalizationRegistryCleanup::CleanupRunnable
|
|||
: public DiscardableRunnable {
|
||||
public:
|
||||
explicit CleanupRunnable(FinalizationRegistryCleanup* aCleanupWork)
|
||||
: DiscardableRunnable("CleanupRunnable"), mCleanupWork(aCleanupWork) {}
|
||||
: DiscardableRunnable("CleanupRunnable"), mCleanupWork(aCleanupWork) {
|
||||
MOZ_ASSERT(aCleanupWork);
|
||||
}
|
||||
|
||||
virtual ~CleanupRunnable() {
|
||||
if (mCleanupWork) {
|
||||
clearPendingRunnable();
|
||||
}
|
||||
}
|
||||
|
||||
// MOZ_CAN_RUN_SCRIPT_BOUNDARY until Runnable::Run is MOZ_CAN_RUN_SCRIPT. See
|
||||
// bug 1535398.
|
||||
MOZ_CAN_RUN_SCRIPT_BOUNDARY
|
||||
NS_IMETHOD Run() override {
|
||||
if (!mCleanupWork) {
|
||||
// The FinalizationRegistryCleanup has been destroyed.
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
clearPendingRunnable();
|
||||
|
||||
mCleanupWork->DoCleanup();
|
||||
mCleanupWork = nullptr;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void clearPendingRunnable() {
|
||||
MOZ_ASSERT(mCleanupWork->mPendingRunnable == this);
|
||||
mCleanupWork->mPendingRunnable = nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
FinalizationRegistryCleanup* mCleanupWork;
|
||||
friend class FinalizationRegistryCleanup;
|
||||
};
|
||||
|
||||
FinalizationRegistryCleanup::FinalizationRegistryCleanup(
|
||||
|
|
@ -1039,6 +1061,10 @@ FinalizationRegistryCleanup::FinalizationRegistryCleanup(
|
|||
void FinalizationRegistryCleanup::Destroy() {
|
||||
// This must happen before the CycleCollectedJSContext destructor calls
|
||||
// JS_DestroyContext().
|
||||
if (mPendingRunnable) {
|
||||
MOZ_ASSERT(mPendingRunnable->mCleanupWork == this);
|
||||
mPendingRunnable->mCleanupWork = nullptr;
|
||||
}
|
||||
mCallbacks.reset();
|
||||
}
|
||||
|
||||
|
|
@ -1059,7 +1085,7 @@ void FinalizationRegistryCleanup::QueueCallback(JSFunction* aDoCleanup,
|
|||
|
||||
void FinalizationRegistryCleanup::QueueCallback(JSFunction* aDoCleanup,
|
||||
JSObject* aHostDefinedData) {
|
||||
bool firstCallback = mCallbacks.empty();
|
||||
MOZ_ASSERT_IF(!mCallbacks.empty(), mPendingRunnable);
|
||||
|
||||
JSObject* incumbentGlobal = nullptr;
|
||||
|
||||
|
|
@ -1074,9 +1100,9 @@ void FinalizationRegistryCleanup::QueueCallback(JSFunction* aDoCleanup,
|
|||
|
||||
MOZ_ALWAYS_TRUE(mCallbacks.append(Callback{aDoCleanup, incumbentGlobal}));
|
||||
|
||||
if (firstCallback) {
|
||||
RefPtr<CleanupRunnable> cleanup = new CleanupRunnable(this);
|
||||
NS_DispatchToCurrentThread(cleanup.forget());
|
||||
if (!mPendingRunnable) {
|
||||
mPendingRunnable = new CleanupRunnable(this);
|
||||
NS_DispatchToCurrentThread(mPendingRunnable);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -134,6 +134,10 @@ class FinalizationRegistryCleanup {
|
|||
// pointer to its containing context here.
|
||||
CycleCollectedJSContext* mContext;
|
||||
|
||||
// Weak pointer to a previously dispatched runnable. The CleanupRunnable::Run
|
||||
// method will null out this pointer.
|
||||
CleanupRunnable* mPendingRunnable = nullptr;
|
||||
|
||||
using CallbackVector = JS::GCVector<Callback, 0, InfallibleAllocPolicy>;
|
||||
JS::PersistentRooted<CallbackVector> mCallbacks;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1762,8 +1762,15 @@ void CycleCollectedJSRuntime::JSObjectsTenured(JS::GCContext* aGCContext) {
|
|||
|
||||
for (auto iter = objects.Iter(); !iter.Done(); iter.Next()) {
|
||||
nsWrapperCache* cache = iter.Get();
|
||||
if (MOZ_UNLIKELY(!cache)) {
|
||||
continue;
|
||||
}
|
||||
JSObject* wrapper = cache->GetWrapperMaybeDead();
|
||||
MOZ_DIAGNOSTIC_ASSERT(wrapper);
|
||||
if (MOZ_UNLIKELY(!wrapper)) {
|
||||
// Wrapper might have been cleared temporarily while updating reflector
|
||||
// global.
|
||||
continue;
|
||||
}
|
||||
|
||||
if (js::gc::InCollectedNurseryRegion(wrapper)) {
|
||||
MOZ_ASSERT(!cache->PreservingWrapper());
|
||||
|
|
@ -1789,6 +1796,17 @@ void CycleCollectedJSRuntime::NurseryWrapperAdded(nsWrapperCache* aCache) {
|
|||
mNurseryObjects.InfallibleAppend(aCache);
|
||||
}
|
||||
|
||||
void CycleCollectedJSRuntime::NurseryWrapperRemovedSlow(
|
||||
nsWrapperCache* aCache) {
|
||||
MOZ_ASSERT(aCache);
|
||||
for (auto iter = mNurseryObjects.IterFromLast(); !iter.Done(); iter.Prev()) {
|
||||
if (iter.Get() == aCache) {
|
||||
iter.Get() = nullptr;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CycleCollectedJSRuntime::DeferredFinalize(
|
||||
DeferredFinalizeAppendFunction aAppendFunc, DeferredFinalizeFunction aFunc,
|
||||
void* aThing) {
|
||||
|
|
|
|||
|
|
@ -529,6 +529,7 @@ class CycleCollectedJSRuntime {
|
|||
// storage), because we do not want to keep it alive. nsWrapperCache handles
|
||||
// this for us via its "object moved" handling.
|
||||
void NurseryWrapperAdded(nsWrapperCache* aCache);
|
||||
void NurseryWrapperRemovedSlow(nsWrapperCache* aCache);
|
||||
void JSObjectsTenured(JS::GCContext* aGCContext);
|
||||
|
||||
void DeferredFinalize(DeferredFinalizeAppendFunction aAppendFunc,
|
||||
|
|
|
|||
|
|
@ -167,6 +167,7 @@
|
|||
|
||||
#include <utility>
|
||||
|
||||
#include "js/friend/CycleCollector.h"
|
||||
#include "js/SliceBudget.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/Likely.h"
|
||||
|
|
@ -1257,6 +1258,12 @@ class nsCycleCollector : public nsIMemoryReporter {
|
|||
// returns whether anything was collected
|
||||
bool CollectWhite();
|
||||
|
||||
void ClearWhiteJSWeakRefTargets();
|
||||
|
||||
public:
|
||||
bool IsGCThingWhiteInCCGraph(JS::GCCellPtr aPtr);
|
||||
|
||||
private:
|
||||
void CleanupAfterCollection();
|
||||
};
|
||||
|
||||
|
|
@ -3223,6 +3230,8 @@ bool nsCycleCollector::CollectWhite() {
|
|||
// - Unlink(whites), which drops outgoing links on each white.
|
||||
// - Unroot(whites), which returns the whites to normal GC.
|
||||
|
||||
ClearWhiteJSWeakRefTargets();
|
||||
|
||||
// Segments are 4 KiB on 32-bit and 8 KiB on 64-bit.
|
||||
static const size_t kSegmentSize = sizeof(void*) * 1024;
|
||||
SegmentedVector<PtrInfo*, kSegmentSize, InfallibleAllocPolicy> whiteNodes(
|
||||
|
|
@ -3314,6 +3323,31 @@ bool nsCycleCollector::CollectWhite() {
|
|||
return numWhiteNodes > 0 || numWhiteGCed > 0 || numWhiteJSZones > 0;
|
||||
}
|
||||
|
||||
static bool IsGCThingWhiteInCCGraph(JS::GCCellPtr aPtr, void* aData) {
|
||||
auto* cc = static_cast<nsCycleCollector*>(aData);
|
||||
return cc->IsGCThingWhiteInCCGraph(aPtr);
|
||||
}
|
||||
|
||||
bool nsCycleCollector::IsGCThingWhiteInCCGraph(JS::GCCellPtr aPtr) {
|
||||
PtrInfo* pinfo = mGraph.FindNode(aPtr.asCell());
|
||||
if (!pinfo) {
|
||||
return false;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(pinfo->mParticipant);
|
||||
bool isWhite = pinfo->mColor == white;
|
||||
|
||||
MOZ_ASSERT_IF(isWhite, pinfo->IsGrayJS());
|
||||
return isWhite;
|
||||
}
|
||||
|
||||
void nsCycleCollector::ClearWhiteJSWeakRefTargets() {
|
||||
// Clear the targets of JS WeakRef objects whose target is part of a cycle
|
||||
// that we're about to unlink.
|
||||
JSRuntime* runtime = Runtime()->Runtime();
|
||||
JS::MaybeClearWeakRefTargets(runtime, &::IsGCThingWhiteInCCGraph, this);
|
||||
}
|
||||
|
||||
////////////////////////
|
||||
// Memory reporting
|
||||
////////////////////////
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@
|
|||
#include "nsXPCOM.h"
|
||||
#include <atomic>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/Atomics.h"
|
||||
|
|
@ -416,6 +417,31 @@ class ThreadSafeAutoRefCnt {
|
|||
mValue.store(aValue, std::memory_order_release);
|
||||
return aValue;
|
||||
}
|
||||
// Atomically decrements the refcount if it is strictly above Limit.
|
||||
// Returns the pair of {success, new value}.
|
||||
template <nsrefcnt Limit>
|
||||
MOZ_ALWAYS_INLINE std::pair<bool, nsrefcnt> DecrementWithLimit() {
|
||||
// This ensures we can never release the final reference on this thread.
|
||||
// Callers which want to do that should use operator--() which adds the
|
||||
// appropriate fencing.
|
||||
static_assert(Limit > 0,
|
||||
"DecrementWithLimit cannot release the final reference");
|
||||
nsrefcnt count = mValue.load(std::memory_order_relaxed);
|
||||
while (count > Limit) {
|
||||
// Since this may be the last release on this thread, we need
|
||||
// release semantics on success so that prior writes on this thread
|
||||
// are visible to the thread that destroys the object when it reads
|
||||
// mValue with acquire semantics.
|
||||
// On failure, this thread still owns a reference to the object,
|
||||
// so no synchronization is required.
|
||||
if (mValue.compare_exchange_weak(count, count - 1,
|
||||
std::memory_order_release,
|
||||
std::memory_order_relaxed)) {
|
||||
return {true, count - 1};
|
||||
}
|
||||
}
|
||||
return {false, count};
|
||||
}
|
||||
MOZ_ALWAYS_INLINE operator nsrefcnt() const { return get(); }
|
||||
MOZ_ALWAYS_INLINE nsrefcnt get() const {
|
||||
// Use acquire semantics since we're not sure what the caller is
|
||||
|
|
|
|||
|
|
@ -2037,7 +2037,6 @@ STATIC_ATOMS = [
|
|||
Atom("chrome", "chrome"),
|
||||
Atom("moz", "moz"),
|
||||
Atom("moz_icon", "moz-icon"),
|
||||
Atom("moz_gio", "moz-gio"),
|
||||
Atom("proxy", "proxy"),
|
||||
Atom("privateBrowsingAllowedPermission", "internal:privateBrowsingAllowed"),
|
||||
Atom("svgContextPropertiesAllowedPermission", "internal:svgContextPropertiesAllowed"),
|
||||
|
|
|
|||
|
|
@ -43,6 +43,15 @@ NS_INTERFACE_MAP_BEGIN(SlicedInputStream)
|
|||
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIInputStream)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
// It is highly unlikely for a stream to exceed INT64_MAX bytes in length, so we
|
||||
// clamp these values here to allow callers as well as SlicedInputStream to
|
||||
// avoid unnecessary integer overflow checks. Out of range start/length values
|
||||
// should behave as expected in all realistic situations.
|
||||
//
|
||||
// Some stream APIs use an int64_t (e.g. Tell), so we use INT64_MAX as the
|
||||
// maximum internal offset for start/end.
|
||||
static constexpr uint64_t kMaxStreamPos = INT64_MAX;
|
||||
|
||||
SlicedInputStream::SlicedInputStream(
|
||||
already_AddRefed<nsIInputStream> aInputStream, uint64_t aStart,
|
||||
uint64_t aLength)
|
||||
|
|
@ -53,8 +62,8 @@ SlicedInputStream::SlicedInputStream(
|
|||
mWeakAsyncInputStream(nullptr),
|
||||
mWeakInputStreamLength(nullptr),
|
||||
mWeakAsyncInputStreamLength(nullptr),
|
||||
mStart(aStart),
|
||||
mLength(aLength),
|
||||
mStart(std::clamp<uint64_t>(aStart, 0, kMaxStreamPos)),
|
||||
mLength(std::clamp<uint64_t>(aLength, 0, kMaxStreamPos - mStart)),
|
||||
mCurPos(0),
|
||||
mClosed(false),
|
||||
mAsyncWaitFlags(0),
|
||||
|
|
@ -491,11 +500,11 @@ bool SlicedInputStream::Deserialize(
|
|||
|
||||
const SlicedInputStreamParams& params = aParams.get_SlicedInputStreamParams();
|
||||
|
||||
auto end = CheckedUint64(params.start()) + params.length();
|
||||
if (!end.isValid()) {
|
||||
if (params.start() > kMaxStreamPos ||
|
||||
params.length() > kMaxStreamPos - params.start()) {
|
||||
return false;
|
||||
}
|
||||
if (params.curPos() > end.value()) {
|
||||
if (params.curPos() > params.start() + params.length()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -47,7 +47,11 @@ class SlicedInputStream final : public nsIAsyncInputStream,
|
|||
// than aStart bytes, reading from SlicedInputStream returns no data. If
|
||||
// aInputStream contains more than aStart bytes, but fewer than aStart +
|
||||
// aLength bytes, reading from SlicedInputStream returns as many bytes as can
|
||||
// be consumed from aInputStream after reading aLength bytes.
|
||||
// be consumed from aInputStream after reading aStart bytes.
|
||||
//
|
||||
// It is safe to specify an arbitrarily large aLength (e.g. UINT64_MAX). Doing
|
||||
// so is treated as allowing an arbitrary number of additional bytes following
|
||||
// aStart.
|
||||
//
|
||||
// aInputStream should not be read from after constructing a
|
||||
// SlicedInputStream wrapper around it.
|
||||
|
|
|
|||
|
|
@ -27,5 +27,12 @@ interface nsICloneableInputStream : nsISupports
|
|||
[scriptable, builtinclass, uuid(ece853c3-aded-4cef-8f51-0d1493d60bd5)]
|
||||
interface nsICloneableInputStreamWithRange : nsICloneableInputStream
|
||||
{
|
||||
// Create a copy of the input stream, but with the data range reduced to a
|
||||
// sub-slice. The copy will begin at `start` bytes, and extends for a maximum
|
||||
// of `length` bytes. If the underlying stream contains more than `start`
|
||||
// bytes, but fewer than `length` bytes, reading from the stream returns all
|
||||
// remaining bytes.
|
||||
//
|
||||
// See SlicedInputStream's constructor for more details.
|
||||
nsIInputStream cloneWithRange(in uint64_t start, in uint64_t length);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -53,6 +53,7 @@ const char* const sExecutableExts[] = {
|
|||
".chm",
|
||||
".cmd",
|
||||
".com",
|
||||
".command", // Mac script
|
||||
".cpl",
|
||||
".crt",
|
||||
".der",
|
||||
|
|
@ -115,6 +116,7 @@ const char* const sExecutableExts[] = {
|
|||
".scf", // Windows explorer command
|
||||
".scr",
|
||||
".sct",
|
||||
".search-ms", // Windows Saved Search
|
||||
".settingcontent-ms",
|
||||
".shb",
|
||||
".shs",
|
||||
|
|
|
|||
|
|
@ -8,9 +8,9 @@
|
|||
#define _NS_LOCAL_FILE_COMMON_H_
|
||||
|
||||
#ifdef MOZ_ESR
|
||||
extern const char* const sExecutableExts[110];
|
||||
extern const char* const sExecutableExts[112];
|
||||
#else
|
||||
extern const char* const sExecutableExts[111];
|
||||
extern const char* const sExecutableExts[113];
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1958,21 +1958,22 @@ nsLocalFile::IsExecutable(bool* aResult) {
|
|||
// Search for any of the set of executable extensions.
|
||||
static const char* const executableExts[] = {
|
||||
#ifdef MOZ_WIDGET_COCOA
|
||||
"afploc", // Can point to other files.
|
||||
".afploc", // Can point to other files.
|
||||
#endif
|
||||
"air", // Adobe AIR installer
|
||||
".air", // Adobe AIR installer
|
||||
#ifdef MOZ_WIDGET_COCOA
|
||||
"atloc", // Can point to other files.
|
||||
"fileloc", // File location files can be used to point to other
|
||||
// files.
|
||||
"ftploc", // Can point to other files.
|
||||
"inetloc", // Shouldn't be able to do the same, but can, due to
|
||||
// macOS vulnerabilities.
|
||||
"terminal", // macOS Terminal app configuration files
|
||||
".atloc", // Can point to other files.
|
||||
".command", // Mac script
|
||||
".fileloc", // File location files can be used to point to other
|
||||
// files.
|
||||
".ftploc", // Can point to other files.
|
||||
".inetloc", // Shouldn't be able to do the same, but can, due to
|
||||
// macOS vulnerabilities.
|
||||
".terminal", // macOS Terminal app configuration files
|
||||
#endif
|
||||
"jar" // java application bundle
|
||||
".jar" // java application bundle
|
||||
};
|
||||
nsDependentSubstring ext = Substring(path, dotIdx + 1);
|
||||
nsDependentSubstring ext = Substring(path, dotIdx);
|
||||
for (auto executableExt : executableExts) {
|
||||
if (ext.EqualsASCII(executableExt)) {
|
||||
// Found a match. Set result and quit.
|
||||
|
|
|
|||
|
|
@ -43,6 +43,37 @@ ThreadSafeAutoRefCnt nsThreadSafeAutoRefCntRunner::sRefCnt;
|
|||
Atomic<uint32_t, Relaxed> nsThreadSafeAutoRefCntRunner::sIncToOne(0);
|
||||
Atomic<uint32_t, Relaxed> nsThreadSafeAutoRefCntRunner::sDecToZero(0);
|
||||
|
||||
class nsThreadSafeAutoRefCntDecrementWithLimitRunner final : public Runnable {
|
||||
public:
|
||||
static constexpr size_t kDecrementsPerThread = 1000;
|
||||
|
||||
NS_IMETHOD Run() final {
|
||||
for (size_t i = 0; i < kDecrementsPerThread; i++) {
|
||||
auto [ok, count] = sRefCnt.DecrementWithLimit<1>();
|
||||
if (!ok) {
|
||||
sLimitHits++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
static ThreadSafeAutoRefCnt sRefCnt;
|
||||
// Relaxed ordering so sLimitHits doesn't affect the ordering properties of
|
||||
// DecrementWithLimit being tested.
|
||||
static Atomic<uint32_t, Relaxed> sLimitHits;
|
||||
|
||||
nsThreadSafeAutoRefCntDecrementWithLimitRunner()
|
||||
: Runnable("nsThreadSafeAutoRefCntDecrementWithLimitRunner") {}
|
||||
|
||||
private:
|
||||
~nsThreadSafeAutoRefCntDecrementWithLimitRunner() = default;
|
||||
};
|
||||
|
||||
ThreadSafeAutoRefCnt nsThreadSafeAutoRefCntDecrementWithLimitRunner::sRefCnt;
|
||||
Atomic<uint32_t, Relaxed>
|
||||
nsThreadSafeAutoRefCntDecrementWithLimitRunner::sLimitHits(0);
|
||||
|
||||
// When a refcounted object is actually owned by a cache, we may not
|
||||
// want to release the object after last reference gets released. In
|
||||
// this pattern, the cache may rely on the balance of increment to one
|
||||
|
|
@ -51,16 +82,87 @@ TEST(AutoRefCnt, ThreadSafeAutoRefCntBalance)
|
|||
{
|
||||
static const size_t kThreadCount = 4;
|
||||
nsCOMPtr<nsIThread> threads[kThreadCount];
|
||||
for (size_t i = 0; i < kThreadCount; i++) {
|
||||
nsresult rv =
|
||||
NS_NewNamedThread("AutoRefCnt Test", getter_AddRefs(threads[i]),
|
||||
new nsThreadSafeAutoRefCntRunner);
|
||||
for (auto& thread : threads) {
|
||||
nsresult rv = NS_NewNamedThread("AutoRefCnt Test", getter_AddRefs(thread),
|
||||
new nsThreadSafeAutoRefCntRunner);
|
||||
EXPECT_NS_SUCCEEDED(rv);
|
||||
}
|
||||
for (size_t i = 0; i < kThreadCount; i++) {
|
||||
threads[i]->Shutdown();
|
||||
for (const auto& thread : threads) {
|
||||
thread->Shutdown();
|
||||
}
|
||||
EXPECT_EQ(nsThreadSafeAutoRefCntRunner::sRefCnt, nsrefcnt(0));
|
||||
EXPECT_EQ(nsThreadSafeAutoRefCntRunner::sIncToOne,
|
||||
nsThreadSafeAutoRefCntRunner::sDecToZero);
|
||||
}
|
||||
|
||||
// Spawns kThreadCount threads each attempting kDecrementsPerThread
|
||||
// decrements, then verifies the final refcount is 1 and that exactly
|
||||
// aExpectedLimitHits threads hit the limit.
|
||||
// kInitial is derived from aExpectedLimitHits so that exactly
|
||||
// (kThreadCount - aExpectedLimitHits) threads can complete all their
|
||||
// decrements; the rest hit the limit.
|
||||
static void RunDecrementWithLimitThreaded(uint32_t aExpectedLimitHits) {
|
||||
static const size_t kThreadCount = 4;
|
||||
const nsrefcnt kInitial =
|
||||
(kThreadCount - aExpectedLimitHits) *
|
||||
nsThreadSafeAutoRefCntDecrementWithLimitRunner::kDecrementsPerThread +
|
||||
1;
|
||||
nsThreadSafeAutoRefCntDecrementWithLimitRunner::sRefCnt = kInitial;
|
||||
nsThreadSafeAutoRefCntDecrementWithLimitRunner::sLimitHits = 0;
|
||||
|
||||
nsCOMPtr<nsIThread> threads[kThreadCount];
|
||||
for (auto& thread : threads) {
|
||||
nsresult rv =
|
||||
NS_NewNamedThread("AutoRefCnt Test", getter_AddRefs(thread),
|
||||
new nsThreadSafeAutoRefCntDecrementWithLimitRunner);
|
||||
EXPECT_NS_SUCCEEDED(rv);
|
||||
}
|
||||
for (const auto& thread : threads) {
|
||||
thread->Shutdown();
|
||||
}
|
||||
EXPECT_EQ(nsThreadSafeAutoRefCntDecrementWithLimitRunner::sRefCnt,
|
||||
nsrefcnt(1));
|
||||
EXPECT_EQ(nsThreadSafeAutoRefCntDecrementWithLimitRunner::sLimitHits,
|
||||
uint32_t(aExpectedLimitHits));
|
||||
}
|
||||
|
||||
// Verify DecrementWithLimit with no lost updates when the limit is never hit.
|
||||
TEST(AutoRefCnt, ThreadSafeAutoRefCntDecrementWithLimitNoHit)
|
||||
{
|
||||
RunDecrementWithLimitThreaded(0);
|
||||
}
|
||||
|
||||
// Verify DecrementWithLimit correctly enforces the limit. With kInitial == 1
|
||||
// (equal to Limit), all threads hit the limit on their first attempt,
|
||||
// giving a deterministic sLimitHits == kThreadCount.
|
||||
TEST(AutoRefCnt, ThreadSafeAutoRefCntDecrementWithLimitHit)
|
||||
{
|
||||
static const size_t kThreadCount = 4;
|
||||
RunDecrementWithLimitThreaded(kThreadCount);
|
||||
}
|
||||
|
||||
// Verify DecrementWithLimit performs exactly kDecrementsPerThread successful
|
||||
// decrements before hitting the limit. Single-threaded for determinism.
|
||||
TEST(AutoRefCnt, ThreadSafeAutoRefCntDecrementWithLimitSequential)
|
||||
{
|
||||
const nsrefcnt kDecrementsPerThread =
|
||||
nsThreadSafeAutoRefCntDecrementWithLimitRunner::kDecrementsPerThread;
|
||||
// Initialize so exactly kDecrementsPerThread decrements succeed before
|
||||
// hitting the limit at 1.
|
||||
nsThreadSafeAutoRefCntDecrementWithLimitRunner::sRefCnt =
|
||||
kDecrementsPerThread + 1;
|
||||
|
||||
size_t successes = 0;
|
||||
while (true) {
|
||||
auto [ok, count] = nsThreadSafeAutoRefCntDecrementWithLimitRunner::sRefCnt
|
||||
.DecrementWithLimit<1>();
|
||||
if (!ok) {
|
||||
break;
|
||||
}
|
||||
successes++;
|
||||
}
|
||||
|
||||
EXPECT_EQ(successes, size_t(kDecrementsPerThread));
|
||||
EXPECT_EQ(nsThreadSafeAutoRefCntDecrementWithLimitRunner::sRefCnt,
|
||||
nsrefcnt(1));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -307,6 +307,41 @@ TEST(TestSlicedInputStream, LengthBiggerThan)
|
|||
ASSERT_EQ((uint64_t)500, count);
|
||||
}
|
||||
|
||||
// Like LengthBiggerThan, but for an overflowing aStart + aLength pair.
|
||||
TEST(TestSlicedInputStream, LengthMuchBiggerThan)
|
||||
{
|
||||
nsCString buf;
|
||||
RefPtr<SlicedInputStream> sis =
|
||||
CreateNonSeekableStreams(500, 100, UINT64_MAX - 1, buf);
|
||||
|
||||
uint64_t length;
|
||||
ASSERT_EQ(NS_OK, sis->Available(&length));
|
||||
ASSERT_EQ((uint64_t)500 - 100, length);
|
||||
|
||||
char buf2[4096];
|
||||
uint32_t count;
|
||||
ASSERT_EQ(NS_OK, sis->Read(buf2, sizeof(buf2), &count));
|
||||
ASSERT_EQ((uint64_t)(500 - 100), count);
|
||||
ASSERT_EQ(Substring(buf, 100, count), Substring(buf2, count));
|
||||
}
|
||||
|
||||
// Like LengthMuchBiggerThan, but with a massive aStart value.
|
||||
TEST(TestSlicedInputStream, StartMuchBiggerThan)
|
||||
{
|
||||
nsCString buf;
|
||||
RefPtr<SlicedInputStream> sis =
|
||||
CreateNonSeekableStreams(500, UINT64_MAX - 1, 100, buf);
|
||||
|
||||
uint64_t length;
|
||||
ASSERT_EQ(NS_OK, sis->Available(&length));
|
||||
ASSERT_EQ((uint64_t)0, length);
|
||||
|
||||
char buf2[4096];
|
||||
uint32_t count;
|
||||
ASSERT_EQ(NS_OK, sis->Read(buf2, sizeof(buf2), &count));
|
||||
ASSERT_EQ((uint64_t)0, count);
|
||||
}
|
||||
|
||||
// What if the length is 0?
|
||||
TEST(TestSlicedInputStream, Length0)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -466,6 +466,10 @@ nsThreadPool::Run() {
|
|||
MOZ_ASSERT(gCurrentThreadPool.get() == this);
|
||||
gCurrentThreadPool.set(nullptr);
|
||||
|
||||
// Clear the thread's back-pointer into this pool so that the profiler's
|
||||
// SamplerThread stops using it for this thread.
|
||||
static_cast<nsThread*>(current.get())->SetPoolThreadFreePtr(nullptr);
|
||||
|
||||
if (shutdownThreadOnExit) {
|
||||
ShutdownThread(current);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue