# How Trio Tasks Are Launched Concurrently in Holehe

> Discover how Holehe launches Trio tasks concurrently. Learn about nursery, start_soon, and launch_module for simultaneous module execution, deterministic concurrency, and isolated error handling.

- Repository: [Palenath/holehe](https://github.com/megadose/holehe)
- Tags: internals
- Published: 2026-08-24

---

**Holehe executes hundreds of website-checking modules simultaneously by opening a Trio nursery in `maincore()` and scheduling each module via `nursery.start_soon()`, wrapping individual HTTP requests in `launch_module()` to ensure deterministic concurrency and isolated error handling.**

Holehe is a Python-based open-source intelligence (OSINT) tool designed to verify email addresses across hundreds of online services. To maximize throughput without blocking the event loop, the project adopts the Trio library for structured concurrency. Analyzing how Trio tasks are launched concurrently within the `megadose/holehe` repository reveals a robust architectural pattern using nurseries, thin task wrappers, and low-level instrumentation.

## Entry Point and Async Runtime Initialization

The concurrent execution begins in [`holehe/core.py`](https://github.com/megadose/holehe/blob/main/holehe/core.py), where the synchronous `main()` function bootstraps the Trio event loop.

```python

# holehe/core.py

import trio

def main():
    # ... argument parsing ...

    trio.run(maincore, args)

```

Calling `trio.run(maincore)` creates a single-threaded async environment and executes the `maincore()` coroutine until completion. This entry point ensures that all subsequent async operations—including HTTP requests and task scheduling—run within Trio's deterministic concurrency model.

## Structured Concurrency with Trio Nurseries

Inside `maincore()`, Holehe implements structured concurrency by opening a **nursery**, which acts as a parent container for all concurrent tasks.

### Creating the Task Nursery

After parsing command-line arguments and discovering available modules via `get_functions()`, `maincore()` instantiates an `httpx.AsyncClient` and prepares the nursery context:

```python

# holehe/core.py

async def maincore(args):
    modules = import_submodules("holehe.modules")
    websites = get_functions(modules, args)
    client = httpx.AsyncClient()
    
    async with trio.open_nursery() as nursery:
        # Task scheduling occurs here

```

The `async with trio.open_nursery()` statement creates a scope where tasks can be launched concurrently. The nursery guarantees that execution proceeds only after every scheduled task has completed or failed, providing deterministic cleanup.

### Scheduling Concurrent Checks

Holehe iterates over the collected website modules and schedules each one using `nursery.start_soon()`. This method accepts a coroutine function and its arguments, creating a new child task that runs concurrently with all others:

```python

# holehe/core.py

out = []  # Shared result list

async with trio.open_nursery() as nursery:
    for website in websites:
        nursery.start_soon(
            launch_module, 
            website, 
            email, 
            client, 
            out
        )

```

By scheduling `launch_module` rather than the raw module functions directly, Holehe inserts a lightweight wrapper that standardizes error handling and result collection across all services.

## Task Wrapping and Error Isolation

The `launch_module()` function in [`holehe/core.py`](https://github.com/megadose/holehe/blob/main/holehe/core.py) serves as a thin adapter between the nursery and the individual website modules located in `holehe/modules/`. This isolation prevents a single failing service from crashing the entire concurrent batch.

```python

# holehe/core.py

async def launch_module(module, email, client, out):
    try:
        await module(email, client, out)
    except Exception:
        name = str(module).split('<function ')[1].split(' ')[0]
        out.append({
            "name": name,
            "domain": module.__name__,
            "rateLimit": False,
            "error": True,
            "exists": False,
            "emailrecovery": None,
            "phoneNumber": None,
            "others": None,
        })

```

Each `launch_module` task runs independently, appending its result dictionary to the shared `out` list upon completion. Because Trio nurseries propagate exceptions only after all tasks finish, the explicit `try/except` block ensures that individual module failures are captured locally while the remaining tasks continue execution.

## Real-Time Progress Monitoring with Custom Instruments

To provide feedback during concurrent execution, Holehe leverages Trio's low-level instrumentation API. Before opening the nursery, `maincore()` registers a custom `TrioProgress` instrument:

```python

# holehe/core.py

from holehe.instruments import TrioProgress

instrument = TrioProgress(len(websites))
trio.lowlevel.add_instrument(instrument)

async with trio.open_nursery() as nursery:
    # ... task scheduling ...

# After nursery exits:

trio.lowlevel.remove_instrument(instrument)

```

The `TrioProgress` class, defined in [`holehe/instruments.py`](https://github.com/megadose/holehe/blob/main/holehe/instruments.py), hooks into Trio's task lifecycle events to track how many modules have completed. This allows the tool to render a real-time progress bar without blocking the event loop or requiring explicit callbacks within each module.

## Summary

- **Nursery-based concurrency**: Holehe launches Trio tasks concurrently via `nursery.start_soon()` within an `async with trio.open_nursery()` block in [`holehe/core.py`](https://github.com/megadose/holehe/blob/main/holehe/core.py), ensuring all modules run simultaneously.
- **Deterministic cleanup**: The nursery scope guarantees that the program proceeds to result sorting and CSV export only after every scheduled task has finished.
- **Isolated error handling**: The `launch_module()` wrapper catches exceptions individually, preventing one failed HTTP request from terminating the entire batch.
- **Instrumentation**: Custom `TrioProgress` instruments are added via `trio.lowlevel.add_instrument()` to monitor task completion across the concurrent workload.

## Frequently Asked Questions

### How does Holehe ensure all Trio tasks complete before exiting?

The `async with trio.open_nursery()` context manager in [`holehe/core.py`](https://github.com/megadose/holehe/blob/main/holehe/core.py) acts as a synchronization barrier. Execution blocks at the end of this block until every task scheduled via `nursery.start_soon()` has finished, allowing safe cleanup of the `httpx.AsyncClient` and removal of the `TrioProgress` instrument.

### What happens if one module crashes during concurrent execution?

The `launch_module()` wrapper in [`holehe/core.py`](https://github.com/megadose/holehe/blob/main/holehe/core.py) catches exceptions individually within a `try/except` block. Instead of propagating the error to the nursery—which would mark the task as failed but not stop others—it appends an error state to the shared results list, ensuring that checks against other websites continue uninterrupted.

### Why does Holehe use Trio instead of Python's standard asyncio?

According to the `megadose/holehe` source code, Trio provides structured concurrency primitives—specifically nurseries and cancellation scopes—that make it easier to reason about concurrent task lifecycles. The nursery pattern ensures that no child task outlives its parent scope, preventing resource leaks common in traditional asyncio patterns.

### Where is the concurrent task logic implemented?

The orchestration logic resides in [`holehe/core.py`](https://github.com/megadose/holehe/blob/main/holehe/core.py) within the `maincore()` function, which opens the nursery and schedules tasks. The individual website-checking implementations are located in `holehe/modules/` as separate async functions, each adhering to the signature `async def module_name(email, client, out)`.