Skip to content

Commit 989a393

Browse files
committed
fix: multi-threaded constraint verified broken by previous commit
1 parent 3c3c41a commit 989a393

File tree

1 file changed

+22
-10
lines changed

1 file changed

+22
-10
lines changed

test/src/main/java/ai/timefold/solver/test/impl/score/stream/ConfiguredConstraintVerifier.java

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,35 +12,47 @@
1212
import ai.timefold.solver.core.api.score.stream.ConstraintProvider;
1313
import ai.timefold.solver.core.config.solver.EnvironmentMode;
1414
import ai.timefold.solver.core.impl.domain.solution.descriptor.SolutionDescriptor;
15-
15+
import ai.timefold.solver.test.api.score.stream.ConstraintVerifier;
16+
17+
/**
18+
* Represents a {@link ConstraintVerifier} with pre-set values.
19+
* A new instance of this class will be created should this change.
20+
* <p>
21+
* This class still needs to be thread-safe, as {@link ScoreDirectorFactoryCache} is not.
22+
* Failure to do so will lead to intermittent failures in tests when run in parallel.
23+
*
24+
* @param <ConstraintProvider_>
25+
* @param <Solution_>
26+
* @param <Score_>
27+
*/
1628
final class ConfiguredConstraintVerifier<ConstraintProvider_ extends ConstraintProvider, Solution_, Score_ extends Score<Score_>> {
1729

18-
/**
19-
* Exists so that people can not, even by accident, pick the same constraint ID as the default cache key.
20-
*/
30+
// Exists so that people can not, even by accident, pick the same constraint ID as the default cache key.
2131
private final ConstraintRef defaultScoreDirectorFactoryMapKey =
2232
ConstraintRef.of(UUID.randomUUID().toString(), UUID.randomUUID().toString());
2333

2434
private final ConstraintProvider_ constraintProvider;
25-
private final ScoreDirectorFactoryCache<ConstraintProvider_, Solution_, Score_> scoreDirectorFactoryContainer;
35+
@SuppressWarnings("java:S5164") // Suppress SonarCloud ThreadLocal warning; this is safe in the context of tests.
36+
private final ThreadLocal<ScoreDirectorFactoryCache<ConstraintProvider_, Solution_, Score_>> scoreDirectorFactoryContainerThreadLocal;
2637

2738
public ConfiguredConstraintVerifier(ConstraintProvider_ constraintProvider,
2839
SolutionDescriptor<Solution_> solutionDescriptor) {
2940
this.constraintProvider = constraintProvider;
30-
this.scoreDirectorFactoryContainer = new ScoreDirectorFactoryCache<>(solutionDescriptor);
41+
this.scoreDirectorFactoryContainerThreadLocal =
42+
ThreadLocal.withInitial(() -> new ScoreDirectorFactoryCache<>(solutionDescriptor));
3143
}
3244

3345
public DefaultSingleConstraintVerification<Solution_, Score_> verifyThat(
3446
BiFunction<ConstraintProvider_, ConstraintFactory, Constraint> constraintFunction) {
3547
requireNonNull(constraintFunction);
36-
var scoreDirectorFactory = scoreDirectorFactoryContainer.getScoreDirectorFactory(constraintFunction, constraintProvider,
37-
EnvironmentMode.FULL_ASSERT);
48+
var scoreDirectorFactory = scoreDirectorFactoryContainerThreadLocal.get().getScoreDirectorFactory(constraintFunction,
49+
constraintProvider, EnvironmentMode.FULL_ASSERT);
3850
return new DefaultSingleConstraintVerification<>(scoreDirectorFactory);
3951
}
4052

4153
public DefaultMultiConstraintVerification<Solution_, Score_> verifyThat() {
42-
var scoreDirectorFactory = scoreDirectorFactoryContainer.getScoreDirectorFactory(defaultScoreDirectorFactoryMapKey,
43-
constraintProvider, EnvironmentMode.FULL_ASSERT);
54+
var scoreDirectorFactory = scoreDirectorFactoryContainerThreadLocal.get()
55+
.getScoreDirectorFactory(defaultScoreDirectorFactoryMapKey, constraintProvider, EnvironmentMode.FULL_ASSERT);
4456
return new DefaultMultiConstraintVerification<>(scoreDirectorFactory, constraintProvider);
4557
}
4658

0 commit comments

Comments
 (0)