1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "third_party/base/allocator/partition_allocator/spin_lock.h"
6 
7 #include "build/build_config.h"
8 #include "third_party/base/logging.h"
9 
10 #if defined(OS_WIN)
11 #include <windows.h>
12 #elif defined(OS_POSIX) || defined(OS_FUCHSIA)
13 #include <sched.h>
14 #endif
15 
16 // The YIELD_PROCESSOR macro wraps an architecture specific-instruction that
17 // informs the processor we're in a busy wait, so it can handle the branch more
18 // intelligently and e.g. reduce power to our core or give more resources to the
19 // other hyper-thread on this core. See the following for context:
20 // https://software.intel.com/en-us/articles/benefitting-power-and-performance-sleep-loops
21 //
22 // The YIELD_THREAD macro tells the OS to relinquish our quantum. This is
23 // basically a worst-case fallback, and if you're hitting it with any frequency
24 // you really should be using a proper lock (such as |base::Lock|)rather than
25 // these spinlocks.
26 #if defined(OS_WIN)
27 
28 #define YIELD_PROCESSOR YieldProcessor()
29 #define YIELD_THREAD SwitchToThread()
30 
31 #elif defined(OS_POSIX) || defined(OS_FUCHSIA)
32 
33 #if defined(ARCH_CPU_X86_64) || defined(ARCH_CPU_X86)
34 #define YIELD_PROCESSOR __asm__ __volatile__("pause")
35 #elif (defined(ARCH_CPU_ARMEL) && __ARM_ARCH >= 6) || defined(ARCH_CPU_ARM64)
36 #define YIELD_PROCESSOR __asm__ __volatile__("yield")
37 #elif defined(ARCH_CPU_MIPSEL)
38 // The MIPS32 docs state that the PAUSE instruction is a no-op on older
39 // architectures (first added in MIPS32r2). To avoid assembler errors when
40 // targeting pre-r2, we must encode the instruction manually.
41 #define YIELD_PROCESSOR __asm__ __volatile__(".word 0x00000140")
42 #elif defined(ARCH_CPU_MIPS64EL) && __mips_isa_rev >= 2
43 // Don't bother doing using .word here since r2 is the lowest supported mips64
44 // that Chromium supports.
45 #define YIELD_PROCESSOR __asm__ __volatile__("pause")
46 #elif defined(ARCH_CPU_PPC64_FAMILY)
47 #define YIELD_PROCESSOR __asm__ __volatile__("or 31,31,31")
48 #elif defined(ARCH_CPU_S390_FAMILY)
49 // just do nothing
50 #define YIELD_PROCESSOR ((void)0)
51 #endif  // ARCH
52 
53 #ifndef YIELD_PROCESSOR
54 #warning "Processor yield not supported on this architecture."
55 #define YIELD_PROCESSOR ((void)0)
56 #endif
57 
58 #define YIELD_THREAD sched_yield()
59 
60 #else  // Other OS
61 
62 #warning "Thread yield not supported on this OS."
63 #define YIELD_THREAD ((void)0)
64 
65 #endif  // OS_WIN
66 
67 namespace pdfium {
68 namespace base {
69 namespace subtle {
70 
LockSlow()71 void SpinLock::LockSlow() {
72   // The value of |kYieldProcessorTries| is cargo culted from TCMalloc, Windows
73   // critical section defaults, and various other recommendations.
74   // TODO(jschuh): Further tuning may be warranted.
75   static const int kYieldProcessorTries = 1000;
76   // The value of |kYieldThreadTries| is completely made up.
77   static const int kYieldThreadTries = 10;
78   int yield_thread_count = 0;
79   do {
80     do {
81       for (int count = 0; count < kYieldProcessorTries; ++count) {
82         // Let the processor know we're spinning.
83         YIELD_PROCESSOR;
84         if (!lock_.load(std::memory_order_relaxed) &&
85             LIKELY(!lock_.exchange(true, std::memory_order_acquire)))
86           return;
87       }
88 
89       if (yield_thread_count < kYieldThreadTries) {
90         ++yield_thread_count;
91         // Give the OS a chance to schedule something on this core.
92         YIELD_THREAD;
93       } else {
94         // At this point, it's likely that the lock is held by a lower priority
95         // thread that is unavailable to finish its work because of higher
96         // priority threads spinning here. Sleeping should ensure that they make
97         // progress.
98         NOTREACHED();
99       }
100     } while (lock_.load(std::memory_order_relaxed));
101   } while (UNLIKELY(lock_.exchange(true, std::memory_order_acquire)));
102 }
103 
104 }  // namespace subtle
105 }  // namespace base
106 }  // namespace pdfium
107