Skip to content

Commit

Permalink
locateWorkingObject()
Browse files Browse the repository at this point in the history
  • Loading branch information
ge0ffrey committed Nov 26, 2016
1 parent 045cab0 commit c709f61
Show file tree
Hide file tree
Showing 4 changed files with 30 additions and 35 deletions.
Expand Up @@ -251,9 +251,10 @@ Here's an example:
public void deleteComputer(final CloudComputer computer) {
solver.addProblemFactChange(scoreDirector -> {
CloudBalance cloudBalance = scoreDirector.getWorkingSolution();
CloudComputer workingComputer = scoreDirector.locateWorkingObject(computer);
// First remove the problem fact from all planning entities that use it
for (CloudProcess process : cloudBalance.getProcessList()) {
if (Objects.equals(process.getComputer(), computer)) {
if (process.getComputer() == workingComputer) {
scoreDirector.beforeVariableChanged(process, "computer");
process.setComputer(null);
scoreDirector.afterVariableChanged(process, "computer");
Expand All @@ -262,17 +263,13 @@ Here's an example:
scoreDirector.triggerVariableListeners();
// A SolutionCloner does not clone problem fact lists (such as computerList)
// Shallow clone the computerList so only workingSolution is affected, not bestSolution or guiSolution
cloudBalance.setComputerList(new ArrayList<>(cloudBalance.getComputerList()));
ArrayList<CloudComputer> computerList = new ArrayList<>(cloudBalance.getComputerList());
cloudBalance.setComputerList(computerList);
// Remove the problem fact itself
for (Iterator<CloudComputer> it = cloudBalance.getComputerList().iterator(); it.hasNext(); ) {
CloudComputer workingComputer = it.next();
if (Objects.equals(workingComputer, computer)) {
scoreDirector.beforeProblemFactRemoved(workingComputer);
it.remove(); // remove from list
scoreDirector.afterProblemFactRemoved(workingComputer);
break;
}
}
scoreDirector.beforeProblemFactRemoved(workingComputer);
computerList.remove(workingComputer);
scoreDirector.afterProblemFactRemoved(workingComputer);
scoreDirector.triggerVariableListeners();
});
}
----
Expand All @@ -284,14 +281,18 @@ Any change on the problem facts or planning entities in a `ProblemFactChange` mu

image::Chapter-Repeated_planning/realTimePlanningConcurrencySequenceDiagram.png[align="center"]


[IMPORTANT]
====
To write a `ProblemFactChange` correctly, it's important to understand the behaviour of <<cloningASolution,a planning clone>>:
* Any change in a `ProblemFactChange` must be done on the `Solution` instance of ``scoreDirector.getWorkingSolution()``. The `workingSolution` is <<cloningASolution,a planning clone>> of the ``BestSolutionChangedEvent``'s ``bestSolution``. So the `workingSolution` in the `Solver` is never the same instance as the `Solution` in the rest of your application.
* A planning clone also clones the planning entities and planning entity collections. So any change on the planning entities must happen on the instances hold by ``scoreDirector.getWorkingSolution()``.
* A planning clone does not clone the problem facts, nor the problem fact collections. _Therefore the ``__workingSolution__`` and the ``__bestSolution__`` share the same problem fact instances and the same problem fact list instances._
* Any change in a `ProblemFactChange` must be done on the `Solution` instance of ``scoreDirector.getWorkingSolution()``.
The `workingSolution` is <<cloningASolution,a planning clone>> of the ``BestSolutionChangedEvent``'s ``bestSolution``.
So the `workingSolution` in the `Solver` is never the same instance as the `Solution` in the rest of your application: it is a planning clone.
Use the method `ScoreDirector.locateWorkingObject()` to translate an retrieve the working solution's instance of an object.
* A planning clone also clones the planning entities and planning entity collections.
So any change on the planning entities must happen on the instances hold by ``scoreDirector.getWorkingSolution()``.
* A planning clone does not clone the problem facts, nor the problem fact collections.
_Therefore the ``__workingSolution__`` and the ``__bestSolution__`` share the same problem fact instances and the same problem fact list instances._
+
Any problem fact or problem fact list changed by a `ProblemFactChange` must be problem cloned first (which can imply rerouting references in other problem facts and planning entities). Otherwise, if the `workingSolution` and `bestSolution` are used in different threads (for example a solver thread and a GUI event thread), a race condition can occur.
Expand Down
Expand Up @@ -42,6 +42,7 @@ public void doChange(ScoreDirector<CloudBalance> scoreDirector) {
}
}
process.setId(nextProcessId);
// A SolutionCloner clones planning entity lists (such as processList), so no need to clone the processList here
// Add the planning entity itself
scoreDirector.beforeEntityAdded(process);
cloudBalance.getProcessList().add(process);
Expand Down
Expand Up @@ -36,9 +36,10 @@ public DeleteComputerProblemFactChange(CloudComputer computer) {

public void doChange(ScoreDirector<CloudBalance> scoreDirector) {
CloudBalance cloudBalance = scoreDirector.getWorkingSolution();
CloudComputer workingComputer = scoreDirector.locateWorkingObject(computer);
// First remove the problem fact from all planning entities that use it
for (CloudProcess process : cloudBalance.getProcessList()) {
if (Objects.equals(process.getComputer(), computer)) {
if (process.getComputer() == workingComputer) {
scoreDirector.beforeVariableChanged(process, "computer");
process.setComputer(null);
scoreDirector.afterVariableChanged(process, "computer");
Expand All @@ -47,17 +48,13 @@ public void doChange(ScoreDirector<CloudBalance> scoreDirector) {
scoreDirector.triggerVariableListeners();
// A SolutionCloner does not clone problem fact lists (such as computerList)
// Shallow clone the computerList so only workingSolution is affected, not bestSolution or guiSolution
cloudBalance.setComputerList(new ArrayList<>(cloudBalance.getComputerList()));
ArrayList<CloudComputer> computerList = new ArrayList<>(cloudBalance.getComputerList());
cloudBalance.setComputerList(computerList);
// Remove the problem fact itself
for (Iterator<CloudComputer> it = cloudBalance.getComputerList().iterator(); it.hasNext(); ) {
CloudComputer workingComputer = it.next();
if (Objects.equals(workingComputer, computer)) {
scoreDirector.beforeProblemFactRemoved(workingComputer);
it.remove(); // remove from list
scoreDirector.afterProblemFactRemoved(workingComputer);
break;
}
}
scoreDirector.beforeProblemFactRemoved(workingComputer);
computerList.remove(workingComputer);
scoreDirector.afterProblemFactRemoved(workingComputer);
scoreDirector.triggerVariableListeners();
}

}
Expand Up @@ -34,16 +34,12 @@ public DeleteProcessProblemFactChange(CloudProcess process) {

public void doChange(ScoreDirector<CloudBalance> scoreDirector) {
CloudBalance cloudBalance = scoreDirector.getWorkingSolution();
// A SolutionCloner clones planning entity lists (such as processList), so no need to clone the processList here
CloudProcess workingProcess = scoreDirector.locateWorkingObject(process);
// Remove the planning entity itself
for (Iterator<CloudProcess> it = cloudBalance.getProcessList().iterator(); it.hasNext(); ) {
CloudProcess workingProcess = it.next();
if (Objects.equals(workingProcess, process)) {
scoreDirector.beforeEntityRemoved(workingProcess);
it.remove(); // remove from list
scoreDirector.afterEntityRemoved(workingProcess);
break;
}
}
scoreDirector.beforeEntityRemoved(workingProcess);
cloudBalance.getProcessList().remove(workingProcess);
scoreDirector.afterEntityRemoved(workingProcess);
scoreDirector.triggerVariableListeners();
}

Expand Down

0 comments on commit c709f61

Please sign in to comment.