Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import org.eclipse.pde.internal.core.PDEPreferencesManager;
import org.eclipse.pde.internal.core.target.Messages;
import org.eclipse.pde.internal.core.target.TargetPlatformService;
import org.eclipse.pde.internal.core.target.TargetResolveSchedulingRule;

/**
* Sets the current target platform based on a target definition.
Expand Down Expand Up @@ -80,6 +81,12 @@ public static void load(ITargetDefinition target, IJobChangeListener listener) {
Job.getJobManager().cancel(JOB_FAMILY_ID);
Job job = new LoadTargetDefinitionJob(target);
job.setUser(true);
// Serialize loads of the same target handle so a new load waits for the
// previous (cancelled) one to actually drain. Job.cancel only sets a
// flag; the in-flight job continues until the next monitor check, so
// without a rule cancelled jobs pile up in the Progress view and two
// loads can race on the p2 profile lock/unlock pair.
job.setRule(TargetResolveSchedulingRule.forHandle(target.getHandle()));
if (listener != null) {
job.addJobChangeListener(listener);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*******************************************************************************
* Copyright (c) 2026 Lars Vogel and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*******************************************************************************/
package org.eclipse.pde.internal.core.target;

import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.jobs.ISchedulingRule;
import org.eclipse.pde.core.target.ITargetHandle;

/**
* A scheduling rule keyed by a target handle's memento string. Two instances
* with the same key conflict, which serializes jobs that operate on the same
* target handle (resolve and load) while leaving operations on different
* targets independent. This avoids races on the p2 profile lock/unlock pair
* (see issue #310) and stops cancelled jobs from piling up in the Progress
* view because cancellation only sets a flag and the in-flight work continues
* until the next monitor check.
*/
public record TargetResolveSchedulingRule(String key) implements ISchedulingRule {

public static ISchedulingRule forHandle(ITargetHandle handle) {
String key;
try {
key = handle.getMemento();
} catch (CoreException e) {
key = String.valueOf(System.identityHashCode(handle));
}
return new TargetResolveSchedulingRule(key);
}

@Override
public boolean contains(ISchedulingRule rule) {
return isConflicting(rule);
}

@Override
public boolean isConflicting(ISchedulingRule rule) {
return rule instanceof TargetResolveSchedulingRule other && key.equals(other.key);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.SafeRunner;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.ISchedulingRule;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.core.runtime.jobs.JobChangeAdapter;
import org.eclipse.e4.core.services.events.IEventBroker;
Expand Down Expand Up @@ -70,6 +69,7 @@
import org.eclipse.pde.internal.core.target.TargetDefinition;
import org.eclipse.pde.internal.core.target.TargetDefinitionPersistenceHelper;
import org.eclipse.pde.internal.core.target.TargetPlatformService;
import org.eclipse.pde.internal.core.target.TargetResolveSchedulingRule;
import org.eclipse.pde.internal.core.target.WorkspaceFileTargetHandle;
import org.eclipse.pde.internal.ui.IHelpContextIds;
import org.eclipse.pde.internal.ui.PDEPlugin;
Expand Down Expand Up @@ -646,37 +646,6 @@ private boolean isActiveTabTextualEditor() {
return getActivePage() == getPageCount() - 1;
}

/**
* A scheduling rule keyed by a target handle's memento string. Two
* instances with the same key conflict, which serializes resolve jobs for
* the same target handle while leaving resolves of different targets
* independent. This avoids races on the p2 profile lock/unlock pair (see
* issue #310) and the pile-up of cancelled resolve jobs in the Progress
* view. Because the rule carries only a lightweight String key and is not
* cached in a static map, there is no risk of unbounded retention.
*/
private record TargetResolveRule(String key) implements ISchedulingRule {
@Override
public boolean contains(ISchedulingRule rule) {
return isConflicting(rule);
}

@Override
public boolean isConflicting(ISchedulingRule rule) {
return rule instanceof TargetResolveRule other && key.equals(other.key);
}
}

private static ISchedulingRule getResolveSchedulingRule(ITargetHandle handle) {
String key;
try {
key = handle.getMemento();
} catch (CoreException e) {
key = String.valueOf(System.identityHashCode(handle));
}
return new TargetResolveRule(key);
}

/**
* When changes are noticed in the target, this listener will resolve the
* target and update the necessary pages in the editor.
Expand Down Expand Up @@ -757,7 +726,7 @@ public boolean belongsTo(Object family) {
return family.equals(getJobFamily());
}
};
resolveJob.setRule(getResolveSchedulingRule(getTarget().getHandle()));
resolveJob.setRule(TargetResolveSchedulingRule.forHandle(getTarget().getHandle()));
resolveJob.addJobChangeListener(new JobChangeAdapter() {
@Override
public void done(org.eclipse.core.runtime.jobs.IJobChangeEvent event) {
Expand Down
Loading