jscanner

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

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:
Msrc/main/java/com/it_jaros/jscanner/Scanner.java | 74++++++++++++++++++++++++++++++++++++++++++++------------------------------
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();