Fixrb

aw

Runs a Ruby block in a forked process.

What the block returns comes back to the parent, and what it raises is raised again there. What it does to the process stays in the child: mutations, globals, constants, the current directory, ENV, a crash. What it does outside, to files or to other processes, does not.

A pixel-art hero shouting “Aw, fork!” while his forked clone strikes a golem.
The forked clone, PID 12345, does the fighting: Ken keeps his 24 HP.
Version
1.0.0
Ruby
3.3 or later, with fork(2): CRuby on Linux, macOS and the BSDs
Dependencies
None
License
MIT
Used by
Fix 1.0

Install

Add gem "aw" to your Gemfile, or run gem install aw. Without fork(2), on Windows or JRuby, Aw.supported? answers false, and fork! and fork? raise NotImplementedError.

Usage

fork! returns what the block returned, and fork? tells whether the block returned rather than raised. The list changes in the child only:

require "aw"

list = ["foo"]

Aw.fork! { list << "bar" } # => ["foo", "bar"]
list                       # => ["foo"]

Aw.fork? { list.fetch(0) } # => true
Aw.fork? { list.fetch(1) } # => false

Errors

fork! raises again in the parent what the block raised, with its message, its backtrace and its cause. That includes the SystemExit of an exit or an abort, and the SignalException of a signal that Ruby turns into an exception, such as TERM: left uncaught, they end the parent as they would have ended the child.

fork! and fork? raise Aw::ChildError when the child delivers no result: a signal such as KILL ended it, the child called exit! or exec, or the block left with break, return or throw. The error’s status is the child’s Process::Status:

require "aw"

raised = begin
  Aw.fork! { Integer("forty-two") }
rescue ArgumentError => e
  e
end
raised.message # => "invalid value for Integer(): \"forty-two\""

failed = begin
  Aw.fork! { Process.kill(:KILL, Process.pid) }
rescue Aw::ChildError => e
  e
end
failed.status.signaled? # => true

The value or the exception travels through a pipe with Marshal, and comes back as a copy. When Marshal cannot dump it (a Proc, an IO, an object with singleton methods, an instance of an anonymous class, or anything that holds one) or the parent cannot load it (an instance of a class the parent does not know), fork! raises Aw::ChildError too. fork? transfers neither, so it answers even when they could not come back.

Limits

The README details the guarantees, and what else to know about fork: buffered output, fiber schedulers, macOS, SIGCHLD and SimpleCov.

Documentation