Changelog
Farce follows Semantic Versioning 2.0. Any public method on any public class or module documented in the README or via YARD and not declared private is part of the public API.
Overview
| Version | Date | Important Changes |
|---|---|---|
| 0.6.0 | 2026-11-04 | Performance improvements, better counters, and WeakCache |
| 0.5.0 | 2026-10-21 | Publishers, memoization, pool and queue resizing, and better concurrent-ruby compatibility |
| 0.4.1 | 2026-10-18 | Include improved io_uring support in prebuilt Linux binaries |
| 0.4.0 | 2026-10-16 | Introduce Farce::Backports |
| 0.3.0 | 2026-10-13 | Introduce Farce::Task and executor improvements |
| 0.2.0 | 2026-10-11 | Improvements for flags, vectors, and timer queues, and performance |
| 0.1.0 | 2026-10-09 | Initial release |
Release History
Farce 0.6.0 (2026-11-04)
Breaking Changes
CounterandLocal::Counterarithmetic methods (increment,add,decrement,subtract, andremove) now return the integer produced by the atomic update instead of the counter. Usenumber = counter.incrementto allocate sequence numbers without a separate read. Calls can no longer be chained.
New Features
Farce.dedupcan now deduplicateRegexp,Range,Data, andSortedSetinstances.Farce::Walker(and thusFarce.freeze_graph) correctly walkStructandRangeinstances now.- Add
Farce::Strict::WeakCache. It offers a subset of the API ofFarce::Strict::WeakValueMap, but without its performance penalty on CRuby 3.x and 4.0 (on other Rubies, including CRuby 4.1, it's just a tiny wrapper around the well-performingWeakValueMap).
Performance improvements
- Faster construction of Map and Vector instances.
- Speed up atoms and memoization.
Farce 0.5.0 (2026-10-21)
New Features
Publishers
Farce::Publisher broadcasts messages to multiple subscribers. It supports Ractors, ports, queues, atoms, counters, flags, collections, signals, other publishers, and callbacks.
publisher = Farce::Publisher.new
counter = Farce::Counter.new
queue = Farce::Queue.new
publisher.subscribe(counter)
publisher.subscribe(queue)
publisher.publish(:hello)
counter.value # => 1
queue.pop # => :hello
Memoization
Added Farce::Memoize for memoization with scopes, transfer modes, and weak values, including after freezing.
class MyClass
include Farce::Memoize
# Memoize #expensive_method, shared across Ractors
memoize def expensive_method = expensive_computation
# Memoize #method_with_unsharable_value for each Ractor separately
def method_with_unsharable_value = unsharable_computation
memoize :method_with_unsharable_value, scope: :ractor
end
Better concurrent-ruby Compatibility
- Added
Farce::Concurrent, with replacements forCountDownLatch,CyclicBarrier,Event,FiberLocalVar,ThreadLocalVar,LockLocalVar, andTuple, plus aliases for existing Farce classes. - Maps now support all public API methods of
Concurrent::Map. - Added
Counter#updateto atomically replace the value using a block. - Added
true?,false?,make_true, andmake_falseto flags.
Queue and Pool Tuning
- Queues, priority queues, timer queues, schedulers, and pools now support resizing.
- Pools now support tuning after construction. Increasing the minimum starts workers immediately. Excess workers retire when idle without interrupting running tasks.
- Added
parallel_pool_*configuration options for the shared CRuby pool used byFarce.in_parallel.
Miscellaneous
- Added
timeout:to executorscheduleandexecutemethods. - Added
Farce::Clock.remainingand refactored code to use it. - Vectors now accept an integer size in their constructors, filling the initial entries with
nil.
Bug Fixes
- Prevent waits from spinning when an unchanged value has non-reflexive equality, such as
Float::NAN, or overrides==,!=, orequal?. Predicates receive unwrapped values. - Prevent graph traversal from retrying indefinitely when a map value cannot be matched using ordinary equality.
- Fix vector wait timeout fallbacks on CRuby and TruffleRuby.
Other changes
- Added
pairing_heapto the priority queue benchmark and refreshed the published benchmark results. It is the fastest priority queue implementation written in pure Ruby.
Farce 0.4.1 (2026-10-18)
- Make sure prebuilt Linux binaries include liburing support.
- Use a Futex for efficient fiber synchronization if possible.
- Consistent
wrap_struct_nameacross the native extension for debugging.
Farce 0.4.0 (2026-10-16)
New Features
Ractor Backports
Farce::Backports makes newer Ractor APIs available on older Ruby versions:
- Added
Ractor#default_port,#join,#monitor,#unmonitor, and#valueon Ruby versions prior to 4.0. - Added
Ractor#alive?on Ruby versions prior to 4.1. - Added optional
timeout:support forRactor.select,Ractor#receive, andRactor::Port#receiveon Ruby versions prior to 4.1.
You can activate the backports for a lexical scope with using Farce::Backports, install them globally with Farce::Backports.install, or call methods on Farce::Backports directly.
Global installation also defines Ractor and Ractor::Port when they are not natively available.
using Farce::Backports
ractor = Farce::Ractor.new { 42 }
ractor.join
ractor.value # => 42
Ractor Improvements
Farce::Ractor.selectis now implemented on JRuby and TruffleRuby. It supports selecting from Ractors and ports, optional timeouts, and waiting without blocking a Fiber scheduler.- The Ractor polyfill now supports
alive?and follows Ruby 4.1'smonitornotification format when running on Ruby 4.1 or later.
Miscellaneous
- Added
set?andunset?predicates toFlagandLocal::Flag. - Added
Farce::NULL_PORT, a write-only port that discards messages and can be used to ignore Ractor exit notifications. - Added
Farce::System.ruby_version, which returns the current Ruby release as a version object without prerelease suffixes. It can be called from any Ractor. - Added
Farce::Extensionto easily define modules that can be used both as refinements or installed globally into existing classes, used byFarce::Backports.
Bug Fixes
- Work around a JRuby synchronization race that could lose a mutex wakeup and leave a scheduled Fiber blocked.
Other changes
- Reorganized the README into a shorter introduction with topic-specific guides in the docs directory.
- Updated compatibility documentation and tests for
ractor-tmvar0.4.0.
Farce 0.3.0 (2026-10-13)
New Features
Breaking Changes
Abstract::Scheduleris no longer a superclass ofPool. UseAbstract::Executorinstead.
Executor Improvements
Scheduler and Pool have the following improvements:
- They now support
try_schedule, which discards tasks and returnsnilwhen their internal queues are at capacity. - They now have
UnsharedandLocalvariants, running in a Ractor-local context (Pool will manage a Thread pool instead of a Ractor pool).
Creates a thread pool with a maximum size of 2 and a queue capacity of 10:
# A thread pool local to the current Ractor
thread_pool = Farce::Unshared::Pool.new(max_size: 2, capacity: 10)
# this should only run approximately 10 tasks on CRuby
500.times { thread_pool.try_schedule { puts "Task executed" } }
Tasks
A new class, Task, has been introduced to represent asynchronous tasks that can be scheduled and executed within Farce's executor framework,
executed manually, or handed off to any other executor supporting procs or callable objects.
task = Farce::Task.schedule do
puts "Task started"
sleep 1
:success
ensure
puts "Task finished"
end
task.alive? # => true
task.value # => :success (blocks until the task completes)
Bug Fixes
- Calling
wait_*methods on atoms on JRuby no longer blocks a fiber scheduler.
Farce 0.2.0 (2026-10-11)
New Features
Waiting on Flags
You can now wait for a Farce::Flag or Farce::Local::Flag to change its value:
flag = Farce::Flag.new
Thread.new do
flag.wait_until_set
puts "Flag has been activated!"
end
flag.set
Timer Queue Entries
TimerQueue#push and TimerQueue#try_push now return a TimerQueue::Entry object on a successful push.
This allows you to cancel a scheduled value using the returned entry:
queue = Farce::TimerQueue.new
entry = queue.push(:work, delay: 60)
entry.delete # => true
Expanded Vector Functionality
Improved Array compatibility for Vector, Strict::Vector, Unshared::Vector, and Local::Vector:
- Added
to_ary,delete, anddelete_if. - Added range and start/length indexing, slice assignment, multiple-value
pushandappend, andpop(count). - Added
shift,unshift,prepend,insert,delete_at,slice!,replace, andfill. - Added
map!,collect!,keep_if,select!,filter!,reject!,compact!, anduniq!. - Added
reverse!,rotate!,shuffle!,sort!,sort_by!, andflatten!. - Added
flatten,transpose,product,combination,repeated_combination,permutation, andrepeated_permutation. - Added lexicographic
<=>. Equality and hashing continue to use object identity.
Miscellaneous
- Unshareable objects may now be frozen.
- Trying to make an unshareable object shareable raises a better error message now (
Ractor::Errorwith a descriptive message, instead of aNoMethodError). - Added a new transfer mode:
:marshal, which will copy objects between Ractors using Ruby's Marshal serialization.
Bug Fixes
- Improve compatibility with other gems modifying
Kernel#require, work around an issue with Ruby 4.0's runtime ractor require patch and alias methods chains not working nicely withModule#prepend.
Performance Improvements
Rework CRuby native extension
Incorporated the advice from "Tips for updating Ruby C extensions for efficiently using Ruby 4 VM APIs".
With this change, all long-living Farce objects can be compacted and moved to old GC generations. This was not the case previously for maps and vectors that had snapshots taken of them (either for iteration or for transactions). These ended up being permanently kept in young generations. A long-living map or vector would therefore cause frequent GC runs to be quite expensive.
| Operation | Before | After | Change |
|---|---|---|---|
Map#each over 1024 entries |
34.1 μs | 35.2 μs | 3% slower |
| Transaction with a map with 16 entries | 25.2 μs | 25.3 μs | 0.4% slower |
| Transaction with a map with 1024 entries | 26.7 μs | 29.2 μs | 9% slower |
| Transaction with a vector with 10,000 entries | 26.1 μs | 29.4 μs | 13% slower |
| GC while keeping a vector with 10,000 entries alive | 67.6 μs | 28.6 μs | 58% faster |
Beyond these specific cases:
- Other iteration methods will have a similar performance impact to
Map#each(for instance,Map#transform_values). - Methods not involving iterations, like reading or writing a specific map entry, do not show any measurable performance impact.
Measurements were taken on CRuby 4.0.7 (arm64-darwin23) with YJIT enabled. Measurements taken against Ruby's main branch show similar results.
Other changes
- Avoid circular require warnings when automatically loading third-party integrations.
- Do not install a require hook if automatic integration loading is disabled.
Farce 0.1.0 (2026-10-09)
Initial release.