+
+ | Caller thread |
+ Wrapper thread |
+ Job thread |
+
+
+ | Caller class |
+ JobManager |
+ JobManager anonymous class |
+ Job instance |
+ Job execute method |
+ Subjob instance |
+ Subjob execute method |
+
+
+ | Create job |
+ |
+ |
+ |
+ |
+ |
+ |
+
+
+ | Configure job |
+ |
+ |
+ |
+ |
+ |
+ |
+
+
+ | Run job |
+ |
+ |
+ |
+ |
+ |
+ |
+
+
+ |
+ Check preconditions |
+ |
+ |
+ |
+ |
+ |
+
+
+ |
+ Queue job |
+ |
+ |
+ |
+ |
+ |
+
+
+ |
+ Create & start wrapper thread |
+ |
+ |
+ |
+ |
+ |
+
+
+ |
+ |
+ Start job |
+ |
+ |
+ |
+ |
+
+
+ |
+ |
+ Cancel job if it reaches timeout |
+ Register subjobs |
+ |
+ |
+ |
+
+
+ |
+ |
+ Execute task |
+ |
+ |
+ |
+
+
+ |
+ |
+ |
+ Run subjob |
+ |
+ |
+
+
+ |
+ |
+ |
+ Execute task |
+ |
+
+
+ |
+ |
+ |
+ |
+ Do something |
+
+
+ |
+ |
+ |
+ |
+ Update progress |
+
+
+ |
+ |
+ |
+ Handle progress update & run event listeners |
+
+
+ |
+ |
+ Handle progress update & run event listeners |
+
+
+ |
+ |
+ |
+ |
+ Do something |
+
+
+ |
+ |
+ |
+ Finish & run event listeners |
+ |
+
+
+ |
+ |
+ |
+ Do something |
+ |
+ |
+
+
+ |
+ |
+ Finish & run event listeners |
+ |
+ |
+ |
+
+
+ |
+ Try to launch next job |
+ |
+ |
+ |
+ |
+ |
+
+
+
+`Job::runAndAwait` works similarly, except that it blocks the caller thread with a while loop (and 200ms `Thread::sleep`s) until the job in question finished executing.
+
+
+## Conventions & guidelines
+- Put larger jobs into their own classes. Anonymous inner classes get messy really quickly.
+- When using the job system in libraries, _always_ use the blocking `Job::runAndAwait`. Library consumers shouldn't have to deal with annoying async stuff. If they want to, they can wrap the method call into a job of their own, and the JobManager will proceed to add your library jobs as children of the library consumer's job. This way, you don't annoy them by default, but developers still have the benefit of seeing what subjobs are being executed when they decide to wrap everything into their own job.
+- As already said earlier, try to register subjobs as early as possible, so users can see what's running and better predict how long they'll have to wait.
diff --git a/src/main/java/matcher/Matcher.java b/src/main/java/matcher/Matcher.java
index 121b604b..0ed17f9b 100644
--- a/src/main/java/matcher/Matcher.java
+++ b/src/main/java/matcher/Matcher.java
@@ -15,17 +15,18 @@
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
+import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.Future;
+import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
import java.util.function.DoubleConsumer;
import java.util.function.Function;
-import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
+import job4j.Job;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -38,6 +39,8 @@
import matcher.classifier.RankResult;
import matcher.config.Config;
import matcher.config.ProjectConfig;
+import matcher.jobs.JobCategories;
+import matcher.jobs.MatcherJob;
import matcher.type.ClassEnv;
import matcher.type.ClassEnvironment;
import matcher.type.ClassInstance;
@@ -59,19 +62,64 @@ public Matcher(ClassEnvironment env) {
this.env = env;
}
- public void init(ProjectConfig config, DoubleConsumer progressReceiver) {
- try {
- env.init(config, progressReceiver);
+ public void init(ProjectConfig config) {
+ var job = new MatcherJob