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