1e6f65cf5e
* Memory leak in makeMutex - Promise never gets garbage collected Hey, I've been debugging a memory leak in my application and traced it back to the makeMutex implementation. The current implementation chains promises indefinitely without ever breaking the chain: Every call to mutex() creates a new Promise that awaits the previous task, then becomes the new task. The problem is the old promises never get released because each one holds a reference to the previous through the closure. What I found Took a heap snapshot after running for a while and found hundreds of Promises from make-mutex.js holding ~15MB and growing. The retainer graph shows a long chain of Promises all pointing back to each other. Since processingMutex handles every incoming message/notification, this chain grows constantly and never shrinks. This keeps the same mutex behavior but lets the GC clean up old promises every 50 tasks instead of holding them forever. * Refactor makeMutex to use AsyncMutex directly * lint * revert
43 lines
927 B
TypeScript
43 lines
927 B
TypeScript
import { Mutex as AsyncMutex } from 'async-mutex'
|
|
|
|
export const makeMutex = () => {
|
|
const mutex = new AsyncMutex()
|
|
|
|
return {
|
|
mutex<T>(code: () => Promise<T> | T): Promise<T> {
|
|
return mutex.runExclusive(code)
|
|
}
|
|
}
|
|
}
|
|
|
|
export type Mutex = ReturnType<typeof makeMutex>
|
|
|
|
export const makeKeyedMutex = () => {
|
|
const map = new Map<string, { mutex: AsyncMutex; refCount: number }>()
|
|
|
|
return {
|
|
async mutex<T>(key: string, task: () => Promise<T> | T): Promise<T> {
|
|
let entry = map.get(key)
|
|
|
|
if (!entry) {
|
|
entry = { mutex: new AsyncMutex(), refCount: 0 }
|
|
map.set(key, entry)
|
|
}
|
|
|
|
entry.refCount++
|
|
|
|
try {
|
|
return await entry.mutex.runExclusive(task)
|
|
} finally {
|
|
entry.refCount--
|
|
// only delete it if this is still the current entry
|
|
if (entry.refCount === 0 && map.get(key) === entry) {
|
|
map.delete(key)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
export type KeyedMutex = ReturnType<typeof makeKeyedMutex>
|