Offload functions to worker threads with shared memory primitives for Node.js.
8
fork

Configure Feed

Select the types of activity you want to include in your feed.

at main 181 lines 4.7 kB view raw
1import { setTimeout } from 'node:timers/promises'; 2import { describe, it } from 'node:test'; 3import assert from 'node:assert/strict'; 4import { rwlock } from 'moroutine'; 5 6describe('RwLock', () => { 7 it('read lock and unlock', async () => { 8 const rw = rwlock(); 9 const guard = await rw.readLock(); 10 assert.equal(typeof guard[Symbol.dispose], 'function'); 11 rw.readUnlock(); 12 }); 13 14 it('write lock and unlock', async () => { 15 const rw = rwlock(); 16 const guard = await rw.writeLock(); 17 assert.equal(typeof guard[Symbol.dispose], 'function'); 18 rw.writeUnlock(); 19 }); 20 21 it('multiple readers can hold the lock concurrently', async () => { 22 const rw = rwlock(); 23 const g1 = await rw.readLock(); 24 const g2 = await rw.readLock(); 25 // Both acquired — success 26 rw.readUnlock(); 27 rw.readUnlock(); 28 }); 29 30 it('writer excludes readers', async () => { 31 const rw = rwlock(); 32 const order: string[] = []; 33 34 await rw.writeLock(); 35 order.push('write-start'); 36 37 const readerDone = (async () => { 38 const g = await rw.readLock(); 39 order.push('read'); 40 rw.readUnlock(); 41 })(); 42 43 await setTimeout(20); 44 order.push('write-end'); 45 rw.writeUnlock(); 46 47 await readerDone; 48 assert.deepEqual(order, ['write-start', 'write-end', 'read']); 49 }); 50 51 it('writer excludes other writers', async () => { 52 const rw = rwlock(); 53 const order: string[] = []; 54 55 await rw.writeLock(); 56 order.push('w1-start'); 57 58 const writer2Done = (async () => { 59 const g = await rw.writeLock(); 60 order.push('w2'); 61 rw.writeUnlock(); 62 })(); 63 64 await setTimeout(20); 65 order.push('w1-end'); 66 rw.writeUnlock(); 67 68 await writer2Done; 69 assert.deepEqual(order, ['w1-start', 'w1-end', 'w2']); 70 }); 71 72 it('ReadGuard dispose calls readUnlock', async () => { 73 const rw = rwlock(); 74 const guard = await rw.readLock(); 75 guard[Symbol.dispose](); 76 const wg = await rw.writeLock(); 77 rw.writeUnlock(); 78 }); 79 80 it('WriteGuard dispose calls writeUnlock', async () => { 81 const rw = rwlock(); 82 const guard = await rw.writeLock(); 83 guard[Symbol.dispose](); 84 const rg = await rw.readLock(); 85 rw.readUnlock(); 86 }); 87 88 it('self-allocates', () => { 89 const rw = rwlock(); 90 assert.ok(rw); 91 }); 92 93 it('exposes byteSize of 4', () => { 94 assert.equal(rwlock.byteSize, 4); 95 }); 96 97 it('wakes all waiting readers when writer unlocks', async () => { 98 const rw = rwlock(); 99 100 await rw.writeLock(); 101 102 const reader1Done = (async () => { 103 const g = await rw.readLock(); 104 rw.readUnlock(); 105 return 'r1'; 106 })(); 107 108 const reader2Done = (async () => { 109 const g = await rw.readLock(); 110 rw.readUnlock(); 111 return 'r2'; 112 })(); 113 114 // Let readers queue up 115 await setTimeout(20); 116 rw.writeUnlock(); 117 118 const results = await Promise.all([reader1Done, reader2Done]); 119 assert.deepEqual(results.sort(), ['r1', 'r2']); 120 }); 121 122 it('tryReadLock returns a guard when unlocked', async () => { 123 const rw = rwlock(); 124 const guard = rw.tryReadLock(); 125 assert.ok(guard); 126 assert.equal(typeof guard[Symbol.dispose], 'function'); 127 guard[Symbol.dispose](); 128 // writeLock only succeeds if readUnlock was called (reader count back to 0) 129 await rw.writeLock(); 130 rw.writeUnlock(); 131 }); 132 133 it('multiple concurrent tryReadLock calls all succeed', () => { 134 const rw = rwlock(); 135 const g1 = rw.tryReadLock(); 136 const g2 = rw.tryReadLock(); 137 const g3 = rw.tryReadLock(); 138 assert.ok(g1); 139 assert.ok(g2); 140 assert.ok(g3); 141 rw.readUnlock(); 142 rw.readUnlock(); 143 rw.readUnlock(); 144 }); 145 146 it('tryReadLock returns null when write-locked', async () => { 147 const rw = rwlock(); 148 await rw.writeLock(); 149 const guard = rw.tryReadLock(); 150 assert.equal(guard, null); 151 rw.writeUnlock(); 152 }); 153 154 it('tryWriteLock returns a guard when unlocked', () => { 155 const rw = rwlock(); 156 const guard = rw.tryWriteLock(); 157 assert.ok(guard); 158 assert.equal(typeof guard[Symbol.dispose], 'function'); 159 guard[Symbol.dispose](); 160 // Should be able to re-acquire after dispose (proves writeUnlock was called) 161 const guard2 = rw.tryWriteLock(); 162 assert.ok(guard2); 163 rw.writeUnlock(); 164 }); 165 166 it('tryWriteLock returns null when read-locked', async () => { 167 const rw = rwlock(); 168 await rw.readLock(); 169 const guard = rw.tryWriteLock(); 170 assert.equal(guard, null); 171 rw.readUnlock(); 172 }); 173 174 it('tryWriteLock returns null when write-locked', async () => { 175 const rw = rwlock(); 176 await rw.writeLock(); 177 const guard = rw.tryWriteLock(); 178 assert.equal(guard, null); 179 rw.writeUnlock(); 180 }); 181});