# How MiroFish Handles Asynchronous Tasks for Long-Running Simulations

> Discover how MiroFish uses Python's asyncio framework to manage long-running simulations concurrently with graceful shutdown and responsive control.

- Repository: [BaiFu/mirofish](https://github.com/666ghj/mirofish)
- Tags: internals
- Published: 2026-02-23

---

**MiroFish leverages Python's `asyncio` framework with a global shutdown event, signal handlers, and `asyncio.gather()` to execute Twitter and Reddit simulations concurrently while maintaining responsive control over potentially endless processes.**

The `666ghj/mirofish` repository implements a sophisticated asynchronous architecture to manage long-running social media simulations. By utilizing Python's native `asyncio` library, the system can execute complex Twitter and Reddit simulations without blocking the interpreter, while providing robust mechanisms for graceful shutdown and external command integration.

## Core Async Architecture

### Global Event Loop and Shutdown Coordination

At the heart of the system is a global `asyncio.Event` named `_shutdown_event`, instantiated in [`backend/scripts/run_parallel_simulation.py`](https://github.com/666ghj/mirofish/blob/main/backend/scripts/run_parallel_simulation.py) (lines 25-28). This event serves as a coordination mechanism that all coroutines monitor to detect termination requests.

```python

# From run_parallel_simulation.py lines 25-28

_shutdown_event = asyncio.Event()

def signal_handler(signum, frame):
    logger.info(f"Received signal {signum}, shutting down...")
    _shutdown_event.set()

```

### Async Simulation Loops

Each platform simulation runs as an independent `async def` coroutine. The `run_twitter_simulation` and `run_reddit_simulation` functions (lines 28-34) implement the main execution loops that repeatedly determine active agents, build dictionaries of `LLMAction()` objects, execute `await env.step(actions)`, and read SQLite traces to log actions.

## Execution Control and Resource Management

### Round Limiting via CLI

To prevent infinite execution, the system accepts a `--max-rounds` parameter through the CLI. In [`run_parallel_simulation.py`](https://github.com/666ghj/mirofish/blob/main/run_parallel_simulation.py) (lines 20-24), the configuration-derived round count is truncated to this limit, enabling controlled testing and CI/CD integration.

```bash
python backend/scripts/run_parallel_simulation.py \
  --config simulations/run1/simulation_config.json \
  --max-rounds 200

```

### Graceful Shutdown Implementation

The system registers signal handlers for `SIGINT` and `SIGTERM` in `setup_signal_handlers` (lines 63-72). When triggered, the handler sets `_shutdown_event`, which unblocks coroutines waiting on `await asyncio.wait_for(_shutdown_event.wait(), timeout=0.5)`. The environments then close via `await env.close()` before the process exits cleanly.

## Parallel Execution and IPC

### Concurrent Platform Simulation

When neither `--twitter-only` nor `--reddit-only` flags are present, the system launches both simulations concurrently using `asyncio.gather()` (lines 84-88). Each platform maintains independent loggers and SQLite databases while sharing the global shutdown event for coordinated termination.

```python

# From run_parallel_simulation.py lines 84-88

await asyncio.gather(
    run_twitter_simulation(twitter_env, config, logger, max_rounds),
    run_reddit_simulation(reddit_env, config, logger, max_rounds)
)

```

### Post-Simulation Command Interface

After completing the main simulation loop, the system enters an IPC (Inter-Process Communication) loop when `--no-wait` is not specified. The `ParallelIPCHandler.process_commands` coroutine polls a directory for JSON command files containing instructions such as "interview" or "batch interview", executing them asynchronously against the live OASIS environment.

## Summary

- **MiroFish leverages Python's `asyncio` framework** to execute long-running Twitter and Reddit simulations without blocking the interpreter.
- **A global `_shutdown_event`** coordinates graceful termination across all concurrent coroutines when `SIGINT` or `SIGTERM` signals are received.
- **The `--max-rounds` CLI parameter** provides deterministic execution limits for testing and CI environments.
- **`asyncio.gather()` enables parallel execution** of platform-specific simulations while maintaining independent logging and database resources.
- **The IPC command loop** allows external tools to interact with running simulations via JSON file drops, supporting interview commands and batch operations.

## Frequently Asked Questions

### How does MiroFish prevent simulations from running indefinitely?

The system implements a round-limiting mechanism through the `--max-rounds` CLI argument. In [`backend/scripts/run_parallel_simulation.py`](https://github.com/666ghj/mirofish/blob/main/backend/scripts/run_parallel_simulation.py) (lines 20-24), the total round count derived from the configuration file is truncated to this specified limit, ensuring simulations terminate deterministically for testing or production constraints.

### What happens when I press Ctrl+C during a simulation?

Pressing Ctrl+C sends a `SIGINT` signal that triggers the `setup_signal_handlers` function (lines 63-72). This sets the global `_shutdown_event`, which causes all running coroutines to exit their main loops cleanly. The system then awaits environment closure via `await env.close()` before terminating, preventing data corruption in the SQLite trace databases.

### Can I run Twitter and Reddit simulations simultaneously?

Yes. When neither `--twitter-only` nor `--reddit-only` flags are provided, the `main` function uses `asyncio.gather()` (lines 84-88) to launch both `run_twitter_simulation` and `run_reddit_simulation` concurrently. Each platform operates with independent loggers and SQLite databases while sharing the global shutdown event for coordinated termination.

### How do external tools interact with a running simulation after it completes the main loop?

When the `--no-wait` flag is omitted, the system enters an IPC (Inter-Process Communication) loop via `ParallelIPCHandler.process_commands`. This coroutine monitors a designated directory for JSON command files containing instructions such as "interview" or "batch interview". Upon detecting a valid command, it executes the operation asynchronously against the live OASIS environment and writes responses to an output directory, enabling external orchestration without restarting the simulation infrastructure.