diff --git a/packages/daemon/src/daemon-node-powers.js b/packages/daemon/src/daemon-node-powers.js index 683da6a79b..ee1b4acdf4 100644 --- a/packages/daemon/src/daemon-node-powers.js +++ b/packages/daemon/src/daemon-node-powers.js @@ -9,6 +9,7 @@ import { makeReaderRef } from './reader-ref.js'; import { makePetStoreMaker } from './pet-store.js'; import { servePrivatePortHttp } from './serve-private-port-http.js'; import { servePrivatePath } from './serve-private-path.js'; +import { makeMutex } from './mutex.js'; const { quote: q } = assert; @@ -354,6 +355,8 @@ export const makeNetworkPowers = ({ http, ws, net }) => { }; export const makeFilePowers = ({ fs, path: fspath }) => { + const writeLock = makeMutex(); + /** * @param {string} path */ @@ -376,7 +379,9 @@ export const makeFilePowers = ({ fs, path: fspath }) => { * @param {string} text */ const writeFileText = async (path, text) => { - await fs.promises.writeFile(path, text); + await writeLock.enqueue(async () => { + await fs.promises.writeFile(path, text); + }); }; /** @@ -418,11 +423,16 @@ export const makeFilePowers = ({ fs, path: fspath }) => { * @param {string} path */ const removePath = async path => { - return fs.promises.rm(path); + await writeLock.enqueue(async () => { + return fs.promises.rm(path); + }); }; - const renamePath = async (source, target) => - fs.promises.rename(source, target); + const renamePath = async (source, target) => { + await writeLock.enqueue(async () => { + return fs.promises.rename(source, target); + }); + }; const joinPath = (...components) => fspath.join(...components); diff --git a/packages/daemon/src/mutex.js b/packages/daemon/src/mutex.js new file mode 100644 index 0000000000..f222fc54f5 --- /dev/null +++ b/packages/daemon/src/mutex.js @@ -0,0 +1,30 @@ +import { makeQueue } from '@endo/stream'; + +/** + * @returns {{ lock: () => Promise, unlock: () => void, enqueue: (asyncFn: () => Promise) => Promise }} + */ +export const makeMutex = () => { + /** @type {import('@endo/stream').AsyncQueue} */ + const queue = makeQueue(); + const lock = () => { + return queue.get(); + }; + const unlock = () => { + queue.put(); + }; + unlock(); + + return { + lock, + unlock, + // helper for correct usage + enqueue: async asyncFn => { + await lock(); + try { + return await asyncFn(); + } finally { + unlock(); + } + }, + }; +};