sxlock.h 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. /*
  2. * Copyright (c) 2022 Agustina Arzille.
  3. *
  4. * This program is free software: you can redistribute it and/or modify
  5. * it under the terms of the GNU General Public License as published by
  6. * the Free Software Foundation, either version 3 of the License, or
  7. * (at your option) any later version.
  8. *
  9. * This program is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License
  15. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  16. *
  17. * Shared-Exclusive locks.
  18. */
  19. #ifndef KERN_SXLOCK_H
  20. #define KERN_SXLOCK_H 1
  21. #include <stdint.h>
  22. #include <kern/atomic.h>
  23. #include <kern/list.h>
  24. #include <kern/spinlock.h>
  25. struct sxlock
  26. {
  27. uint32_t word;
  28. struct spinlock lock;
  29. struct list waiters;
  30. struct list readers;
  31. };
  32. #define SXLOCK_WAITERS_BIT 31
  33. #define SXLOCK_WAITERS (1u << SXLOCK_WAITERS_BIT)
  34. #define SXLOCK_MASK (SXLOCK_WAITERS - 1)
  35. /*
  36. * Possible values for the Shared-Exclusive word:
  37. *
  38. * 0 => Unlocked, can be taken by readers or writers.
  39. * INT32_MAX => Locked by a writer.
  40. * N where N in [1 .. INT32_MAX) => Locked by N readers.
  41. *
  42. * The presence of the SXLOCK_WAITERS bit indicates that there is
  43. * contention (Writers waiting on readers and viceversa). Setting
  44. * and clearing this bit must be done with the internal spinlock held.
  45. *
  46. */
  47. static inline void
  48. sxlock_init (struct sxlock *sxp)
  49. {
  50. sxp->word = 0;
  51. spinlock_init (&sxp->lock);
  52. list_init (&sxp->readers);
  53. list_init (&sxp->waiters);
  54. }
  55. static inline bool
  56. sxlock_exclusive (uint32_t value)
  57. {
  58. return ((value & SXLOCK_MASK) == SXLOCK_MASK);
  59. }
  60. static inline bool
  61. sxlock_phasing (uint32_t value)
  62. {
  63. return (value == SXLOCK_WAITERS);
  64. }
  65. static inline int
  66. sxlock_tryexlock (struct sxlock *sxp)
  67. {
  68. return (atomic_cas_bool_acq (&sxp->word, 0, SXLOCK_MASK) ? 0 : EBUSY);
  69. }
  70. void sxlock_exlock_slow (struct sxlock *sxp);
  71. static inline void
  72. sxlock_exlock (struct sxlock *sxp)
  73. {
  74. if (sxlock_tryexlock (sxp) != 0)
  75. sxlock_exlock_slow (sxp);
  76. }
  77. static inline int
  78. sxlock_tryshlock (struct sxlock *sxp)
  79. {
  80. uint32_t val = atomic_load_rlx (&sxp->word);
  81. return (!sxlock_exclusive (val) &&
  82. !sxlock_phasing (val) &&
  83. atomic_cas_bool_acq (&sxp->word, val, val + 1) ? 0 : EBUSY);
  84. }
  85. void sxlock_shlock_slow (struct sxlock *sxp);
  86. static inline void
  87. sxlock_shlock (struct sxlock *sxp)
  88. {
  89. if (sxlock_tryshlock (sxp) != 0)
  90. sxlock_shlock_slow (sxp);
  91. }
  92. void sxlock_wake_readers (struct sxlock *sxp);
  93. // Mutate an exclusive lock into a shared one.
  94. static inline void
  95. sxlock_share (struct sxlock *sxp)
  96. {
  97. uint32_t prev = atomic_and_rel (&sxp->word, SXLOCK_WAITERS | 1);
  98. if (prev & SXLOCK_WAITERS)
  99. {
  100. SPINLOCK_GUARD (&sxp->lock);
  101. sxlock_wake_readers (sxp);
  102. }
  103. }
  104. void sxlock_unlock_slow (struct sxlock *sxp);
  105. static inline void
  106. sxlock_unlock (struct sxlock *sxp)
  107. {
  108. uint32_t tmp = atomic_load_rlx (&sxp->word);
  109. tmp = sxlock_exclusive (tmp) ?
  110. (atomic_and_rel (&sxp->word, SXLOCK_WAITERS) & SXLOCK_WAITERS) :
  111. atomic_sub_rel (&sxp->word, 1) - 1;
  112. if (sxlock_phasing (tmp))
  113. sxlock_unlock_slow (sxp);
  114. }
  115. // Shared-Exclusive lock guards.
  116. static inline void
  117. sxlock_guard_fini (void *ptr)
  118. {
  119. sxlock_unlock (*(struct sxlock **)ptr);
  120. }
  121. #define SXLOCK_GUARD_IMPL(sxp, fn) \
  122. CLEANUP (sxlock_guard_fini) __unused _Auto UNIQ(sxg) = \
  123. ({ \
  124. struct sxlock *sxp_ = (sxp); \
  125. fn (sxp_); \
  126. sxp_; \
  127. })
  128. #define SXLOCK_SHGUARD(sxp) SXLOCK_GUARD_IMPL (sxp, sxlock_shlock)
  129. #define SXLOCK_EXGUARD(sxp) SXLOCK_GUARD_IMPL (sxp, sxlock_exlock)
  130. #endif