opentelemetry中spin_lock_mutex.h中的实现:
// Copyright The OpenTelemetry Authors// SPDX-License-Identifier: Apache-2.0#pragma once#include <atomic>#include <chrono>#include <thread>#include "opentelemetry/version.h"#if defined(_MSC_VER)# ifndef NOMINMAX# define NOMINMAX# endif# define _WINSOCKAPI_ // stops including winsock.h# include <windows.h>#elif defined(__i386__) || defined(__x86_64__)# if defined(__clang__)# include <emmintrin.h># endif#endifOPENTELEMETRY_BEGIN_NAMESPACEnamespace common{constexpr int SPINLOCK_FAST_ITERATIONS = 100;constexpr int SPINLOCK_SLEEP_MS = 1;/*** A Mutex which uses atomic flags and spin-locks instead of halting threads.** This mutex uses an incremental back-off strategy with the following phases:* 1. A tight spin-lock loop (pending: using hardware PAUSE/YIELD instructions)* 2. A loop where the current thread yields control after checking the lock.* 3. Issuing a thread-sleep call before starting back in phase 1.** This is meant to give a good balance of perofrmance and CPU consumption in* practice.** This mutex uses an incremental back-off strategy with the following phases:* 1. A tight spin-lock loop (pending: using hardware PAUSE/YIELD instructions)* 2. A loop where the current thread yields control after checking the lock.* 3. Issuing a thread-sleep call before starting back in phase 1.** This is meant to give a good balance of perofrmance and CPU consumption in* practice.** This class implements the `BasicLockable` specification:* https://en.cppreference.com/w/cpp/named_req/BasicLockable*/class SpinLockMutex{public:SpinLockMutex() noexcept {}~SpinLockMutex() noexcept = default;SpinLockMutex(const SpinLockMutex &) = delete;SpinLockMutex &operator=(const SpinLockMutex &) = delete;SpinLockMutex &operator=(const SpinLockMutex &) volatile = delete;/*** Attempts to lock the mutex. Return immediately with `true` (success) or `false` (failure).*/bool try_lock() noexcept{return !flag_.load(std::memory_order_relaxed) &&!flag_.exchange(true, std::memory_order_acquire);}/*** Blocks until a lock can be obtained for the current thread.** This mutex will spin the current CPU waiting for the lock to be available. This can have* decent performance in scenarios where there is low lock contention and lock-holders achieve* their work quickly. It degrades in scenarios where locked tasks take a long time.*/void lock() noexcept{for (;;){// Try onceif (!flag_.exchange(true, std::memory_order_acquire)){return;}// Spin-Fast (goal ~10ns)for (std::size_t i = 0; i < SPINLOCK_FAST_ITERATIONS; ++i){if (try_lock()){return;}// Issue a Pause/Yield instruction while spinning.#if defined(_MSC_VER)YieldProcessor();#elif defined(__i386__) || defined(__x86_64__)# if defined(__clang__)_mm_pause();# else__builtin_ia32_pause();# endif#elif defined(__arm__)// This intrinsic should fail to be found if YIELD is not supported on the current// processor.// __yield();asm volatile("yield");#else// TODO: Issue PAGE/YIELD on other architectures.#endif}// Yield then try again (goal ~100ns)std::this_thread::yield();if (try_lock()){return;}// Sleep and then start the whole process again. (goal ~1000ns)std::this_thread::sleep_for(std::chrono::milliseconds(SPINLOCK_SLEEP_MS));}return;}/** Releases the lock held by the execution agent. Throws no exceptions. */void unlock() noexcept { flag_.store(false, std::memory_order_release); }private:std::atomic<bool> flag_{false};};} // namespace commonOPENTELEMETRY_END_NAMESPACE
编译问题:使用ninja+Ubuntu+clang编译 android arm时找不到__yield。
找到腾讯云加社区解答:
__yield内部函数被指定为ARM C Language Extensions的一部分(参见8.4“提示”)。它发出yield instruction这大致相当于x86 pause。它正是为等待自旋锁这样的情况而设计的;它可以防止CPU过度冲击高速缓存线(这会损害性能),可能会节省一些电量,并且在使用超线程CPU的情况下,会使更多的计算单元可供其他逻辑处理器使用。
(请注意,它纯粹是一个CPU函数,而不是操作系统或库调用;它不会像类似名称的pause()、sched_yield()或std::this_thread::yield()调用那样为操作系统生成CPU时间片。)
尽管GCC支持一些ACLE内部函数,但它似乎缺少这个函数。您应该能够替换为asm volatile(“yield”);。yield指令没有体系结构效果(它像nop一样执行),因此不需要寄存器或内存占位器。
