opentelemetry中spin_lock_mutex.h中的实现:

    1. // Copyright The OpenTelemetry Authors
    2. // SPDX-License-Identifier: Apache-2.0
    3. #pragma once
    4. #include <atomic>
    5. #include <chrono>
    6. #include <thread>
    7. #include "opentelemetry/version.h"
    8. #if defined(_MSC_VER)
    9. # ifndef NOMINMAX
    10. # define NOMINMAX
    11. # endif
    12. # define _WINSOCKAPI_ // stops including winsock.h
    13. # include <windows.h>
    14. #elif defined(__i386__) || defined(__x86_64__)
    15. # if defined(__clang__)
    16. # include <emmintrin.h>
    17. # endif
    18. #endif
    19. OPENTELEMETRY_BEGIN_NAMESPACE
    20. namespace common
    21. {
    22. constexpr int SPINLOCK_FAST_ITERATIONS = 100;
    23. constexpr int SPINLOCK_SLEEP_MS = 1;
    24. /**
    25. * A Mutex which uses atomic flags and spin-locks instead of halting threads.
    26. *
    27. * This mutex uses an incremental back-off strategy with the following phases:
    28. * 1. A tight spin-lock loop (pending: using hardware PAUSE/YIELD instructions)
    29. * 2. A loop where the current thread yields control after checking the lock.
    30. * 3. Issuing a thread-sleep call before starting back in phase 1.
    31. *
    32. * This is meant to give a good balance of perofrmance and CPU consumption in
    33. * practice.
    34. *
    35. * This mutex uses an incremental back-off strategy with the following phases:
    36. * 1. A tight spin-lock loop (pending: using hardware PAUSE/YIELD instructions)
    37. * 2. A loop where the current thread yields control after checking the lock.
    38. * 3. Issuing a thread-sleep call before starting back in phase 1.
    39. *
    40. * This is meant to give a good balance of perofrmance and CPU consumption in
    41. * practice.
    42. *
    43. * This class implements the `BasicLockable` specification:
    44. * https://en.cppreference.com/w/cpp/named_req/BasicLockable
    45. */
    46. class SpinLockMutex
    47. {
    48. public:
    49. SpinLockMutex() noexcept {}
    50. ~SpinLockMutex() noexcept = default;
    51. SpinLockMutex(const SpinLockMutex &) = delete;
    52. SpinLockMutex &operator=(const SpinLockMutex &) = delete;
    53. SpinLockMutex &operator=(const SpinLockMutex &) volatile = delete;
    54. /**
    55. * Attempts to lock the mutex. Return immediately with `true` (success) or `false` (failure).
    56. */
    57. bool try_lock() noexcept
    58. {
    59. return !flag_.load(std::memory_order_relaxed) &&
    60. !flag_.exchange(true, std::memory_order_acquire);
    61. }
    62. /**
    63. * Blocks until a lock can be obtained for the current thread.
    64. *
    65. * This mutex will spin the current CPU waiting for the lock to be available. This can have
    66. * decent performance in scenarios where there is low lock contention and lock-holders achieve
    67. * their work quickly. It degrades in scenarios where locked tasks take a long time.
    68. */
    69. void lock() noexcept
    70. {
    71. for (;;)
    72. {
    73. // Try once
    74. if (!flag_.exchange(true, std::memory_order_acquire))
    75. {
    76. return;
    77. }
    78. // Spin-Fast (goal ~10ns)
    79. for (std::size_t i = 0; i < SPINLOCK_FAST_ITERATIONS; ++i)
    80. {
    81. if (try_lock())
    82. {
    83. return;
    84. }
    85. // Issue a Pause/Yield instruction while spinning.
    86. #if defined(_MSC_VER)
    87. YieldProcessor();
    88. #elif defined(__i386__) || defined(__x86_64__)
    89. # if defined(__clang__)
    90. _mm_pause();
    91. # else
    92. __builtin_ia32_pause();
    93. # endif
    94. #elif defined(__arm__)
    95. // This intrinsic should fail to be found if YIELD is not supported on the current
    96. // processor.
    97. // __yield();
    98. asm volatile("yield");
    99. #else
    100. // TODO: Issue PAGE/YIELD on other architectures.
    101. #endif
    102. }
    103. // Yield then try again (goal ~100ns)
    104. std::this_thread::yield();
    105. if (try_lock())
    106. {
    107. return;
    108. }
    109. // Sleep and then start the whole process again. (goal ~1000ns)
    110. std::this_thread::sleep_for(std::chrono::milliseconds(SPINLOCK_SLEEP_MS));
    111. }
    112. return;
    113. }
    114. /** Releases the lock held by the execution agent. Throws no exceptions. */
    115. void unlock() noexcept { flag_.store(false, std::memory_order_release); }
    116. private:
    117. std::atomic<bool> flag_{false};
    118. };
    119. } // namespace common
    120. OPENTELEMETRY_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一样执行),因此不需要寄存器或内存占位器。