Data Race Safe way of Modifying Files

Hey everyone, I’m trying to find a way to avoid a data race. I’m trying to track renames in other files similar to how backlinks are tracked. It works by itself, but runs into a race issue when there are also back links being updated. It seems like the backlink modification is always overwriting the updates I’m making, which I’m guessing is because it’s waiting on more i/o to complete after reading.

As far as I can tell from the API, there’s not a safe way to do this, and plugins will race with the backlinks update, as well as potentially other plugins. If that is true, then I’d like to suggest an update to the API to prevent races when reading and writing to a file.

If it’s helpful for anyone else, what I’ve settled on is to check the cache refs to see if there are any stale links. This could still be unsafe if multiple plugins are attempting to modify a file for the same event. It’s unfortunately not great, since it relies on sleeping/setTimeout.

const CHECK_SAFE_ATTEMPTS = 10;
const CHECK_SAFE_WAIT = 50;
async function safeToUpdate(
  app: App,
  file: TFile,
  oldPath: string
): Promise<void> {
  const oldLinks = pathToLinks(oldPath);
  for (let i = 0; i < CHECK_SAFE_ATTEMPTS; i++) {
    const refCache = await app.metadataCache.getFileCache(file);
    const staleLinks = iterateCacheRefs(refCache, (ref) =>
      oldLinks.includes(ref.link)
    );
    if (!staleLinks) {
      return;
    }
    await new Promise((resolve) =>
      setTimeout(resolve, CHECK_SAFE_WAIT + CHECK_SAFE_WAIT * i)
    );
  }
}

function pathToLinks(path: string): string[] {
  const regex = /^(?<parent>\/?(?:[^./]+\/)*)(?<name>[^.]+)(?<ext>(?:\.\w+)+)/;
  const { parent, name, ext } = path.match(regex).groups;
  return parent.split("/").flatMap((_, idx, ps) => {
    const path = ps.slice(idx).join("/");
    return [path + name, path + name + ext];
  });
}