Repository: jenkinsci/jenkins
File: core/src/main/java/hudson/model/Fingerprint.java (Java)

Code before:
     * @param b {@link Run} to be referenced in {@link #usages}
     * @since 1.577
     */
    public void addFor(@NonNull Run b) throws IOException {
        add(b.getParent().getFullName(), b.getNumber());
    }

    /**
     * Records that a build of a job has used this file.
     */
    public synchronized void add(@NonNull String jobFullName, int n) throws IOException {
        addWithoutSaving(jobFullName, n);
        save();
    }

    // JENKINS-49588
    @SuppressFBWarnings(value = "IS2_INCONSISTENT_SYNC", justification = "nothing should be competing with XStream during deserialization")
    protected Object readResolve() {
        if (usages == null) {
            usages = new Hashtable<>();
        }
        return this;
    }

    void addWithoutSaving(@NonNull String jobFullName, int n) {
        synchronized (this) {
            RangeSet r = usages.get(jobFullName);
            if (r == null) {
                r = new RangeSet();
                usages.put(jobFullName, r);
            }
            r.add(n);
        }
    }

    /**
     * Returns true if any of the builds recorded in this fingerprint
     * is still retained.
     *
     * <p>
     * This is used to find out old fingerprint records that can be removed
     * without losing too much information.
     */
    public synchronized boolean isAlive() {
        if (original != null && original.isAlive())
            return true;

        for (Map.Entry<String, RangeSet> e : usages.entrySet()) {
            Job j = Jenkins.get().getItemByFullName(e.getKey(), Job.class);
            if (j == null)
                continue;

Review comment (suggestion):
This was implemented with that comment in 2013.  You haven't mentioned a specific problem that has been detected due to that synchronization, nor have you described the detailed analysis you did to confirm that this change to synchronization is safe, nor have you created tests that exercise the concurrency.

I'm not willing to accept this change without a clear description of the specific problem that is being solved and tests that show the problem is solved by the change.

Code after:
     * @param b {@link Run} to be referenced in {@link #usages}
     * @since 1.577
     */
    public void addFor(@NonNull Run b) throws IOException {
        add(b.getParent().getFullName(), b.getNumber());
    }

    /**
     * Records that a build of a job has used this file.
     */
    public synchronized void add(@NonNull String jobFullName, int n) throws IOException {
        addWithoutSaving(jobFullName, n);
        save();
    }

    // JENKINS-49588
    @SuppressFBWarnings(value = "IS2_INCONSISTENT_SYNC", justification = "nothing should be competing with XStream during deserialization")
    protected Object readResolve() {
        if (usages == null) {
            usages = new Hashtable<>();
        }
        return this;
    }

    void addWithoutSaving(@NonNull String jobFullName, int n) {
        synchronized (usages) { // TODO why not synchronized (this) like some, though not all, other accesses?
            RangeSet r = usages.get(jobFullName);
            if (r == null) {
                r = new RangeSet();
                usages.put(jobFullName, r);
            }
            r.add(n);
        }
    }

    /**
     * Returns true if any of the builds recorded in this fingerprint
     * is still retained.
     *
     * <p>
     * This is used to find out old fingerprint records that can be removed
     * without losing too much information.
     */
    public synchronized boolean isAlive() {
        if (original != null && original.isAlive())
            return true;

        for (Map.Entry<String, RangeSet> e : usages.entrySet()) {
            Job j = Jenkins.get().getItemByFullName(e.getKey(), Job.class);
            if (j == null)
                continue;