Skip to content

Commit

Permalink
Move WTF::SpinLock to base::SpinLock.
Browse files Browse the repository at this point in the history
This CL depends on https://codereview.chromium.org/2473153003 ("Move some
compiler intrinsic #defines to base/.").

This CL is a re-land of https://codereview.chromium.org/2484803003.

BUG=632441
TBR=esprehn

Review-Url: https://codereview.chromium.org/2519183002
Cr-Commit-Position: refs/heads/master@{#434297}
  • Loading branch information
palmer authored and Commit bot committed Nov 24, 2016
1 parent 0f1facb commit 74db88d
Show file tree
Hide file tree
Showing 6 changed files with 95 additions and 89 deletions.
10 changes: 10 additions & 0 deletions base/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

import("//build/buildflag_header.gni")
import("//build/config/allocator.gni")
import("//build/config/arm.gni")
import("//build/config/chromecast_build.gni")
import("//build/config/clang/clang.gni")
import("//build/config/compiler/compiler.gni")
Expand Down Expand Up @@ -1215,6 +1216,15 @@ component("base") {
]
}

# SpinLock uses inline assembly that doesn't work on NaCl, and for which there
# is no code for ARMv6.
if (!is_nacl && (current_cpu != "arm" || arm_version >= 7)) {
sources += [
"synchronization/spin_lock.cc",
"synchronization/spin_lock.h",
]
}

# Windows.
if (is_win) {
sources += [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,11 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "wtf/SpinLock.h"
#include "base/synchronization/spin_lock.h"

#include "wtf/Atomics.h"
#include "wtf/CPU.h"
#include "wtf/Compiler.h"

#if OS(WIN)
#if defined(OS_WIN)
#include <windows.h>
#elif OS(POSIX)
#elif defined(OS_POSIX)
#include <sched.h>
#endif

Expand All @@ -20,23 +16,24 @@
// other hyper-thread on this core. See the following for context:
// https://software.intel.com/en-us/articles/benefitting-power-and-performance-sleep-loops
//
// The YIELD_THREAD macro tells the OS to relinquish our quanta. This is
// The YIELD_THREAD macro tells the OS to relinquish our quantum. This is
// basically a worst-case fallback, and if you're hitting it with any frequency
// you really should be using proper lock rather than these spinlocks.
#if OS(WIN)
// you really should be using a proper lock (such as |base::Lock|)rather than
// these spinlocks.
#if defined(OS_WIN)
#define YIELD_PROCESSOR YieldProcessor()
#define YIELD_THREAD SwitchToThread()
#elif COMPILER(GCC) || COMPILER(CLANG)
#if CPU(X86_64) || CPU(X86)
#elif defined(COMPILER_GCC) || defined(__clang__)
#if defined(ARCH_CPU_X86_64) || defined(ARCH_CPU_X86)
#define YIELD_PROCESSOR __asm__ __volatile__("pause")
#elif CPU(ARM) || CPU(ARM64)
#elif defined(ARCH_CPU_ARMEL) || defined(ARCH_CPU_ARM64)
#define YIELD_PROCESSOR __asm__ __volatile__("yield")
#elif CPU(MIPS)
#elif defined(ARCH_CPU_MIPSEL)
// The MIPS32 docs state that the PAUSE instruction is a no-op on older
// architectures (first added in MIPS32r2). To avoid assembler errors when
// targeting pre-r2, we must encode the instruction manually.
#define YIELD_PROCESSOR __asm__ __volatile__(".word 0x00000140")
#elif CPU(MIPS64) && __mips_isa_rev >= 2
#elif defined(ARCH_CPU_MIPS64EL) && __mips_isa_rev >= 2
// Don't bother doing using .word here since r2 is the lowest supported mips64
// that Chromium supports.
#define YIELD_PROCESSOR __asm__ __volatile__("pause")
Expand All @@ -49,35 +46,37 @@
#endif

#ifndef YIELD_THREAD
#if OS(POSIX)
#if defined(OS_POSIX)
#define YIELD_THREAD sched_yield()
#else
#warning "Thread yield not supported on this OS."
#define YIELD_THREAD ((void)0)
#endif
#endif

namespace WTF {
namespace base {
namespace subtle {

void SpinLock::lockSlow() {
// The value of kYieldProcessorTries is cargo culted from TCMalloc, Windows
void SpinLock::LockSlow() {
// The value of |kYieldProcessorTries| is cargo culted from TCMalloc, Windows
// critical section defaults, and various other recommendations.
// TODO(jschuh): Further tuning may be warranted.
static const int kYieldProcessorTries = 1000;
do {
do {
for (int count = 0; count < kYieldProcessorTries; ++count) {
// Let the Processor know we're spinning.
// Let the processor know we're spinning.
YIELD_PROCESSOR;
if (!m_lock.load(std::memory_order_relaxed) &&
LIKELY(!m_lock.exchange(true, std::memory_order_acquire)))
if (!lock_.load(std::memory_order_relaxed) &&
LIKELY(!lock_.exchange(true, std::memory_order_acquire)))
return;
}

// Give the OS a chance to schedule something on this core.
YIELD_THREAD;
} while (m_lock.load(std::memory_order_relaxed));
} while (UNLIKELY(m_lock.exchange(true, std::memory_order_acquire)));
} while (lock_.load(std::memory_order_relaxed));
} while (UNLIKELY(lock_.exchange(true, std::memory_order_acquire)));
}

} // namespace WTF
} // namespace subtle
} // namespace base
52 changes: 52 additions & 0 deletions base/synchronization/spin_lock.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef BASE_SYNCHRONIZATION_SPIN_LOCK_H
#define BASE_SYNCHRONIZATION_SPIN_LOCK_H

#include <atomic>
#include <memory>
#include <mutex>

#include "base/base_export.h"
#include "base/compiler_specific.h"

// Spinlock is a simple spinlock class based on the standard CPU primitive of
// atomic increment and decrement of an int at a given memory address. These are
// intended only for very short duration locks and assume a system with multiple
// cores. For any potentially longer wait you should use a real lock, such as
// |base::Lock|.
//
// |SpinLock|s MUST be globals. Using them as (e.g.) struct/class members will
// result in an uninitialized lock, which is dangerously incorrect.

namespace base {
namespace subtle {

class SpinLock {
public:
using Guard = std::lock_guard<SpinLock>;

ALWAYS_INLINE void lock() {
static_assert(sizeof(lock_) == sizeof(int),
"int and lock_ are different sizes");
if (LIKELY(!lock_.exchange(true, std::memory_order_acquire)))
return;
LockSlow();
}

ALWAYS_INLINE void unlock() { lock_.store(false, std::memory_order_release); }

private:
// This is called if the initial attempt to acquire the lock fails. It's
// slower, but has a much better scheduling and power consumption behavior.
BASE_EXPORT void LockSlow();

std::atomic_int lock_;
};

} // namespace subtle
} // namespace base

#endif // BASE_SYNCHRONIZATION_SPIN_LOCK_H
1 change: 0 additions & 1 deletion third_party/WebKit/Source/wtf/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,6 @@ component("wtf") {
"SaturatedArithmetic.h",
"SizeAssertions.h",
"SizeLimits.cpp",
"SpinLock.cpp",
"SpinLock.h",
"StaticConstructors.h",
"StdLibExtras.h",
Expand Down
1 change: 1 addition & 0 deletions third_party/WebKit/Source/wtf/DEPS
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ include_rules = [
"+base/optional.h",
"+base/rand_util.h",
"+base/strings",
"+base/synchronization/spin_lock.h",
"+base/threading/thread_checker.h",
"+base/time/time.h",
"+base/tuple.h",
Expand Down
71 changes: 8 additions & 63 deletions third_party/WebKit/Source/wtf/SpinLock.h
Original file line number Diff line number Diff line change
@@ -1,73 +1,18 @@
/*
* Copyright (C) 2013 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef WTF_SpinLock_h
#define WTF_SpinLock_h

#include "wtf/Compiler.h"
#include "wtf/WTFExport.h"
#include <atomic>
#include <memory>
#include <mutex>

// DESCRIPTION
// Spinlock is a simple spinlock class based on the standard CPU primitive of
// atomic increment and decrement of an int at a given memory address. These are
// intended only for very short duration locks and assume a system with multiple
// cores. For any potentially longer wait you should be using a real lock.
#include "base/macros.h"
#include "base/synchronization/spin_lock.h"

namespace WTF {

class SpinLock {
public:
using Guard = std::lock_guard<SpinLock>;

ALWAYS_INLINE void lock() {
static_assert(sizeof(m_lock) == sizeof(int),
"int and m_lock are different sizes");
if (LIKELY(!m_lock.exchange(true, std::memory_order_acquire)))
return;
lockSlow();
}

ALWAYS_INLINE void unlock() {
m_lock.store(false, std::memory_order_release);
}

private:
// This is called if the initial attempt to acquire the lock fails. It's
// slower, but has a much better scheduling and power consumption behavior.
WTF_EXPORT void lockSlow();

std::atomic_int m_lock;
};
// WTF::SpinLock is base::SpinLock. See base/synchronization/spin_lock.h for
// documentation.
using SpinLock = base::subtle::SpinLock;

} // namespace WTF

Expand Down

0 comments on commit 74db88d

Please sign in to comment.