[M65] Add sleep fallback to base::SpinLock.

This avoids a situation where a thread of lesser priority is holding the
lock while a bunch of higher priority threads hog all CPU spinning,
preventing the lesser priority thread from releasing the lock.

Bug: 758695, 770312
Change-Id: Ibf36f16023069e32f3f55716a2b63e47fff0d9f7
Reviewed-on: https://chromium-review.googlesource.com/873647
Commit-Queue: Max Morin <maxmorin@chromium.org>
Reviewed-by: Kentaro Hara <haraken@chromium.org>
Reviewed-by: Chris Palmer <palmer@chromium.org>
Cr-Original-Commit-Position: refs/heads/master@{#530835}(cherry picked from commit da57669f62bed877ec39dd5847470592109cb405)
Reviewed-on: https://chromium-review.googlesource.com/928481
Reviewed-by: Max Morin <maxmorin@chromium.org>
Cr-Commit-Position: refs/branch-heads/3325@{#529}
Cr-Branched-From: bc084a8b5afa3744a74927344e304c02ae54189f-refs/heads/master@{#530369}
diff --git a/base/allocator/partition_allocator/spin_lock.cc b/base/allocator/partition_allocator/spin_lock.cc
index c30d6cd..fd062c3 100644
--- a/base/allocator/partition_allocator/spin_lock.cc
+++ b/base/allocator/partition_allocator/spin_lock.cc
@@ -10,6 +10,8 @@
 #include <sched.h>
 #endif
 
+#include "base/threading/platform_thread.h"
+
 // The YIELD_PROCESSOR macro wraps an architecture specific-instruction that
 // informs the processor we're in a busy wait, so it can handle the branch more
 // intelligently and e.g. reduce power to our core or give more resources to the
@@ -67,6 +69,9 @@
   // critical section defaults, and various other recommendations.
   // TODO(jschuh): Further tuning may be warranted.
   static const int kYieldProcessorTries = 1000;
+  // The value of |kYieldThreadTries| is completely made up.
+  static const int kYieldThreadTries = 10;
+  int yield_thread_count = 0;
   do {
     do {
       for (int count = 0; count < kYieldProcessorTries; ++count) {
@@ -77,8 +82,17 @@
           return;
       }
 
-      // Give the OS a chance to schedule something on this core.
-      YIELD_THREAD;
+      if (yield_thread_count < kYieldThreadTries) {
+        ++yield_thread_count;
+        // Give the OS a chance to schedule something on this core.
+        YIELD_THREAD;
+      } else {
+        // At this point, it's likely that the lock is held by a lower priority
+        // thread that is unavailable to finish its work because of higher
+        // priority threads spinning here. Sleeping should ensure that they make
+        // progress.
+        PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(1));
+      }
     } while (lock_.load(std::memory_order_relaxed));
   } while (UNLIKELY(lock_.exchange(true, std::memory_order_acquire)));
 }