This repository has been archived by the owner on Jul 17, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix: Use daemon threads for SolverManager (#69)
Before, if a SolverManager creates a Solver, it creates a non-daemon thread, which can prevent the Python process from EVER exiting unless forced. Now, the SolverManager spawns only daemon threads, which allows the Python process to exit. This also allows us to remove some test configuration code that was used to force the JVM to exit. Additionaly, SolverManager can take SolverConfig directly and may take a SolutionManagerConfig too. Removed some solver manager tests to reduced flakiness.
- Loading branch information
1 parent
b6b1fa1
commit 57ac260
Showing
5 changed files
with
83 additions
and
69 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
32 changes: 32 additions & 0 deletions
32
timefold-solver-python-core/src/main/java/ai/timefold/solver/python/DaemonThreadFactory.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
package ai.timefold.solver.python; | ||
|
||
import java.util.concurrent.Executors; | ||
import java.util.concurrent.ThreadFactory; | ||
|
||
/** | ||
* There a Catch-22 that occurs on shutdown: | ||
* <p> | ||
* - In order for Python to free its variables, it must be terminated. | ||
* - In order for Python to be terminated, the JVM must be terminated. | ||
* - In order for the JVM to be terminated, all its non-daemon threads must be terminated. | ||
* - Executors keep all its threads alive until it is freed/have no more references. | ||
* - In order for the Executor to be freed/have no more references, it cannot have a reference in Python. | ||
* - To not have a reference in Python means Python must free its variables, creating the Catch-22 | ||
* <p> | ||
* Thus, if non-daemon threads are used, and a {@link ai.timefold.solver.core.api.solver.SolverManager} | ||
* solves at least one problem (creating a keep-alive thread in its {@link java.util.concurrent.ThreadPoolExecutor}), | ||
* Python cannot shut down gracefully and will become unresponsive when interrupted. | ||
* <p> | ||
* This class uses {@link Executors#defaultThreadFactory()} to create a new thread, but sets the created | ||
* thread to daemon mode so Python can shut down gracefully. | ||
*/ | ||
public class DaemonThreadFactory implements ThreadFactory { | ||
private static final ThreadFactory THREAD_FACTORY = Executors.defaultThreadFactory(); | ||
|
||
@Override | ||
public Thread newThread(Runnable runnable) { | ||
Thread out = THREAD_FACTORY.newThread(runnable); | ||
out.setDaemon(true); | ||
return out; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters