Skip to content

Commit

Permalink
fix(daemon): node makeFilePowers uses mutex for serial file updates
Browse files Browse the repository at this point in the history
  • Loading branch information
kumavis committed Feb 17, 2024
1 parent 0c52db1 commit ba1b33f
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 4 deletions.
18 changes: 14 additions & 4 deletions packages/daemon/src/daemon-node-powers.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -354,6 +355,8 @@ export const makeNetworkPowers = ({ http, ws, net }) => {
};

export const makeFilePowers = ({ fs, path: fspath }) => {
const writeLock = makeMutex();

/**
* @param {string} path
*/
Expand All @@ -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);
});
};

/**
Expand Down Expand Up @@ -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);

Expand Down
30 changes: 30 additions & 0 deletions packages/daemon/src/mutex.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { makeQueue } from '@endo/stream';

/**
* @returns {{ lock: () => Promise<void>, unlock: () => void, enqueue: (asyncFn: () => Promise<any>) => Promise<any> }}
*/
export const makeMutex = () => {
/** @type {import('@endo/stream').AsyncQueue<void>} */
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();
}
},
};
};

0 comments on commit ba1b33f

Please sign in to comment.