michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim:set ts=2 sw=2 sts=2 et cindent: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: #include "nsExpirationTracker.h" michael@0: #include "nsMemory.h" michael@0: #include "nsAutoPtr.h" michael@0: #include "nsString.h" michael@0: #include "nsDirectoryServiceDefs.h" michael@0: #include "nsDirectoryServiceUtils.h" michael@0: #include "nsComponentManagerUtils.h" michael@0: #include "nsXPCOM.h" michael@0: #include "nsIFile.h" michael@0: #include "prinrval.h" michael@0: #include "nsThreadUtils.h" michael@0: michael@0: namespace TestExpirationTracker { michael@0: michael@0: struct Object { michael@0: Object() : mExpired(false) { Touch(); } michael@0: void Touch() { mLastUsed = PR_IntervalNow(); mExpired = false; } michael@0: michael@0: nsExpirationState mExpiration; michael@0: nsExpirationState* GetExpirationState() { return &mExpiration; } michael@0: michael@0: PRIntervalTime mLastUsed; michael@0: bool mExpired; michael@0: }; michael@0: michael@0: static bool error; michael@0: static uint32_t periodMS = 100; michael@0: static uint32_t ops = 1000; michael@0: static uint32_t iterations = 2; michael@0: static bool logging = 0; michael@0: static uint32_t sleepPeriodMS = 50; michael@0: static uint32_t slackMS = 20; // allow this much error michael@0: michael@0: static void SignalError() { michael@0: printf("ERROR!\n"); michael@0: error = true; michael@0: } michael@0: michael@0: template class Tracker : public nsExpirationTracker { michael@0: public: michael@0: Tracker() : nsExpirationTracker(periodMS) { michael@0: Object* obj = new Object(); michael@0: mUniverse.AppendElement(obj); michael@0: LogAction(obj, "Created"); michael@0: } michael@0: michael@0: nsTArray > mUniverse; michael@0: michael@0: void LogAction(Object* aObj, const char* aAction) { michael@0: if (logging) { michael@0: printf("%d %p(%d): %s\n", PR_IntervalNow(), michael@0: static_cast(aObj), aObj->mLastUsed, aAction); michael@0: } michael@0: } michael@0: michael@0: void DoRandomOperation() { michael@0: Object* obj; michael@0: switch (rand() & 0x7) { michael@0: case 0: { michael@0: if (mUniverse.Length() < 50) { michael@0: obj = new Object(); michael@0: mUniverse.AppendElement(obj); michael@0: nsExpirationTracker::AddObject(obj); michael@0: LogAction(obj, "Created and added"); michael@0: } michael@0: break; michael@0: } michael@0: case 4: { michael@0: if (mUniverse.Length() < 50) { michael@0: obj = new Object(); michael@0: mUniverse.AppendElement(obj); michael@0: LogAction(obj, "Created"); michael@0: } michael@0: break; michael@0: } michael@0: case 1: { michael@0: obj = mUniverse[uint32_t(rand())%mUniverse.Length()]; michael@0: if (obj->mExpiration.IsTracked()) { michael@0: nsExpirationTracker::RemoveObject(obj); michael@0: LogAction(obj, "Removed"); michael@0: } michael@0: break; michael@0: } michael@0: case 2: { michael@0: obj = mUniverse[uint32_t(rand())%mUniverse.Length()]; michael@0: if (!obj->mExpiration.IsTracked()) { michael@0: obj->Touch(); michael@0: nsExpirationTracker::AddObject(obj); michael@0: LogAction(obj, "Added"); michael@0: } michael@0: break; michael@0: } michael@0: case 3: { michael@0: obj = mUniverse[uint32_t(rand())%mUniverse.Length()]; michael@0: if (obj->mExpiration.IsTracked()) { michael@0: obj->Touch(); michael@0: nsExpirationTracker::MarkUsed(obj); michael@0: LogAction(obj, "Marked used"); michael@0: } michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: michael@0: protected: michael@0: void NotifyExpired(Object* aObj) { michael@0: LogAction(aObj, "Expired"); michael@0: PRIntervalTime now = PR_IntervalNow(); michael@0: uint32_t timeDiffMS = (now - aObj->mLastUsed)*1000/PR_TicksPerSecond(); michael@0: // See the comment for NotifyExpired in nsExpirationTracker.h for these michael@0: // bounds michael@0: uint32_t lowerBoundMS = (K-1)*periodMS - slackMS; michael@0: uint32_t upperBoundMS = K*(periodMS + sleepPeriodMS) + slackMS; michael@0: if (logging) { michael@0: printf("Checking: %d-%d = %d [%d,%d]\n", michael@0: now, aObj->mLastUsed, timeDiffMS, lowerBoundMS, upperBoundMS); michael@0: } michael@0: if (timeDiffMS < lowerBoundMS || timeDiffMS > upperBoundMS) { michael@0: if (timeDiffMS < periodMS && aObj->mExpired) { michael@0: // This is probably OK, it probably just expired twice michael@0: } else { michael@0: SignalError(); michael@0: } michael@0: } michael@0: aObj->Touch(); michael@0: aObj->mExpired = true; michael@0: DoRandomOperation(); michael@0: DoRandomOperation(); michael@0: DoRandomOperation(); michael@0: } michael@0: }; michael@0: michael@0: template static bool test_random() { michael@0: srand(K); michael@0: error = false; michael@0: michael@0: for (uint32_t j = 0; j < iterations; ++j) { michael@0: Tracker tracker; michael@0: michael@0: uint32_t i = 0; michael@0: for (i = 0; i < ops; ++i) { michael@0: if ((rand() & 0xF) == 0) { michael@0: // Simulate work that takes time michael@0: if (logging) { michael@0: printf("SLEEPING for %dms (%d)\n", sleepPeriodMS, PR_IntervalNow()); michael@0: } michael@0: PR_Sleep(PR_MillisecondsToInterval(sleepPeriodMS)); michael@0: // Process pending timer events michael@0: NS_ProcessPendingEvents(nullptr); michael@0: } michael@0: tracker.DoRandomOperation(); michael@0: } michael@0: } michael@0: michael@0: return !error; michael@0: } michael@0: michael@0: static bool test_random3() { return test_random<3>(); } michael@0: static bool test_random4() { return test_random<4>(); } michael@0: static bool test_random8() { return test_random<8>(); } michael@0: michael@0: typedef bool (*TestFunc)(); michael@0: #define DECL_TEST(name) { #name, name } michael@0: michael@0: static const struct Test { michael@0: const char* name; michael@0: TestFunc func; michael@0: } tests[] = { michael@0: DECL_TEST(test_random3), michael@0: DECL_TEST(test_random4), michael@0: DECL_TEST(test_random8), michael@0: { nullptr, nullptr } michael@0: }; michael@0: michael@0: } michael@0: michael@0: using namespace TestExpirationTracker; michael@0: michael@0: int main(int argc, char **argv) { michael@0: int count = 1; michael@0: if (argc > 1) michael@0: count = atoi(argv[1]); michael@0: michael@0: if (NS_FAILED(NS_InitXPCOM2(nullptr, nullptr, nullptr))) michael@0: return -1; michael@0: michael@0: while (count--) { michael@0: for (const Test* t = tests; t->name != nullptr; ++t) { michael@0: printf("%25s : %s\n", t->name, t->func() ? "SUCCESS" : "FAILURE"); michael@0: } michael@0: } michael@0: michael@0: NS_ShutdownXPCOM(nullptr); michael@0: return 0; michael@0: }