Backed out 5 changesets (bug 1313489, bug 1313488, bug 1313484) in hopes that OS X will go back to building without complaining about missing symbols in nsIFileEnumerator
authorPhil Ringnalda <philringnalda@gmail.com>
Mon, 07 Nov 2016 19:12:13 -0800
changeset 364452 01800003ec3b043995e4ec1daaaf827e7ee2bbee
parent 364451 788b95bf4ea32c2684fb30eb719ed1b078dcd3df
child 364453 d537051ade6a33a18905cb6e901f07e60dbda168
push id6795
push userjlund@mozilla.com
push dateMon, 23 Jan 2017 14:19:46 +0000
treeherdermozilla-beta@76101b503191 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1313489, 1313488, 1313484
milestone52.0a1
backs out276b093c968ffc889f465161b7aa1fe80c796988
5a062f72097d95c6f8c92515380d889286d0daa3
dbd9b56b50e73bf584c5ae8b38e08faac490d7e7
22e770047827a7852201162b86b661cee76479dc
6a7831fbca696651b68f30cbe0d984d2fc1357c3
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Backed out 5 changesets (bug 1313489, bug 1313488, bug 1313484) in hopes that OS X will go back to building without complaining about missing symbols in nsIFileEnumerator Backed out changeset 276b093c968f (bug 1313489) Backed out changeset 5a062f72097d (bug 1313489) Backed out changeset dbd9b56b50e7 (bug 1313488) Backed out changeset 22e770047827 (bug 1313488) Backed out changeset 6a7831fbca69 (bug 1313484) MozReview-Commit-ID: KP9gk9o11co
testing/cppunittest.ini
xpcom/tests/TestDeadlockDetector.cpp
xpcom/tests/TestDeadlockDetectorScalability.cpp
xpcom/tests/TestTimers.cpp
xpcom/tests/gtest/TestDeadlockDetector.cpp
xpcom/tests/gtest/TestDeadlockDetectorScalability.cpp
xpcom/tests/gtest/TestObserverService.cpp
xpcom/tests/gtest/TestTimers.cpp
xpcom/tests/gtest/moz.build
xpcom/tests/moz.build
--- a/testing/cppunittest.ini
+++ b/testing/cppunittest.ini
@@ -13,16 +13,19 @@ skip-if = os == 'b2g'  # Bug 1062937
 skip-if = os != 'win'
 [TestCasting]
 [TestCeilingFloor]
 [TestCertDB]
 [TestCheckedInt]
 [TestCookie]
 [TestCountPopulation]
 [TestCountZeroes]
+[TestDeadlockDetector]
+skip-if = os == 'b2g' || (os == 'android' && debug) # Bug 1054249
+[TestDeadlockDetectorScalability]
 [TestDllInterceptor]
 skip-if = os != 'win'
 [TestEndian]
 [TestEnumeratedArray]
 [TestEnumSet]
 [TestEnumTypeTraits]
 [TestFastBernoulliTrial]
 [TestFloatingPoint]
@@ -51,16 +54,17 @@ skip-if = os == 'android' # Bug 1147630
 [TestSHA1]
 [TestSTSParser]
 [TestSaturate]
 [TestSplayTree]
 [TestStartupCache]
 skip-if = os == 'b2g' || os == 'android'  # Bug 929655
 support-files = TestStartupCacheTelemetry.js TestStartupCacheTelemetry.manifest
 [TestSyncRunnable]
+[TestTimers]
 [TestTXMgr]
 skip-if = os == 'b2g'  #Bug 919595
 [TestTemplateLib]
 [TestTuple]
 [TestTypeTraits]
 [TestTypedEnum]
 [TestUDPSocket]
 [TestUniquePtr]
rename from xpcom/tests/gtest/TestDeadlockDetector.cpp
rename to xpcom/tests/TestDeadlockDetector.cpp
--- a/xpcom/tests/gtest/TestDeadlockDetector.cpp
+++ b/xpcom/tests/TestDeadlockDetector.cpp
@@ -1,113 +1,322 @@
 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: sw=4 ts=4 et :
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/ArrayUtils.h"
 
-#include "prthread.h"
+#include "prenv.h"
+#include "prerror.h"
+#include "prio.h"
+#include "prproces.h"
 
-#include "nsTArray.h"
 #include "nsMemory.h"
 
 #include "mozilla/CondVar.h"
 #include "mozilla/ReentrantMonitor.h"
 #include "mozilla/Mutex.h"
 
-#include "gtest/gtest.h"
+#include "TestHarness.h"
 
 using namespace mozilla;
 
 static PRThread*
 spawn(void (*run)(void*), void* arg)
 {
     return PR_CreateThread(PR_SYSTEM_THREAD,
                            run,
                            arg,
                            PR_PRIORITY_NORMAL,
                            PR_GLOBAL_THREAD,
                            PR_JOINABLE_THREAD,
                            0);
 }
 
-// This global variable is defined in toolkit/xre/nsSigHandlers.cpp.
-extern unsigned int _gdb_sleep_duration;
+#define PASS()                                  \
+    do {                                        \
+        passed(__FUNCTION__);                   \
+        return NS_OK;                           \
+    } while (0)
 
-/**
- * Simple test fixture that makes sure the gdb sleep setup in the
- * ah crap handler is bypassed during the death tests.
- */
-class DeadlockDetectorTest : public ::testing::Test
+#define FAIL(why)                               \
+    do {                                        \
+        fail("%s | %s - %s", __FILE__, __FUNCTION__, why); \
+        return NS_ERROR_FAILURE;                \
+    } while (0)
+
+//-----------------------------------------------------------------------------
+
+static const char* sPathToThisBinary;
+static const char* sAssertBehaviorEnv = "XPCOM_DEBUG_BREAK=abort";
+
+class Subprocess
 {
-protected:
-  void SetUp() final {
-    mOldSleepDuration = _gdb_sleep_duration;
-    _gdb_sleep_duration = 0;
-  }
+public:
+    // not available until process finishes
+    int32_t mExitCode;
+    nsCString mStdout;
+    nsCString mStderr;
+
+    explicit Subprocess(const char* aTestName) {
+        // set up stdio redirection
+        PRFileDesc* readStdin;  PRFileDesc* writeStdin;
+        PRFileDesc* readStdout; PRFileDesc* writeStdout;
+        PRFileDesc* readStderr; PRFileDesc* writeStderr;
+        PRProcessAttr* pattr = PR_NewProcessAttr();
+
+        NS_ASSERTION(pattr, "couldn't allocate process attrs");
+
+        NS_ASSERTION(PR_SUCCESS == PR_CreatePipe(&readStdin, &writeStdin),
+                     "couldn't create child stdin pipe");
+        NS_ASSERTION(PR_SUCCESS == PR_SetFDInheritable(readStdin, true),
+                     "couldn't set child stdin inheritable");
+        PR_ProcessAttrSetStdioRedirect(pattr, PR_StandardInput, readStdin);
+
+        NS_ASSERTION(PR_SUCCESS == PR_CreatePipe(&readStdout, &writeStdout),
+                     "couldn't create child stdout pipe");
+        NS_ASSERTION(PR_SUCCESS == PR_SetFDInheritable(writeStdout, true),
+                     "couldn't set child stdout inheritable");
+        PR_ProcessAttrSetStdioRedirect(pattr, PR_StandardOutput, writeStdout);
+
+        NS_ASSERTION(PR_SUCCESS == PR_CreatePipe(&readStderr, &writeStderr),
+                     "couldn't create child stderr pipe");
+        NS_ASSERTION(PR_SUCCESS == PR_SetFDInheritable(writeStderr, true),
+                     "couldn't set child stderr inheritable");
+        PR_ProcessAttrSetStdioRedirect(pattr, PR_StandardError, writeStderr);
+
+        // set up argv with test name to run
+        char* const newArgv[3] = {
+            strdup(sPathToThisBinary),
+            strdup(aTestName),
+            0
+        };
+
+        // make sure the child will abort if an assertion fails
+        NS_ASSERTION(PR_SUCCESS == PR_SetEnv(sAssertBehaviorEnv),
+                     "couldn't set XPCOM_DEBUG_BREAK env var");
+
+        PRProcess* proc;
+        NS_ASSERTION(proc = PR_CreateProcess(sPathToThisBinary,
+                                             newArgv,
+                                             0, // inherit environment
+                                             pattr),
+                     "couldn't create process");
+        PR_Close(readStdin);
+        PR_Close(writeStdout);
+        PR_Close(writeStderr);
+
+        mProc = proc;
+        mStdinfd = writeStdin;
+        mStdoutfd = readStdout;
+        mStderrfd = readStderr;
+
+        free(newArgv[0]);
+        free(newArgv[1]);
+        PR_DestroyProcessAttr(pattr);
+    }
+
+    void RunToCompletion(uint32_t aWaitMs)
+    {
+        PR_Close(mStdinfd);
 
-  void TearDown() final {
-    _gdb_sleep_duration = mOldSleepDuration;
-  }
+        PRPollDesc pollfds[2];
+        int32_t nfds;
+        bool stdoutOpen = true, stderrOpen = true;
+        char buf[4096];
+
+        PRIntervalTime now = PR_IntervalNow();
+        PRIntervalTime deadline = now + PR_MillisecondsToInterval(aWaitMs);
+
+        while ((stdoutOpen || stderrOpen) && now < deadline) {
+            nfds = 0;
+            if (stdoutOpen) {
+                pollfds[nfds].fd = mStdoutfd;
+                pollfds[nfds].in_flags = PR_POLL_READ;
+                pollfds[nfds].out_flags = 0;
+                ++nfds;
+            }
+            if (stderrOpen) {
+                pollfds[nfds].fd = mStderrfd;
+                pollfds[nfds].in_flags = PR_POLL_READ;
+                pollfds[nfds].out_flags = 0;
+                ++nfds;
+            }
+
+            int32_t rv = PR_Poll(pollfds, nfds, deadline - now);
+            NS_ASSERTION(0 <= rv, PR_ErrorToName(PR_GetError()));
+
+            if (0 == rv) {      // timeout
+                fputs("(timed out!)\n", stderr);
+                Finish(false); // abnormal
+                return;
+            }
+
+            for (int32_t i = 0; i < nfds; ++i) {
+                if (!pollfds[i].out_flags)
+                    continue;
+
+                bool isStdout = mStdoutfd == pollfds[i].fd;
+                int32_t len = 0;
+
+                if (PR_POLL_READ & pollfds[i].out_flags) {
+                    len = PR_Read(pollfds[i].fd, buf, sizeof(buf) - 1);
+                    NS_ASSERTION(0 <= len, PR_ErrorToName(PR_GetError()));
+                }
+                else if (!(PR_POLL_HUP & pollfds[i].out_flags)) {
+                    NS_ERROR(PR_ErrorToName(PR_GetError()));
+                }
+
+                if (0 < len) {
+                    buf[len] = '\0';
+                    if (isStdout)
+                        mStdout += buf;
+                    else
+                        mStderr += buf;
+                }
+                else if (isStdout) {
+                    stdoutOpen = false;
+                }
+                else {
+                    stderrOpen = false;
+                }
+            }
+
+            now = PR_IntervalNow();
+        }
+
+        if (stdoutOpen)
+            fputs("(stdout still open!)\n", stderr);
+        if (stderrOpen)
+            fputs("(stderr still open!)\n", stderr);
+        if (now > deadline)
+            fputs("(timed out!)\n", stderr);
+
+        Finish(!stdoutOpen && !stderrOpen && now <= deadline);
+    }
 
 private:
-  unsigned int mOldSleepDuration;
+    void Finish(bool normalExit) {
+        if (!normalExit) {
+            PR_KillProcess(mProc);
+            mExitCode = -1;
+            int32_t dummy;
+            PR_WaitProcess(mProc, &dummy);
+        }
+        else {
+            PR_WaitProcess(mProc, &mExitCode); // this had better not block ...
+        }
+
+        PR_Close(mStdoutfd);
+        PR_Close(mStderrfd);
+    }
+
+    PRProcess* mProc;
+    PRFileDesc* mStdinfd;         // writeable
+    PRFileDesc* mStdoutfd;        // readable
+    PRFileDesc* mStderrfd;        // readable
 };
 
 //-----------------------------------------------------------------------------
+// Harness for checking detector errors
+bool
+CheckForDeadlock(const char* test, const char* const* findTokens)
+{
+    Subprocess proc(test);
+    proc.RunToCompletion(5000);
+
+    if (0 == proc.mExitCode)
+        return false;
+
+    int32_t idx = 0;
+    for (const char* const* tp = findTokens; *tp; ++tp) {
+        const char* const token = *tp;
+#ifdef MOZILLA_INTERNAL_API
+        idx = proc.mStderr.Find(token, false, idx);
+#else
+        nsCString tokenCString(token);
+        idx = proc.mStderr.Find(tokenCString, idx);
+#endif
+        if (-1 == idx) {
+            printf("(missed token '%s' in output)\n", token);
+            puts("----------------------------------\n");
+            puts(proc.mStderr.get());
+            puts("----------------------------------\n");
+            return false;
+        }
+        idx += strlen(token);
+    }
+
+    return true;
+}
+
+//-----------------------------------------------------------------------------
 // Single-threaded sanity tests
 
 // Stupidest possible deadlock.
 int
 Sanity_Child()
 {
     mozilla::Mutex m1("dd.sanity.m1");
     m1.Lock();
     m1.Lock();
     return 0;                  // not reached
 }
 
-TEST_F(DeadlockDetectorTest, SanityDeathTest)
+nsresult
+Sanity()
 {
-    const char* const regex =
-        "###!!! ERROR: Potential deadlock detected.*"
-        "=== Cyclical dependency starts at.*--- Mutex : dd.sanity.m1.*"
-        "=== Cycle completed at.*--- Mutex : dd.sanity.m1.*"
-        "###!!! Deadlock may happen NOW!.*" // better catch these easy cases...
-        "###!!! ASSERTION: Potential deadlock detected.*";
-
-    ASSERT_DEATH(Sanity_Child(), regex);
+    const char* const tokens[] = {
+        "###!!! ERROR: Potential deadlock detected",
+        "=== Cyclical dependency starts at\n--- Mutex : dd.sanity.m1",
+        "=== Cycle completed at\n--- Mutex : dd.sanity.m1",
+        "###!!! Deadlock may happen NOW!", // better catch these easy cases...
+        "###!!! ASSERTION: Potential deadlock detected",
+        0
+    };
+    if (CheckForDeadlock("Sanity", tokens)) {
+        PASS();
+    } else {
+        FAIL("deadlock not detected");
+    }
 }
 
 // Slightly less stupid deadlock.
 int
 Sanity2_Child()
 {
     mozilla::Mutex m1("dd.sanity2.m1");
     mozilla::Mutex m2("dd.sanity2.m2");
     m1.Lock();
     m2.Lock();
     m1.Lock();
     return 0;                  // not reached
 }
 
-TEST_F(DeadlockDetectorTest, Sanity2DeathTest)
+nsresult
+Sanity2()
 {
-    const char* const regex =
-        "###!!! ERROR: Potential deadlock detected.*"
-        "=== Cyclical dependency starts at.*--- Mutex : dd.sanity2.m1.*"
-        "--- Next dependency:.*--- Mutex : dd.sanity2.m2.*"
-        "=== Cycle completed at.*--- Mutex : dd.sanity2.m1.*"
-        "###!!! Deadlock may happen NOW!.*" // better catch these easy cases...
-        "###!!! ASSERTION: Potential deadlock detected.*";
+    const char* const tokens[] = {
+        "###!!! ERROR: Potential deadlock detected",
+        "=== Cyclical dependency starts at\n--- Mutex : dd.sanity2.m1",
+        "--- Next dependency:\n--- Mutex : dd.sanity2.m2",
+        "=== Cycle completed at\n--- Mutex : dd.sanity2.m1",
+        "###!!! Deadlock may happen NOW!", // better catch these easy cases...
+        "###!!! ASSERTION: Potential deadlock detected",
+        0
+    };
+    if (CheckForDeadlock("Sanity2", tokens)) {
+        PASS();
+    } else {
+        FAIL("deadlock not detected");
+    }
+}
 
-    ASSERT_DEATH(Sanity2_Child(), regex);
-}
 
 int
 Sanity3_Child()
 {
     mozilla::Mutex m1("dd.sanity3.m1");
     mozilla::Mutex m2("dd.sanity3.m2");
     mozilla::Mutex m3("dd.sanity3.m3");
     mozilla::Mutex m4("dd.sanity3.m4");
@@ -121,175 +330,237 @@ Sanity3_Child()
     m2.Unlock();
     m1.Unlock();
 
     m4.Lock();
     m1.Lock();
     return 0;
 }
 
-TEST_F(DeadlockDetectorTest, Sanity3DeathTest)
+nsresult
+Sanity3()
 {
-    const char* const regex =
-        "###!!! ERROR: Potential deadlock detected.*"
-        "=== Cyclical dependency starts at.*--- Mutex : dd.sanity3.m1.*"
-        "--- Next dependency:.*--- Mutex : dd.sanity3.m2.*"
-        "--- Next dependency:.*--- Mutex : dd.sanity3.m3.*"
-        "--- Next dependency:.*--- Mutex : dd.sanity3.m4.*"
-        "=== Cycle completed at.*--- Mutex : dd.sanity3.m1.*"
-        "###!!! ASSERTION: Potential deadlock detected.*";
+    const char* const tokens[] = {
+        "###!!! ERROR: Potential deadlock detected",
+        "=== Cyclical dependency starts at\n--- Mutex : dd.sanity3.m1",
+        "--- Next dependency:\n--- Mutex : dd.sanity3.m2",
+        "--- Next dependency:\n--- Mutex : dd.sanity3.m3",
+        "--- Next dependency:\n--- Mutex : dd.sanity3.m4",
+        "=== Cycle completed at\n--- Mutex : dd.sanity3.m1",
+        "###!!! ASSERTION: Potential deadlock detected",
+        0
+    };
+    if (CheckForDeadlock("Sanity3", tokens)) {
+        PASS();
+    } else {
+        FAIL("deadlock not detected");
+    }
+}
 
-    ASSERT_DEATH(Sanity3_Child(), regex);
-}
 
 int
 Sanity4_Child()
 {
     mozilla::ReentrantMonitor m1("dd.sanity4.m1");
     mozilla::Mutex m2("dd.sanity4.m2");
     m1.Enter();
     m2.Lock();
     m1.Enter();
     return 0;
 }
 
-TEST_F(DeadlockDetectorTest, Sanity4DeathTest)
+nsresult
+Sanity4()
 {
-    const char* const regex =
-        "Re-entering ReentrantMonitor after acquiring other resources.*"
-        "###!!! ERROR: Potential deadlock detected.*"
-        "=== Cyclical dependency starts at.*--- ReentrantMonitor : dd.sanity4.m1.*"
-        "--- Next dependency:.*--- Mutex : dd.sanity4.m2.*"
-        "=== Cycle completed at.*--- ReentrantMonitor : dd.sanity4.m1.*"
-        "###!!! ASSERTION: Potential deadlock detected.*";
-    ASSERT_DEATH(Sanity4_Child(), regex);
+    const char* const tokens[] = {
+        "Re-entering ReentrantMonitor after acquiring other resources",
+        "###!!! ERROR: Potential deadlock detected",
+        "=== Cyclical dependency starts at\n--- ReentrantMonitor : dd.sanity4.m1",
+        "--- Next dependency:\n--- Mutex : dd.sanity4.m2",
+        "=== Cycle completed at\n--- ReentrantMonitor : dd.sanity4.m1",
+        "###!!! ASSERTION: Potential deadlock detected",
+        0
+    };
+    if (CheckForDeadlock("Sanity4", tokens)) {
+        PASS();
+    } else {
+        FAIL("deadlock not detected");
+    }
 }
 
 //-----------------------------------------------------------------------------
 // Multithreaded tests
 
-/**
- * Helper for passing state to threads in the multithread tests.
- */
-struct ThreadState
-{
-  /**
-   * Locks to use during the test. This is just a reference and is owned by
-   * the main test thread.
-   */
-  const nsTArray<mozilla::Mutex*>& locks;
-
-  /**
-   * Integer argument used to identify each thread.
-   */
-  int id;
-};
+mozilla::Mutex* ttM1;
+mozilla::Mutex* ttM2;
 
 static void
 TwoThreads_thread(void* arg)
 {
-    ThreadState* state = static_cast<ThreadState*>(arg);
-
-    mozilla::Mutex* ttM1 = state->locks[0];
-    mozilla::Mutex* ttM2 = state->locks[1];
-
-    if (state->id) {
+    int32_t m1First = NS_PTR_TO_INT32(arg);
+    if (m1First) {
         ttM1->Lock();
         ttM2->Lock();
         ttM2->Unlock();
         ttM1->Unlock();
     }
     else {
         ttM2->Lock();
         ttM1->Lock();
         ttM1->Unlock();
         ttM2->Unlock();
     }
 }
 
 int
 TwoThreads_Child()
 {
-    nsTArray<mozilla::Mutex*> locks = {
-      new mozilla::Mutex("dd.twothreads.m1"),
-      new mozilla::Mutex("dd.twothreads.m2")
-    };
+    ttM1 = new mozilla::Mutex("dd.twothreads.m1");
+    ttM2 = new mozilla::Mutex("dd.twothreads.m2");
+    if (!ttM1 || !ttM2)
+        NS_RUNTIMEABORT("couldn't allocate mutexes");
 
-    ThreadState state_1 {locks, 0};
-    PRThread* t1 = spawn(TwoThreads_thread, &state_1);
+    PRThread* t1 = spawn(TwoThreads_thread, (void*) 0);
     PR_JoinThread(t1);
 
-    ThreadState state_2 {locks, 1};
-    PRThread* t2 = spawn(TwoThreads_thread, &state_2);
+    PRThread* t2 = spawn(TwoThreads_thread, (void*) 1);
     PR_JoinThread(t2);
 
-    for (auto& lock : locks) {
-      delete lock;
-    }
-
     return 0;
 }
 
-TEST_F(DeadlockDetectorTest, TwoThreadsDeathTest)
+nsresult
+TwoThreads()
 {
-    const char* const regex =
-        "###!!! ERROR: Potential deadlock detected.*"
-        "=== Cyclical dependency starts at.*--- Mutex : dd.twothreads.m2.*"
-        "--- Next dependency:.*--- Mutex : dd.twothreads.m1.*"
-        "=== Cycle completed at.*--- Mutex : dd.twothreads.m2.*"
-        "###!!! ASSERTION: Potential deadlock detected.*";
+    const char* const tokens[] = {
+        "###!!! ERROR: Potential deadlock detected",
+        "=== Cyclical dependency starts at\n--- Mutex : dd.twothreads.m2",
+        "--- Next dependency:\n--- Mutex : dd.twothreads.m1",
+        "=== Cycle completed at\n--- Mutex : dd.twothreads.m2",
+        "###!!! ASSERTION: Potential deadlock detected",
+        0
+    };
 
-    ASSERT_DEATH(TwoThreads_Child(), regex);
+    if (CheckForDeadlock("TwoThreads", tokens)) {
+        PASS();
+    } else {
+        FAIL("deadlock not detected");
+    }
 }
 
+
+mozilla::Mutex* cndMs[4];
+const uint32_t K = 100000;
+
 static void
 ContentionNoDeadlock_thread(void* arg)
 {
-    const uint32_t K = 100000;
-
-    ThreadState* state = static_cast<ThreadState*>(arg);
-    int32_t starti = static_cast<int32_t>(state->id);
-    auto& cndMs = state->locks;
+    int32_t starti = NS_PTR_TO_INT32(arg);
 
     for (uint32_t k = 0; k < K; ++k) {
-        for (int32_t i = starti; i < (int32_t)cndMs.Length(); ++i)
+        for (int32_t i = starti; i < (int32_t) ArrayLength(cndMs); ++i)
             cndMs[i]->Lock();
         // comment out the next two lines for deadlocking fun!
-        for (int32_t i = cndMs.Length() - 1; i >= starti; --i)
+        for (int32_t i = ArrayLength(cndMs) - 1; i >= starti; --i)
             cndMs[i]->Unlock();
 
         starti = (starti + 1) % 3;
     }
 }
 
 int
 ContentionNoDeadlock_Child()
 {
-    const size_t kMutexCount = 4;
+    PRThread* threads[3];
 
-    PRThread* threads[3];
-    nsTArray<mozilla::Mutex*> locks;
-    ThreadState states[] = {
-      { locks, 0 },
-      { locks, 1 },
-      { locks, 2 }
-    };
-
-    for (uint32_t i = 0; i < kMutexCount; ++i)
-        locks.AppendElement(new mozilla::Mutex("dd.cnd.ms"));
+    for (uint32_t i = 0; i < ArrayLength(cndMs); ++i)
+        cndMs[i] = new mozilla::Mutex("dd.cnd.ms");
 
     for (int32_t i = 0; i < (int32_t) ArrayLength(threads); ++i)
-        threads[i] = spawn(ContentionNoDeadlock_thread, states + i);
+        threads[i] = spawn(ContentionNoDeadlock_thread, NS_INT32_TO_PTR(i));
 
     for (uint32_t i = 0; i < ArrayLength(threads); ++i)
         PR_JoinThread(threads[i]);
 
-    for (uint32_t i = 0; i < locks.Length(); ++i)
-        delete locks[i];
+    for (uint32_t i = 0; i < ArrayLength(cndMs); ++i)
+        delete cndMs[i];
 
     return 0;
 }
 
-TEST_F(DeadlockDetectorTest, ContentionNoDeadlock)
+nsresult
+ContentionNoDeadlock()
+{
+    const char * func = __func__;
+    Subprocess proc(func);
+    proc.RunToCompletion(60000);
+    if (0 != proc.mExitCode) {
+        printf("(expected 0 == return code, got %d)\n", proc.mExitCode);
+        puts("(output)\n----------------------------------\n");
+        puts(proc.mStdout.get());
+        puts("----------------------------------\n");
+        puts("(error output)\n----------------------------------\n");
+        puts(proc.mStderr.get());
+        puts("----------------------------------\n");
+
+        FAIL("deadlock");
+    }
+    PASS();
+}
+
+
+
+//-----------------------------------------------------------------------------
+
+int
+main(int argc, char** argv)
 {
-  // Just check that this test runs to completion.
-  ASSERT_EQ(ContentionNoDeadlock_Child(), 0);
+    if (1 < argc) {
+        // XXX can we run w/o scoped XPCOM?
+        const char* test = argv[1];
+        ScopedXPCOM xpcom(test);
+        if (xpcom.failed())
+            return 1;
+
+        // running in a spawned process.  call the specificed child function.
+        if (!strcmp("Sanity", test))
+            return Sanity_Child();
+        if (!strcmp("Sanity2", test))
+            return Sanity2_Child();
+        if (!strcmp("Sanity3", test))
+            return Sanity3_Child();
+        if (!strcmp("Sanity4", test))
+            return Sanity4_Child();
+
+        if (!strcmp("TwoThreads", test))
+            return TwoThreads_Child();
+        if (!strcmp("ContentionNoDeadlock", test))
+            return ContentionNoDeadlock_Child();
+
+        fail("%s | %s - unknown child test", __FILE__, __FUNCTION__);
+        return 2;
+    }
+
+    ScopedXPCOM xpcom("XPCOM deadlock detector correctness (" __FILE__ ")");
+    if (xpcom.failed())
+        return 1;
+
+    // in the first invocation of this process.  we will be the "driver".
+    int rv = 0;
+
+    sPathToThisBinary = argv[0];
+
+    if (NS_FAILED(Sanity()))
+        rv = 1;
+    if (NS_FAILED(Sanity2()))
+        rv = 1;
+    if (NS_FAILED(Sanity3()))
+        rv = 1;
+    if (NS_FAILED(Sanity4()))
+        rv = 1;
+
+    if (NS_FAILED(TwoThreads()))
+        rv = 1;
+    if (NS_FAILED(ContentionNoDeadlock()))
+        rv = 1;
+
+    return rv;
 }
rename from xpcom/tests/gtest/TestDeadlockDetectorScalability.cpp
rename to xpcom/tests/TestDeadlockDetectorScalability.cpp
--- a/xpcom/tests/gtest/TestDeadlockDetectorScalability.cpp
+++ b/xpcom/tests/TestDeadlockDetectorScalability.cpp
@@ -2,169 +2,245 @@
  * vim: sw=4 ts=4 et :
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 // Avoid DMD-specific parts of MOZ_DEFINE_MALLOC_SIZE_OF
 #undef MOZ_DMD
 
+#include "TestHarness.h"
 #include "nsIMemoryReporter.h"
-#include "mozilla/Mutex.h"
+
+//#define OLD_API
+
+#define PASS()                                  \
+    do {                                        \
+        passed(__FUNCTION__);                   \
+        return NS_OK;                           \
+    } while (0)
+
+#define FAIL(why)                               \
+    do {                                        \
+        fail("%s | %s - %s", __FILE__, __FUNCTION__, why); \
+        return NS_ERROR_FAILURE;                \
+    } while (0)
 
-#include "gtest/gtest.h"
+#ifdef OLD_API
+#  include "nsAutoLock.h"
+   typedef PRLock* moz_lock_t;
+#  define NEWLOCK(n) nsAutoLock::NewLock(n)
+#  define DELETELOCK(v) nsAutoLock::DestroyLock(v)
+#  define AUTOLOCK(v, l) nsAutoLock v(l)
+#else
+#  include "mozilla/Mutex.h"
+   typedef mozilla::Mutex* moz_lock_t;
+#  define NEWLOCK(n) new mozilla::Mutex(n)
+#  define DELETELOCK(v) delete (v)
+#  define AUTOLOCK(v, l) mozilla::MutexAutoLock v(*l)
+#endif
+
+// def/undef these to run particular tests.
+#undef DD_TEST1
+#undef DD_TEST2
+#undef DD_TEST3
+#undef DD_TEST4
 
 //-----------------------------------------------------------------------------
 
+#ifdef DD_TEST1
+
 static void
 AllocLockRecurseUnlockFree(int i)
 {
     if (0 == i)
         return;
 
-    mozilla::Mutex* lock = new mozilla::Mutex("deadlockDetector.scalability.t1");
+    moz_lock_t lock = NEWLOCK("deadlockDetector.scalability.t1");
     {
-        mozilla::MutexAutoLock _(*lock);
+        AUTOLOCK(_, lock);
         AllocLockRecurseUnlockFree(i - 1);
     }
-    delete lock;
+    DELETELOCK(lock);
 }
 
 // This test creates a resource dependency chain N elements long, then
 // frees all the resources in the chain.
-TEST(DeadlockDetectorScalability, LengthNDepChain)
+static nsresult
+LengthNDepChain(int N)
 {
-    const int N = 1 << 14; // 16K
     AllocLockRecurseUnlockFree(N);
-    ASSERT_TRUE(true);
+    PASS();
 }
 
+#endif
+
 //-----------------------------------------------------------------------------
 
+#ifdef DD_TEST2
+
 // This test creates a single lock that is ordered < N resources, then
 // repeatedly exercises this order k times.
-//
-// NB: It takes a minute or two to run so it is disabled by default.
-TEST(DeadlockDetectorScalability, DISABLED_OneLockNDeps)
+static nsresult
+OneLockNDeps(const int N, const int K)
 {
-    // NB: Using a larger test size to stress our traversal logic.
-    const int N = 1 << 17; // 131k
-    const int K = 100;
-
-    mozilla::Mutex* lock = new mozilla::Mutex("deadlockDetector.scalability.t2.master");
-    mozilla::Mutex** locks = new mozilla::Mutex*[N];
+    moz_lock_t lock = NEWLOCK("deadlockDetector.scalability.t2.master");
+    moz_lock_t* locks = new moz_lock_t[N];
     if (!locks)
         NS_RUNTIMEABORT("couldn't allocate lock array");
 
     for (int i = 0; i < N; ++i)
         locks[i] =
-            new mozilla::Mutex("deadlockDetector.scalability.t2.dep");
+            NEWLOCK("deadlockDetector.scalability.t2.dep");
 
     // establish orders
-    {mozilla::MutexAutoLock m(*lock);
+    {AUTOLOCK(m, lock);
         for (int i = 0; i < N; ++i)
-            mozilla::MutexAutoLock s(*locks[i]);
+            AUTOLOCK(s, locks[i]);
     }
 
     // exercise order check
-    {mozilla::MutexAutoLock m(*lock);
+    {AUTOLOCK(m, lock);
         for (int i = 0; i < K; ++i)
             for (int j = 0; j < N; ++j)
-                mozilla::MutexAutoLock s(*locks[i]);
+                AUTOLOCK(s, locks[i]);
     }
 
     for (int i = 0; i < N; ++i)
-        delete locks[i];
+        DELETELOCK(locks[i]);
     delete[] locks;
 
-    ASSERT_TRUE(true);
+    PASS();
 }
 
+#endif
+
 //-----------------------------------------------------------------------------
 
+#ifdef DD_TEST3
+
 // This test creates N resources and adds the theoretical maximum number
 // of dependencies, O(N^2).  It then repeats that sequence of
 // acquisitions k times.  Finally, all resources are freed.
 //
 // It's very difficult to perform well on this test.  It's put forth as a
 // challenge problem.
 
-TEST(DeadlockDetectorScalability, MaxDepsNsq)
+static nsresult
+MaxDepsNsq(const int N, const int K)
 {
-    const int N = 1 << 10; // 1k
-    const int K = 10;
-
-    mozilla::Mutex** locks = new mozilla::Mutex*[N];
+    moz_lock_t* locks = new moz_lock_t[N];
     if (!locks)
         NS_RUNTIMEABORT("couldn't allocate lock array");
 
     for (int i = 0; i < N; ++i)
-        locks[i] = new mozilla::Mutex("deadlockDetector.scalability.t3");
+        locks[i] = NEWLOCK("deadlockDetector.scalability.t3");
 
     for (int i = 0; i < N; ++i) {
-        mozilla::MutexAutoLock al1(*locks[i]);
+        AUTOLOCK(al1, locks[i]);
         for (int j = i+1; j < N; ++j)
-            mozilla::MutexAutoLock al2(*locks[j]);
+            AUTOLOCK(al2, locks[j]);
     }
 
     for (int i = 0; i < K; ++i) {
         for (int j = 0; j < N; ++j) {
-            mozilla::MutexAutoLock al1(*locks[j]);
+            AUTOLOCK(al1, locks[j]);
             for (int k = j+1; k < N; ++k)
-                mozilla::MutexAutoLock al2(*locks[k]);
+                AUTOLOCK(al2, locks[k]);
         }
     }
 
     for (int i = 0; i < N; ++i)
-        delete locks[i];
+        DELETELOCK(locks[i]);
     delete[] locks;
 
-    ASSERT_TRUE(true);
+    PASS();
 }
 
+#endif
+
 //-----------------------------------------------------------------------------
 
+#ifdef DD_TEST4
+
 // This test creates a single lock that is ordered < N resources. The
 // resources are allocated, exercised K times, and deallocated one at
 // a time.
 
-TEST(DeadlockDetectorScalability, OneLockNDepsUsedSeveralTimes)
+static nsresult
+OneLockNDepsUsedSeveralTimes(const size_t N, const size_t K)
 {
-    const size_t N = 1 << 17; // 131k
-    const size_t K = 3;
-
     // Create master lock.
-    mozilla::Mutex* lock_1 = new mozilla::Mutex("deadlockDetector.scalability.t4.master");
+    moz_lock_t lock_1 = NEWLOCK("deadlockDetector.scalability.t4.master");
     for (size_t n = 0; n < N; n++) {
         // Create child lock.
-        mozilla::Mutex* lock_2 = new mozilla::Mutex("deadlockDetector.scalability.t4.child");
+        moz_lock_t lock_2 = NEWLOCK("deadlockDetector.scalability.t4.child");
 
         // First lock the master.
-        mozilla::MutexAutoLock m(*lock_1);
+        AUTOLOCK(m, lock_1);
 
         // Now lock and unlock the child a few times.
         for (size_t k = 0; k < K; k++) {
-            mozilla::MutexAutoLock c(*lock_2);
+            AUTOLOCK(c, lock_2);
         }
 
         // Destroy the child lock.
-        delete lock_2;
+        DELETELOCK(lock_2);
     }
 
     // Cleanup the master lock.
-    delete lock_1;
+    DELETELOCK(lock_1);
 
-    ASSERT_TRUE(true);
+    PASS();
 }
 
+#endif
+
 //-----------------------------------------------------------------------------
 
 MOZ_DEFINE_MALLOC_SIZE_OF(DeadlockDetectorMallocSizeOf)
 
-// This is a simple test that exercises the deadlock detector memory reporting
-// functionality.
-TEST(DeadlockDetectorScalability, SizeOf)
+int
+main(int argc, char** argv)
 {
+    ScopedXPCOM xpcom("Deadlock detector scalability (" __FILE__ ")");
+    if (xpcom.failed())
+        return 1;
+
+    int rv = 0;
+
+    // Uncomment these tests to run them.  Not expected to be common.
+
+#ifndef DD_TEST1
+    puts("Skipping not-requested LengthNDepChain() test");
+#else
+    if (NS_FAILED(LengthNDepChain(1 << 14))) // 16K
+        rv = 1;
+#endif
+
+#ifndef DD_TEST2
+    puts("Skipping not-requested OneLockNDeps() test");
+#else
+    // NB: Using a larger test size to stress our traversal logic.
+    if (NS_FAILED(OneLockNDeps(1 << 17, 100))) // 131k
+        rv = 1;
+#endif
+
+#ifndef DD_TEST3
+    puts("Skipping not-requested MaxDepsNsq() test");
+#else
+    if (NS_FAILED(MaxDepsNsq(1 << 10, 10))) // 1k
+        rv = 1;
+#endif
+
+#ifndef DD_TEST4
+    puts("Skipping not-requested OneLockNDepsUsedSeveralTimes() test");
+#else
+    if (NS_FAILED(OneLockNDepsUsedSeveralTimes(1 << 17, 3))) // 131k
+        rv = 1;
+#endif
+
     size_t memory_used = mozilla::BlockingResourceBase::SizeOfDeadlockDetector(
         DeadlockDetectorMallocSizeOf);
+    printf_stderr("Used %d bytes\n", (int)memory_used);
 
-    ASSERT_GT(memory_used, size_t(0));
+    return rv;
 }
rename from xpcom/tests/gtest/TestTimers.cpp
rename to xpcom/tests/TestTimers.cpp
--- a/xpcom/tests/gtest/TestTimers.cpp
+++ b/xpcom/tests/TestTimers.cpp
@@ -1,31 +1,32 @@
 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+#include "TestHarness.h"
+
 #include "nsIThread.h"
 #include "nsITimer.h"
 
 #include "nsCOMPtr.h"
 #include "nsComponentManagerUtils.h"
 #include "nsServiceManagerUtils.h"
 #include "nsThreadUtils.h"
 #include "prinrval.h"
 #include "prmon.h"
 #include "prthread.h"
 #include "mozilla/Attributes.h"
 
 #include "mozilla/ReentrantMonitor.h"
 
 #include <list>
 #include <vector>
-
-#include "gtest/gtest.h"
+#include <stdio.h>
 
 using namespace mozilla;
 
 typedef nsresult(*TestFuncPtr)();
 
 class AutoTestThread
 {
 public:
@@ -99,74 +100,80 @@ private:
   ~TimerCallback() {}
 
   nsIThread** mThreadPtr;
   ReentrantMonitor* mReentrantMonitor;
 };
 
 NS_IMPL_ISUPPORTS(TimerCallback, nsITimerCallback)
 
-TEST(Timers, TargetedTimers)
+nsresult
+TestTargetedTimers()
 {
   AutoCreateAndDestroyReentrantMonitor newMon;
-  ASSERT_TRUE(newMon);
+  NS_ENSURE_TRUE(newMon, NS_ERROR_OUT_OF_MEMORY);
 
   AutoTestThread testThread;
-  ASSERT_TRUE(testThread);
+  NS_ENSURE_TRUE(testThread, NS_ERROR_OUT_OF_MEMORY);
 
   nsresult rv;
   nsCOMPtr<nsITimer> timer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv);
-  ASSERT_TRUE(NS_SUCCEEDED(rv));
+  NS_ENSURE_SUCCESS(rv, rv);
 
   nsIEventTarget* target = static_cast<nsIEventTarget*>(testThread);
 
   rv = timer->SetTarget(target);
-  ASSERT_TRUE(NS_SUCCEEDED(rv));
+  NS_ENSURE_SUCCESS(rv, rv);
 
   nsIThread* notifiedThread = nullptr;
 
   nsCOMPtr<nsITimerCallback> callback =
     new TimerCallback(&notifiedThread, newMon);
-  ASSERT_TRUE(callback);
+  NS_ENSURE_TRUE(callback, NS_ERROR_OUT_OF_MEMORY);
 
   rv = timer->InitWithCallback(callback, 2000, nsITimer::TYPE_ONE_SHOT);
-  ASSERT_TRUE(NS_SUCCEEDED(rv));
+  NS_ENSURE_SUCCESS(rv, rv);
 
   ReentrantMonitorAutoEnter mon(*newMon);
   while (!notifiedThread) {
     mon.Wait();
   }
-  ASSERT_EQ(notifiedThread, testThread);
+  NS_ENSURE_TRUE(notifiedThread == testThread, NS_ERROR_FAILURE);
+
+  return NS_OK;
 }
 
-TEST(Timers, TimerWithStoppedTarget)
+nsresult
+TestTimerWithStoppedTarget()
 {
   AutoTestThread testThread;
-  ASSERT_TRUE(testThread);
+  NS_ENSURE_TRUE(testThread, NS_ERROR_OUT_OF_MEMORY);
 
   nsresult rv;
   nsCOMPtr<nsITimer> timer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv);
-  ASSERT_TRUE(NS_SUCCEEDED(rv));
+  NS_ENSURE_SUCCESS(rv, rv);
 
   nsIEventTarget* target = static_cast<nsIEventTarget*>(testThread);
 
   rv = timer->SetTarget(target);
-  ASSERT_TRUE(NS_SUCCEEDED(rv));
+  NS_ENSURE_SUCCESS(rv, rv);
 
   // If this is called, we'll assert
   nsCOMPtr<nsITimerCallback> callback =
     new TimerCallback(nullptr, nullptr);
-  ASSERT_TRUE(callback);
+  NS_ENSURE_TRUE(callback, NS_ERROR_OUT_OF_MEMORY);
 
   rv = timer->InitWithCallback(callback, 100, nsITimer::TYPE_ONE_SHOT);
-  ASSERT_TRUE(NS_SUCCEEDED(rv));
+  NS_ENSURE_SUCCESS(rv, rv);
 
   testThread->Shutdown();
 
   PR_Sleep(400);
+
+  return NS_OK;
 }
 
 #define FUZZ_MAX_TIMEOUT 9
 class FuzzTestThreadState final : public nsITimerCallback {
   public:
     NS_DECL_THREADSAFE_ISUPPORTS
 
     explicit FuzzTestThreadState(nsIThread* thread) :
@@ -403,17 +410,18 @@ class FuzzTestThreadState final : public
     std::list<nsCOMPtr<nsITimer>> mOneShotTimersByDelay[FUZZ_MAX_TIMEOUT + 1];
     std::vector<nsCOMPtr<nsITimer>> mRepeatingTimers;
     Atomic<bool> mStopped;
     Atomic<size_t> mTimersOutstanding;
 };
 
 NS_IMPL_ISUPPORTS(FuzzTestThreadState, nsITimerCallback)
 
-TEST(Timers, FuzzTestTimers)
+nsresult
+FuzzTestTimers()
 {
   static const size_t kNumThreads(10);
   AutoTestThread threads[kNumThreads];
   RefPtr<FuzzTestThreadState> threadStates[kNumThreads];
 
   for (size_t i = 0; i < kNumThreads; ++i) {
     threadStates[i] = new FuzzTestThreadState(&*threads[i]);
     threadStates[i]->Start();
@@ -425,13 +433,36 @@ TEST(Timers, FuzzTestTimers)
     threadStates[i]->Stop();
   }
 
   // Wait at most 10 seconds for all outstanding timers to pop
   PRIntervalTime start = PR_IntervalNow();
   for (auto& threadState : threadStates) {
     while (threadState->HasTimersOutstanding()) {
       uint32_t elapsedMs = PR_IntervalToMilliseconds(PR_IntervalNow() - start);
-      ASSERT_LE(elapsedMs, uint32_t(10000)) << "Timed out waiting for all timers to pop";
+      MOZ_RELEASE_ASSERT(elapsedMs <= 10000,
+                         "Timed out waiting for all timers to pop");
       PR_Sleep(PR_MillisecondsToInterval(10));
     }
   }
+
+  return NS_OK;
 }
+
+int main(int argc, char** argv)
+{
+  ScopedXPCOM xpcom("TestTimers");
+  NS_ENSURE_FALSE(xpcom.failed(), 1);
+
+  static TestFuncPtr testsToRun[] = {
+    TestTargetedTimers,
+    TestTimerWithStoppedTarget,
+    FuzzTestTimers
+  };
+  static uint32_t testCount = sizeof(testsToRun) / sizeof(testsToRun[0]);
+
+  for (uint32_t i = 0; i < testCount; i++) {
+    nsresult rv = testsToRun[i]();
+    NS_ENSURE_SUCCESS(rv, 1);
+  }
+
+  return 0;
+}
--- a/xpcom/tests/gtest/TestObserverService.cpp
+++ b/xpcom/tests/gtest/TestObserverService.cpp
@@ -6,17 +6,16 @@
 #include "nsISupports.h"
 #include "nsIComponentManager.h"
 #include "nsIObserverService.h"
 #include "nsIObserver.h"
 #include "nsISimpleEnumerator.h"
 #include "nsComponentManagerUtils.h"
 
 #include "nsCOMPtr.h"
-#include "nsString.h"
 #include "nsWeakReference.h"
 
 #include "mozilla/RefPtr.h"
 
 #include "gtest/gtest.h"
 
 static void testResult( nsresult rv ) {
   EXPECT_TRUE(NS_SUCCEEDED(rv)) << "0x" << std::hex << (int)rv;
--- a/xpcom/tests/gtest/moz.build
+++ b/xpcom/tests/gtest/moz.build
@@ -32,31 +32,22 @@ UNIFIED_SOURCES += [
     'TestStringStream.cpp',
     'TestSynchronization.cpp',
     'TestTArray.cpp',
     'TestTArray2.cpp',
     'TestTextFormatter.cpp',
     'TestThreadPool.cpp',
     'TestThreads.cpp',
     'TestThreadUtils.cpp',
-    'TestTimers.cpp',
     'TestTimeStamp.cpp',
     'TestTokenizer.cpp',
     'TestUTF.cpp',
     'TestXPIDLString.cpp',
 ]
 
-if CONFIG['MOZ_DEBUG'] and CONFIG['OS_ARCH'] not in ('WINNT') and CONFIG['OS_TARGET'] != 'Android':
-    # FIXME bug 523392: TestDeadlockDetector doesn't like Windows
-    # Bug 1054249: Doesn't work on Android
-    UNIFIED_SOURCES += [
-        'TestDeadlockDetector.cpp',
-        'TestDeadlockDetectorScalability.cpp',
-    ]
-
 # Compile TestAllocReplacement separately so Windows headers don't pollute
 # the global namespace for other files.
 SOURCES += [
     'TestAllocReplacement.cpp',
     'TestCOMPtr.cpp', # Redefines IFoo and IBar
     'TestHashtables.cpp', # Redefines IFoo
     'TestNsRefPtr.cpp', # Redefines Foo
     #'TestTArray2.cpp',
--- a/xpcom/tests/moz.build
+++ b/xpcom/tests/moz.build
@@ -40,26 +40,38 @@ if CONFIG['OS_TARGET'] == 'WINNT':
 
 if CONFIG['WRAP_STL_INCLUDES'] and not CONFIG['CLANG_CL']:
     GeckoSimplePrograms([
         'TestSTLWrappers',
     ])
 
 XPCSHELL_TESTS_MANIFESTS += ['unit/xpcshell.ini']
 
+GeckoCppUnitTests([
+    'TestTimers'
+])
+
 if CONFIG['MOZ_MEMORY']:
     GeckoCppUnitTests([
         'TestJemalloc',
     ])
 
 # FIXME: bug 577500 TestStaticAtoms fails when run in dist/bin
 #CPP_UNIT_TESTS += [
 #    'TestStaticAtoms',
 #]
 
+if CONFIG['MOZ_DEBUG'] and CONFIG['OS_ARCH'] not in ('WINNT'):
+    # FIXME bug 523392: TestDeadlockDetector doesn't like Windows
+    # FIXME bug 523378: also fails on OS X
+    GeckoCppUnitTests([
+        'TestDeadlockDetector',
+        'TestDeadlockDetectorScalability',
+    ])
+
 if CONFIG['COMPILE_ENVIRONMENT']:
     TEST_HARNESS_FILES.xpcshell.xpcom.tests.unit += [
         '!/dist/bin/components/xpcomtest.xpt',
     ]
 
 XPIDL_MODULE = 'xpcomtest'
 XPIDL_SOURCES += [
     'NotXPCOMTest.idl',