# How Patator's Multi-Threaded Architecture Handles Concurrent Connections

> Discover how Patator's multi-threaded architecture manages concurrent connections using producer-consumer patterns with isolated queues and shared synchronization for efficient performance. Learn more.

- Repository: [lanjelot/patator](https://github.com/lanjelot/patator)
- Tags: internals
- Published: 2026-03-05

---

**Patator implements concurrency using a producer-consumer pattern built on Python's `multiprocessing` module, where dedicated OS processes (exposed as "threads" in the CLI) handle network connections via isolated task queues and a shared synchronization namespace.**

Patator's multi-threaded architecture is designed to maximize throughput during brute-force and fuzzing operations by sidestepping Python's Global Interpreter Lock (GIL). Despite the CLI referring to them as "threads," the tool actually spawns separate OS processes to manage concurrent connections, ensuring true parallelism for network I/O tasks.

## Process-Based Concurrency vs. Threading

While the command-line interface uses the `--threads` argument, Patator's core controller in [`src/patator/patator.py`](https://github.com/lanjelot/patator/blob/main/src/patator/patator.py) utilizes the `multiprocessing` module to spawn independent processes. This architectural decision prevents network latency from blocking the main execution loop and allows the tool to saturate network bandwidth during high-speed attacks.

Each worker operates as a standalone `Consumer` process with its own memory space, eliminating race conditions during payload execution while maintaining coordination through a `SyncManager` namespace.

## The Producer-Consumer Pattern in Patator's Architecture

### Task Distribution Across Worker Queues

The controller initializes one dedicated `multiprocessing.Queue` per worker with a maximum buffer size of 10,000 items. This isolation prevents queue contention between high-volume consumers:

```python

# From src/patator/patator.py (L1144)

task_queues = [multiprocessing.Queue(maxsize=10000) for _ in range(self.num_threads)]

```

Each queue feeds a single consumer process, ensuring that payload distribution remains lock-free and scalable up to the operating system's process limits.

### The Producer Process

A dedicated `Producer` process generates the Cartesian product of wordlists, ranges, and static payloads. It distributes tasks using a round-robin algorithm to balance load across all workers:

```python

# From src/patator/patator.py (L1267-L1298)

cid = count % self.num_threads
task_queues[cid].put_nowait(prod)

```

The producer also enforces global rate-limiting (`--rate-limit`) and resume logic (`--resume`) before enqueueing work, ensuring that network constraints are respected at the generation layer rather than the execution layer.

### Consumer Processes (The "Threads")

For each configured thread, the controller spawns a `Consumer-%d` process that executes the actual network operations:

```python

# From src/patator/patator.py (L1148-L1153)

t = multiprocessing.Process(
    name='Consumer-%d' % num,
    target=self.consume,
    args=(task_queues[num], report_queue, logger.queue)
)

```

Each consumer runs an infinite loop that:
1. Pulls a payload from its dedicated queue
2. Expands placeholders (`FILE`, `NET`, `COMBO`, `RANGE`)
3. Applies encodings and transformations
4. Invokes the module's `execute(**payload)` method to perform the network request

## Synchronization and Shared State Management

Patator's multi-threaded architecture relies on a `SyncManager` namespace (`self.ns`) to coordinate actions across processes without shared memory:

```python

# Conceptual representation from patator.py

self.ns = manager.Namespace()
self.ns.quit = False
self.ns.pause = False

```

Consumers check these flags before executing payloads, enabling global commands like:
- **Pause**: Halting new connections while maintaining active sockets
- **Quit**: Graceful shutdown after current jobs complete
- **Skip**: Bypassing specific payload iterations

This design eliminates the need for complex locking mechanisms while maintaining responsive control over long-running brute-force sessions.

## Result Aggregation and Logging

After executing a network request, consumers post results to a thread-safe `report_queue`:

```python

# From consumer logic in patator.py

report_queue.put((actions, prod_str, resp, time() - start_time))

```

The main controller aggregates these tuples to update per-worker progress bars and statistics. A separate `LogSvc` process (defined in [`src/patator/patator.py`](https://github.com/lanjelot/patator/blob/main/src/patator/patator.py) lines 32-71) handles disk I/O for output files, preventing filesystem bottlenecks from blocking network operations.

## Summary

- Patator's multi-threaded architecture uses **OS processes** rather than Python threads to bypass the GIL and achieve true network parallelism.
- A **round-robin producer** distributes payloads across isolated per-worker queues (`task_queues`) to prevent contention.
- **Consumer processes** handle the actual socket connections, executing module-specific `execute()` methods in parallel.
- **SyncManager namespaces** coordinate global state (pause, quit, skip) without shared memory locks.
- **Separate logging processes** ensure disk I/O doesn't throttle network throughput.

## Frequently Asked Questions

### Does Patator use threads or processes for concurrency?

Patator uses **processes**, not threads. Despite the `--threads` CLI argument, the tool spawns separate OS processes via Python's `multiprocessing` module. This design avoids Python's Global Interpreter Lock (GIL), allowing true parallel execution of network I/O operations across multiple CPU cores.

### How does Patator distribute tasks across workers?

The controller creates one `multiprocessing.Queue` per worker in [`src/patator/patator.py`](https://github.com/lanjelot/patator/blob/main/src/patator/patator.py) (line 1144). A dedicated **Producer** process generates payload combinations and distributes them using round-robin logic (`cid = count % self.num_threads`), pushing each job to the appropriate worker's queue. This ensures load balancing while preventing queue contention.

### What happens if a consumer process crashes during execution?

Because each consumer runs as an independent OS process, a crash in one worker does not affect others. The main controller monitors the `report_queue` for results; if a consumer dies, its queue stops being consumed, but the remaining workers continue processing. The user can interrupt the entire operation using the global `quit` flag in the `SyncManager` namespace.

### How does Patator handle rate limiting across multiple workers?

Rate limiting is enforced at the **producer** level before jobs enter the queues. The producer checks global timing constraints and resume logic (`--rate-limit`, `--resume`) when generating payloads. This centralizes throttling control, ensuring that the aggregate request rate across all consumer processes does not exceed the specified limit, regardless of how many workers are active.