lock.ts 2.2 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. export namespace Lock {
  2. const locks = new Map<
  3. string,
  4. {
  5. readers: number
  6. writer: boolean
  7. waitingReaders: (() => void)[]
  8. waitingWriters: (() => void)[]
  9. }
  10. >()
  11. function get(key: string) {
  12. if (!locks.has(key)) {
  13. locks.set(key, {
  14. readers: 0,
  15. writer: false,
  16. waitingReaders: [],
  17. waitingWriters: [],
  18. })
  19. }
  20. return locks.get(key)!
  21. }
  22. function process(key: string) {
  23. const lock = locks.get(key)
  24. if (!lock || lock.writer || lock.readers > 0) return
  25. // Prioritize writers to prevent starvation
  26. if (lock.waitingWriters.length > 0) {
  27. const nextWriter = lock.waitingWriters.shift()!
  28. nextWriter()
  29. return
  30. }
  31. // Wake up all waiting readers
  32. while (lock.waitingReaders.length > 0) {
  33. const nextReader = lock.waitingReaders.shift()!
  34. nextReader()
  35. }
  36. // Clean up empty locks
  37. if (lock.readers === 0 && !lock.writer && lock.waitingReaders.length === 0 && lock.waitingWriters.length === 0) {
  38. locks.delete(key)
  39. }
  40. }
  41. export async function read(key: string): Promise<Disposable> {
  42. const lock = get(key)
  43. return new Promise((resolve) => {
  44. if (!lock.writer && lock.waitingWriters.length === 0) {
  45. lock.readers++
  46. resolve({
  47. [Symbol.dispose]: () => {
  48. lock.readers--
  49. process(key)
  50. },
  51. })
  52. } else {
  53. lock.waitingReaders.push(() => {
  54. lock.readers++
  55. resolve({
  56. [Symbol.dispose]: () => {
  57. lock.readers--
  58. process(key)
  59. },
  60. })
  61. })
  62. }
  63. })
  64. }
  65. export async function write(key: string): Promise<Disposable> {
  66. const lock = get(key)
  67. return new Promise((resolve) => {
  68. if (!lock.writer && lock.readers === 0) {
  69. lock.writer = true
  70. resolve({
  71. [Symbol.dispose]: () => {
  72. lock.writer = false
  73. process(key)
  74. },
  75. })
  76. } else {
  77. lock.waitingWriters.push(() => {
  78. lock.writer = true
  79. resolve({
  80. [Symbol.dispose]: () => {
  81. lock.writer = false
  82. process(key)
  83. },
  84. })
  85. })
  86. }
  87. })
  88. }
  89. }