AtomicEdgeAction.h 2.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687
  1. /*
  2. * Copyright (c) 2021, the SerenityOS developers.
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #pragma once
  7. #include <AK/Atomic.h>
  8. #include <Kernel/Arch/x86/Processor.h>
  9. namespace Kernel {
  10. template<typename AtomicRefCountType>
  11. class AtomicEdgeAction {
  12. public:
  13. template<typename FirstRefAction>
  14. bool ref(FirstRefAction first_ref_action)
  15. {
  16. AtomicRefCountType expected = 0;
  17. AtomicRefCountType desired = (1 << 1) | 1;
  18. // Least significant bit indicates we're busy protecting/unprotecting
  19. for (;;) {
  20. if (m_atomic_ref_count.compare_exchange_strong(expected, desired, AK::memory_order_relaxed))
  21. break;
  22. Processor::wait_check();
  23. expected &= ~1;
  24. desired = expected + (1 << 1);
  25. VERIFY(desired > expected);
  26. if (expected == 0)
  27. desired |= 1;
  28. }
  29. atomic_thread_fence(AK::memory_order_acquire);
  30. if (expected == 0) {
  31. first_ref_action();
  32. // drop the busy flag
  33. m_atomic_ref_count.store(desired & ~1, AK::memory_order_release);
  34. return true;
  35. }
  36. return false;
  37. }
  38. template<typename LastRefAction>
  39. bool unref(LastRefAction last_ref_action)
  40. {
  41. AtomicRefCountType expected = 1 << 1;
  42. AtomicRefCountType desired = (1 << 1) | 1;
  43. // Least significant bit indicates we're busy protecting/unprotecting
  44. for (;;) {
  45. if (m_atomic_ref_count.compare_exchange_strong(expected, desired, AK::memory_order_relaxed))
  46. break;
  47. Processor::wait_check();
  48. expected &= ~1;
  49. VERIFY(expected != 0); // Someone should always have at least one reference
  50. if (expected == 1 << 1) {
  51. desired = (1 << 1) | 1;
  52. } else {
  53. desired = expected - (1 << 1);
  54. VERIFY(desired < expected);
  55. }
  56. }
  57. AK::atomic_thread_fence(AK::memory_order_release);
  58. if (expected == 1 << 1) {
  59. last_ref_action();
  60. // drop the busy flag and release reference
  61. m_atomic_ref_count.store(0, AK::memory_order_release);
  62. return true;
  63. }
  64. return false;
  65. }
  66. private:
  67. Atomic<AtomicRefCountType> m_atomic_ref_count { 0 };
  68. };
  69. }