syncs_test.go 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293
  1. // Copyright (c) Tailscale Inc & AUTHORS
  2. // SPDX-License-Identifier: BSD-3-Clause
  3. package syncs
  4. import (
  5. "context"
  6. "io"
  7. "os"
  8. "sync"
  9. "testing"
  10. "time"
  11. "github.com/google/go-cmp/cmp"
  12. )
  13. func TestAtomicValue(t *testing.T) {
  14. {
  15. // Always wrapping should not allocate for simple values
  16. // because wrappedValue[T] has the same memory layout as T.
  17. var v AtomicValue[bool]
  18. bools := []bool{true, false}
  19. if n := int(testing.AllocsPerRun(1000, func() {
  20. for _, b := range bools {
  21. v.Store(b)
  22. }
  23. })); n != 0 {
  24. t.Errorf("AllocsPerRun = %d, want 0", n)
  25. }
  26. }
  27. {
  28. var v AtomicValue[int]
  29. got, gotOk := v.LoadOk()
  30. if got != 0 || gotOk {
  31. t.Fatalf("LoadOk = (%v, %v), want (0, false)", got, gotOk)
  32. }
  33. v.Store(1)
  34. got, gotOk = v.LoadOk()
  35. if got != 1 || !gotOk {
  36. t.Fatalf("LoadOk = (%v, %v), want (1, true)", got, gotOk)
  37. }
  38. }
  39. {
  40. var v AtomicValue[error]
  41. got, gotOk := v.LoadOk()
  42. if got != nil || gotOk {
  43. t.Fatalf("LoadOk = (%v, %v), want (nil, false)", got, gotOk)
  44. }
  45. v.Store(io.EOF)
  46. got, gotOk = v.LoadOk()
  47. if got != io.EOF || !gotOk {
  48. t.Fatalf("LoadOk = (%v, %v), want (EOF, true)", got, gotOk)
  49. }
  50. err := &os.PathError{}
  51. v.Store(err)
  52. got, gotOk = v.LoadOk()
  53. if got != err || !gotOk {
  54. t.Fatalf("LoadOk = (%v, %v), want (%v, true)", got, gotOk, err)
  55. }
  56. v.Store(nil)
  57. got, gotOk = v.LoadOk()
  58. if got != nil || !gotOk {
  59. t.Fatalf("LoadOk = (%v, %v), want (nil, true)", got, gotOk)
  60. }
  61. }
  62. {
  63. c1, c2, c3 := make(chan struct{}), make(chan struct{}), make(chan struct{})
  64. var v AtomicValue[chan struct{}]
  65. if v.CompareAndSwap(c1, c2) != false {
  66. t.Fatalf("CompareAndSwap = true, want false")
  67. }
  68. if v.CompareAndSwap(nil, c1) != true {
  69. t.Fatalf("CompareAndSwap = false, want true")
  70. }
  71. if v.CompareAndSwap(c2, c3) != false {
  72. t.Fatalf("CompareAndSwap = true, want false")
  73. }
  74. if v.CompareAndSwap(c1, c2) != true {
  75. t.Fatalf("CompareAndSwap = false, want true")
  76. }
  77. }
  78. }
  79. func TestMutexValue(t *testing.T) {
  80. var v MutexValue[time.Time]
  81. if n := int(testing.AllocsPerRun(1000, func() {
  82. v.Store(v.Load())
  83. v.WithLock(func(*time.Time) {})
  84. })); n != 0 {
  85. t.Errorf("AllocsPerRun = %d, want 0", n)
  86. }
  87. now := time.Now()
  88. v.Store(now)
  89. if !v.Load().Equal(now) {
  90. t.Errorf("Load = %v, want %v", v.Load(), now)
  91. }
  92. var group sync.WaitGroup
  93. var v2 MutexValue[int]
  94. var sum int
  95. for i := range 10 {
  96. group.Go(func() {
  97. old1 := v2.Load()
  98. old2 := v2.Swap(old1 + i)
  99. delta := old2 - old1
  100. v2.WithLock(func(p *int) { *p += delta })
  101. })
  102. sum += i
  103. }
  104. group.Wait()
  105. if v2.Load() != sum {
  106. t.Errorf("Load = %v, want %v", v2.Load(), sum)
  107. }
  108. }
  109. func TestWaitGroupChan(t *testing.T) {
  110. wg := NewWaitGroupChan()
  111. wantNotDone := func() {
  112. t.Helper()
  113. select {
  114. case <-wg.DoneChan():
  115. t.Fatal("done too early")
  116. default:
  117. }
  118. }
  119. wantDone := func() {
  120. t.Helper()
  121. select {
  122. case <-wg.DoneChan():
  123. default:
  124. t.Fatal("expected to be done")
  125. }
  126. }
  127. wg.Add(2)
  128. wantNotDone()
  129. wg.Decr()
  130. wantNotDone()
  131. wg.Decr()
  132. wantDone()
  133. wantDone()
  134. }
  135. func TestClosedChan(t *testing.T) {
  136. ch := ClosedChan()
  137. for range 2 {
  138. select {
  139. case <-ch:
  140. default:
  141. t.Fatal("not closed")
  142. }
  143. }
  144. }
  145. func TestSemaphore(t *testing.T) {
  146. s := NewSemaphore(2)
  147. assertLen := func(want int) {
  148. t.Helper()
  149. if got := s.Len(); got != want {
  150. t.Fatalf("Len = %d, want %d", got, want)
  151. }
  152. }
  153. assertLen(0)
  154. s.Acquire()
  155. assertLen(1)
  156. if !s.TryAcquire() {
  157. t.Fatal("want true")
  158. }
  159. assertLen(2)
  160. if s.TryAcquire() {
  161. t.Fatal("want false")
  162. }
  163. ctx, cancel := context.WithCancel(context.Background())
  164. cancel()
  165. if s.AcquireContext(ctx) {
  166. t.Fatal("want false")
  167. }
  168. s.Release()
  169. assertLen(1)
  170. if !s.AcquireContext(context.Background()) {
  171. t.Fatal("want true")
  172. }
  173. assertLen(2)
  174. s.Release()
  175. assertLen(1)
  176. s.Release()
  177. assertLen(0)
  178. }
  179. func TestMap(t *testing.T) {
  180. var m Map[string, int]
  181. if v, ok := m.Load("noexist"); v != 0 || ok {
  182. t.Errorf(`Load("noexist") = (%v, %v), want (0, false)`, v, ok)
  183. }
  184. m.LoadFunc("noexist", func(v int, ok bool) {
  185. if v != 0 || ok {
  186. t.Errorf(`LoadFunc("noexist") = (%v, %v), want (0, false)`, v, ok)
  187. }
  188. })
  189. m.Store("one", 1)
  190. if v, ok := m.LoadOrStore("one", -1); v != 1 || !ok {
  191. t.Errorf(`LoadOrStore("one", 1) = (%v, %v), want (1, true)`, v, ok)
  192. }
  193. if v, ok := m.Load("one"); v != 1 || !ok {
  194. t.Errorf(`Load("one") = (%v, %v), want (1, true)`, v, ok)
  195. }
  196. m.LoadFunc("one", func(v int, ok bool) {
  197. if v != 1 || !ok {
  198. t.Errorf(`LoadFunc("one") = (%v, %v), want (1, true)`, v, ok)
  199. }
  200. })
  201. if v, ok := m.LoadOrStore("two", 2); v != 2 || ok {
  202. t.Errorf(`LoadOrStore("two", 2) = (%v, %v), want (2, false)`, v, ok)
  203. }
  204. if v, ok := m.LoadOrInit("three", func() int { return 3 }); v != 3 || ok {
  205. t.Errorf(`LoadOrInit("three", 3) = (%v, %v), want (3, true)`, v, ok)
  206. }
  207. got := map[string]int{}
  208. want := map[string]int{"one": 1, "two": 2, "three": 3}
  209. for k, v := range m.All() {
  210. got[k] = v
  211. }
  212. if d := cmp.Diff(got, want); d != "" {
  213. t.Errorf("Range mismatch (-got +want):\n%s", d)
  214. }
  215. if v, ok := m.LoadAndDelete("two"); v != 2 || !ok {
  216. t.Errorf(`LoadAndDelete("two) = (%v, %v), want (2, true)`, v, ok)
  217. }
  218. if v, ok := m.LoadAndDelete("two"); v != 0 || ok {
  219. t.Errorf(`LoadAndDelete("two) = (%v, %v), want (0, false)`, v, ok)
  220. }
  221. m.Delete("three")
  222. m.Delete("one")
  223. m.Delete("noexist")
  224. got = map[string]int{}
  225. want = map[string]int{}
  226. for k, v := range m.All() {
  227. got[k] = v
  228. }
  229. if d := cmp.Diff(got, want); d != "" {
  230. t.Errorf("Range mismatch (-got +want):\n%s", d)
  231. }
  232. t.Run("LoadOrStore", func(t *testing.T) {
  233. var m Map[string, string]
  234. var wg sync.WaitGroup
  235. var ok1, ok2 bool
  236. wg.Go(func() { _, ok1 = m.LoadOrStore("", "") })
  237. wg.Go(func() { _, ok2 = m.LoadOrStore("", "") })
  238. wg.Wait()
  239. if ok1 == ok2 {
  240. t.Errorf("exactly one LoadOrStore should load")
  241. }
  242. })
  243. t.Run("Clear", func(t *testing.T) {
  244. var m Map[string, string]
  245. _, _ = m.LoadOrStore("a", "1")
  246. _, _ = m.LoadOrStore("b", "2")
  247. _, _ = m.LoadOrStore("c", "3")
  248. _, _ = m.LoadOrStore("d", "4")
  249. _, _ = m.LoadOrStore("e", "5")
  250. if m.Len() != 5 {
  251. t.Errorf("Len after loading want=5 got=%d", m.Len())
  252. }
  253. m.Clear()
  254. if m.Len() != 0 {
  255. t.Errorf("Len after Clear want=0 got=%d", m.Len())
  256. }
  257. })
  258. t.Run("Swap", func(t *testing.T) {
  259. var m Map[string, string]
  260. m.Store("hello", "world")
  261. if got, want := m.Swap("hello", "world2"), "world"; got != want {
  262. t.Errorf("got old value %q, want %q", got, want)
  263. }
  264. if got := m.Swap("empty", "foo"); got != "" {
  265. t.Errorf("got old value %q, want empty string", got)
  266. }
  267. })
  268. }