---
title: Execution, faults, and lifetimes
description: Run schedules serially or in parallel, and handle cancellation and failure.
sidebar:
  order: 11
---

## Serial and parallel

Serial execution is the default. `WorldBuilder.UseParallelExecution(maxConcurrency)` enables the world-owned reusable worker pool. The positive limit includes the owner thread.

A batch with one runnable work item executes on the calling thread. A partitioned system can produce multiple work items.

After a parallel batch, each worker spins for up to 50 µs before it blocks, and the calling thread spins for up to 50 µs while it waits for workers. A parallel batch that starts inside that window wakes no thread. A single-item batch makes spinning workers block at once, so they do not compete with the calling thread, and the next parallel batch wakes them. The spinning uses CPU time between closely spaced parallel batches. A pool whose limit exceeds `Environment.ProcessorCount` blocks without spinning.

A bound schedule borrows this pool, so disposing one schedule does not stop other schedules. Resource and component conflicts determine which systems can run together, and `Partitioned = true` on `[Each]` or `[EachBlock]` opts eligible systems into [row-range partitioning](/docs/guides/systems#partitioning).

```csharp
using var world = new WorldBuilder()
    .AddModule(GameEcs.Module)
    .UseParallelExecution(maxConcurrency: 4)
    .Build();
```

:::note
Parallel dispatch has material overhead on tiny workloads. Measure before enabling it. See [Performance](/docs/performance).
:::

## Running and cancelling

`schedule.Run(CancellationToken)` completes synchronously. Cancellation stops new work between systems or row ranges. Active user code must return before join completes.

## Faults

The owner reports execution failures after active workers join. `AggregateException` contains `SystemExecutionException` entries with the instance, system name, and row range (`FirstRow`, `RowCount`).

Failure or cancellation sets `IsFaulted`. Completed writes, local changes, message cursors, and earlier command playback remain. Unapplied commands are discarded.

`Reset()` acknowledges partial effects and permits another run. It does not retry automatically. Inspect the partial effects first. Disposal is allowed after a fully joined failure without resetting first.

## Ownership rules

- World and schedule control require the world's owner thread.
- Concurrent or nested runs against one world, and disposal during execution, are rejected.
- Every bound schedule must be disposed before its world.
- World disposal joins its workers before releasing native storage and owned resources.

## What the compiler cannot prove

Default or expired execution tokens fail validation. `scoped` and ref-struct signatures restrict escape, but they do not make captured mutable statics, unsafe pointers, or external object graphs safe for parallel use. Those remain application responsibilities.
