void updateSymlink(int fileId, @NotNull String linkPath, @Nullable String linkTarget) {
    synchronized (myLock) {
      SymlinkData data = mySymlinksById.get(fileId);
      if (data != null) {
        if (FileUtil.pathsEqual(data.path, linkPath) && FileUtil.pathsEqual(data.target, linkTarget)) {
          // Avoid costly removal and re-addition of the request in case of no-op update
          return;
        }
        mySymlinksById.remove(fileId);
        mySymlinksByPath.remove(data.path);
        data.removeRequest(this);
      }

      data = new SymlinkData(fileId, linkPath, linkTarget);

      SymlinkData existing = mySymlinksByPath.get(linkPath);
      if (existing != null) {
        LOG.error("Path conflict. " +
                  "Existing symlink: " + existing + " vs. incoming symlink: " + data);
        return;
      }

      mySymlinksByPath.put(data.path, data);
      mySymlinksById.put(data.id, data);
      if (data.hasValidTarget()
          && WatchRootsUtil.isCoveredRecursively(myOptimizedRecursiveWatchRoots, data.path)) {
        addWatchSymlinkRequest(data.getWatchRequest());
      }
    }
  }
void updateSymlink(int fileId, @NotNull String linkPath, @Nullable String linkTarget)