commit 51dffeae16f9a51c8d7260972be2d86c79fa35d3
parent 95e957736627cd11d977424f5cd18d496747dd18
Author: Matthias Jaros <jaros@mailbox.org>
Date: Sat, 4 Jul 2026 12:06:49 +0200
Same producer pattern for ports as for hosts
Diffstat:
1 file changed, 44 insertions(+), 30 deletions(-)
diff --git a/src/main/java/com/it_jaros/jscanner/Scanner.java b/src/main/java/com/it_jaros/jscanner/Scanner.java
@@ -138,45 +138,59 @@ public class Scanner implements AutoCloseable {
CompletionService<PortResult> completionService = new ExecutorCompletionService<>(executor);
List<Throwable> errors = new ArrayList<>();
- AtomicLong portSlotFactory = new AtomicLong(System.nanoTime());
- int activePerHostWorkers = 0;
- while ((portRange.hasNext() && !cancelled) || activePerHostWorkers > 0) {
- while (
- portRange.hasNext() // continue as long as we have ports
- && activePerHostWorkers < maxWorkersPerHost // and we have not reached our limit
- && !cancelled // and the scanner is not about to close
- ) {
- // Here we are filling the completion service
- // port <-> virtual thread
- final int currentPort = portRange.next();
- activePerHostWorkers++;
- completionService.submit(() -> {
- waitForSlot(portSlotFactory);
- return checkPort(host, currentPort, scan);
- });
+ final AtomicLong portSlotFactory = new AtomicLong(System.nanoTime());
+ final Semaphore activePerHostWorkers = new Semaphore(maxWorkersPerHost);
+ final AtomicInteger inPipeline = new AtomicInteger(0);
+
+ // producer thread
+ executor.submit(() -> {
+ try {
+ while (portRange.hasNext() && !cancelled) {
+ activePerHostWorkers.acquire();
+ // Here we are filling the completion service
+ // port <-> virtual thread
+ final int currentPort = portRange.next();
+ inPipeline.incrementAndGet();
+ completionService.submit(() -> {
+ waitForSlot(portSlotFactory);
+ return checkPort(host, currentPort, scan);
+ });
+ }
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
}
+ });
+ // consumer is the main thread
+ while ((portRange.hasNext() && !cancelled) || inPipeline.get() > 0) {
try {
Future<PortResult> portResultFuture = completionService.poll(10, TimeUnit.MILLISECONDS);
if (portResultFuture == null) {
continue;
}
- PortResult portResult = portResultFuture.get();
- activePerHostWorkers--;
- progress.done().incrementAndGet();
- switch (portResult.getState()) {
- case OPEN -> {
- // bitset is not thread-safe, so it is set
- // outside the other virtual threads that update progress
- openPorts.set(portResult.getPort());
- progress.open().incrementAndGet();
- serviceTypes.put(portResult.getPort(), ServiceDetector.detect(portResult.getBanner()));
- }
- case FILTERED -> {
- filteredPorts.set(portResult.getPort());
- progress.filtered().incrementAndGet();
+ try {
+ PortResult portResult = portResultFuture.get();
+ switch (portResult.getState()) {
+ case OPEN -> {
+ // bitset is not thread-safe, so it is set
+ // outside the other virtual threads that update progress
+ openPorts.set(portResult.getPort());
+ progress.open().incrementAndGet();
+ serviceTypes.put(portResult.getPort(), ServiceDetector.detect(portResult.getBanner()));
+ }
+ case FILTERED -> {
+ filteredPorts.set(portResult.getPort());
+ progress.filtered().incrementAndGet();
+ }
+ default -> {
+ // sonarcube glücklich machen
+ }
}
+ } finally {
+ activePerHostWorkers.release();
+ inPipeline.decrementAndGet();
+ progress.done().incrementAndGet();
}
} catch (InterruptedException ignored) {
Thread.currentThread().interrupt();