lock.test.ts 1.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172
  1. import { describe, expect, test } from "bun:test"
  2. import { Lock } from "../../src/util/lock"
  3. function tick() {
  4. return new Promise<void>((r) => queueMicrotask(r))
  5. }
  6. async function flush(n = 5) {
  7. for (let i = 0; i < n; i++) await tick()
  8. }
  9. describe("util.lock", () => {
  10. test("writer exclusivity: blocks reads and other writes while held", async () => {
  11. const key = "lock:" + Math.random().toString(36).slice(2)
  12. const state = {
  13. writer2: false,
  14. reader: false,
  15. writers: 0,
  16. }
  17. // Acquire writer1
  18. using writer1 = await Lock.write(key)
  19. state.writers++
  20. expect(state.writers).toBe(1)
  21. // Start writer2 candidate (should block)
  22. const writer2Task = (async () => {
  23. const w = await Lock.write(key)
  24. state.writers++
  25. expect(state.writers).toBe(1)
  26. state.writer2 = true
  27. // Hold for a tick so reader cannot slip in
  28. await tick()
  29. return w
  30. })()
  31. // Start reader candidate (should block)
  32. const readerTask = (async () => {
  33. const r = await Lock.read(key)
  34. state.reader = true
  35. return r
  36. })()
  37. // Flush microtasks and assert neither acquired
  38. await flush()
  39. expect(state.writer2).toBe(false)
  40. expect(state.reader).toBe(false)
  41. // Release writer1
  42. writer1[Symbol.dispose]()
  43. state.writers--
  44. // writer2 should acquire next
  45. const writer2 = await writer2Task
  46. expect(state.writer2).toBe(true)
  47. // Reader still blocked while writer2 held
  48. await flush()
  49. expect(state.reader).toBe(false)
  50. // Release writer2
  51. writer2[Symbol.dispose]()
  52. state.writers--
  53. // Reader should now acquire
  54. const reader = await readerTask
  55. expect(state.reader).toBe(true)
  56. reader[Symbol.dispose]()
  57. })
  58. })