Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
- Avoid finalizer hangs when a forked child garbage-collects a non-idle inherited `:single_threaded` context
- Allow Ruby thread interrupts, process shutdown, and cross-thread `Context#dispose` to terminate busy `:single_threaded` JavaScript execution instead of hanging
- Make `Context#dispose` while an attached Ruby callback is active either terminate safely or raise instead of deadlocking
- Add `MiniRacer.pause(timeout:)` / `MiniRacer.resume` to quiesce MiniRacer globally, plus opt-in fork hooks built on that pause gate

- 0.21.2 - 11-06-2026
- Add `Context#perform_microtask_checkpoint` to synchronously drain the V8 microtask queue, useful for spec-compliant `dispatchEvent` sequencing inside Ruby callbacks
Expand Down
39 changes: 35 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -143,10 +143,41 @@ When using pre-fork `MiniRacer::Context` objects in `:single_threaded` mode,
ensure the process only forks while MiniRacer is quiescent: no thread may be
evaluating JavaScript, calling into a context, disposing/freeing a context,
running a Ruby callback from JavaScript, or otherwise using MiniRacer at the
instant of `fork`. In multi-threaded applications, guard all MiniRacer context
operations and the `fork` itself with the same application-level lock. Forking
while a MiniRacer operation is in progress can leave inherited pthread mutexes
in an unusable state in the child process.
instant of `fork`. Forking while a MiniRacer operation is in progress can leave
inherited pthread mutexes in an unusable state in the child process.

`MiniRacer.pause(timeout:)` is a process-global quiesce gate. It prevents new
MiniRacer operations from starting, waits for operations already in progress to
finish, and then keeps MiniRacer paused until `MiniRacer.resume` is called.
`timeout:` is in seconds; if MiniRacer cannot drain in time,
`MiniRacer::PauseTimeoutError` is raised and the pause is rolled back. Omitting
`timeout:` waits indefinitely, which is useful only when the caller knows active
JavaScript cannot get stuck.

```ruby
MiniRacer.pause(timeout: 5)
begin
pid = fork do
MiniRacer.resume # child: reset inherited pause state
# child process work
end
ensure
MiniRacer.resume # parent: release the pause
end
```

For normal Ruby forks you can install an opt-in `Process._fork` hook which uses
that same pause gate automatically:

```ruby
MiniRacer.install_fork_hooks!(timeout: 5)
```

The hook covers `Kernel#fork`, `Process.fork`, and `IO.popen("-")` on Rubies
that expose `Process._fork`. It intentionally does not cover `Process.daemon` or
raw native `fork(2)` calls from other C extensions. When the hook is installed,
do not call `MiniRacer.resume` again in the child block; the hook already resumes
in both parent and child before user child code runs.

If you want to ensure your application does not leak memory after fork either:

Expand Down
Loading
Loading