Skip to content

Commit de5f729

Browse files
committed
Add schedule_inline documentation and changelog
- Document erlang.schedule_inline() in asyncio.md - Add comparison table (schedule_inline vs schedule_py) - Update "When to Use Each Pattern" table - Add v2.3.0 changelog entry
1 parent f957d5d commit de5f729

File tree

2 files changed

+51
-2
lines changed

2 files changed

+51
-2
lines changed

CHANGELOG.md

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,20 @@
11
# Changelog
22

3-
## 2.2.0 (2026-03-13)
3+
## 2.2.0 (unreleased)
44

55
### Added
66

7+
- **Inline Continuation API** - High-performance scheduling without Erlang messaging
8+
- `erlang.schedule_inline(module, func, args, kwargs)` - Chain Python calls via `enif_schedule_nif()`
9+
- ~3x faster than `schedule_py` for tight loops (bypasses gen_server messaging)
10+
- Captures caller's globals/locals for correct namespace resolution with subinterpreters
11+
- `InlineScheduleMarker` type returned, must be returned from handler
12+
- See [Scheduling API docs](docs/asyncio.md#explicit-scheduling-api)
13+
14+
- **Inline Continuation Benchmark** - Performance comparison
15+
- `bench_schedule_inline` in `examples/benchmark.erl`
16+
- Compares `schedule_inline` vs `schedule_py` throughput
17+
718
- **Process-Bound Python Environments** - Each Erlang process gets an isolated Python namespace
819
- `py:get_local_env/1` - Get or create a process-local Python environment
920
- Variables defined via `py:exec()` persist across calls within the same Erlang process

docs/asyncio.md

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1063,6 +1063,43 @@ This is useful for:
10631063
- Allowing other Erlang processes to run
10641064
- Cooperative multitasking
10651065

1066+
#### erlang.schedule_inline(module, func, args=None, kwargs=None)
1067+
1068+
Release the dirty scheduler and continue by calling a Python function via `enif_schedule_nif()` - bypassing Erlang messaging entirely.
1069+
1070+
```python
1071+
import erlang
1072+
1073+
def process_chunk(data, offset=0, results=None):
1074+
"""Process data in chunks with inline continuations."""
1075+
if results is None:
1076+
results = []
1077+
1078+
chunk_end = min(offset + 100, len(data))
1079+
for i in range(offset, chunk_end):
1080+
results.append(transform(data[i]))
1081+
1082+
if chunk_end < len(data):
1083+
# Continue inline - no Erlang messaging overhead
1084+
return erlang.schedule_inline(
1085+
'__main__', 'process_chunk',
1086+
args=[data, chunk_end, results]
1087+
)
1088+
1089+
return results
1090+
```
1091+
1092+
**When to use `schedule_inline` vs `schedule_py`:**
1093+
1094+
| Aspect | `schedule_inline` | `schedule_py` |
1095+
|--------|-------------------|---------------|
1096+
| Flow | Python -> NIF -> enif_schedule_nif -> Python | Python -> NIF -> Erlang message -> Python |
1097+
| Speed | ~3x faster for tight loops | Slower due to messaging |
1098+
| Use case | Pure Python chains, no Erlang interaction | When you need Erlang messaging between steps |
1099+
| Overhead | Minimal (direct NIF continuation) | Higher (gen_server call) |
1100+
1101+
**Important:** `schedule_inline` captures the caller's globals/locals, ensuring correct namespace resolution even with subinterpreters.
1102+
10661103
#### erlang.consume_time_slice(percent)
10671104

10681105
Check if the NIF time slice is exhausted. Returns `True` if you should yield, `False` if more time remains.
@@ -1099,7 +1136,8 @@ def long_computation(items, start_idx=0):
10991136
|---------|----------|-----------------|
11001137
| `erlang.call()` | Quick operations or callbacks that use `receive` | Held (unless callback suspends via `receive`) |
11011138
| `erlang.schedule()` | Need to call Erlang callback and always release scheduler | Released |
1102-
| `erlang.schedule_py()` | Long Python computation, cooperative scheduling | Released |
1139+
| `erlang.schedule_py()` | Long Python computation, need Erlang interaction between steps | Released |
1140+
| `erlang.schedule_inline()` | Tight Python loops, no Erlang interaction needed (~3x faster) | Released |
11031141
| `consume_time_slice()` | Fine-grained control over yielding | N/A (checks time slice) |
11041142

11051143
### Example: Cooperative Long-Running Task

0 commit comments

Comments
 (0)