Skip to content

Commit

Permalink
Fix defining object finalizer in Ruby 3.1 to reference a UUID string
Browse files Browse the repository at this point in the history
In Ruby 3.1, accessing callback_queue from a finalizer causes an "undefined
local variable or method `callback_queue' for FiniteMachine::Observer:Class
(NameError)", and callback_queue is never cleaned up. However, making the
finalizer an instance method rather than a class method throws a warning
about the improper use of finalizers. Hence, using the UUID string as
an object to check before it is destroyed by garbage collection for when
to call the finalizer proc.

---------

Co-authored-by: Piotr Murach <pmurach@gmail.com>
  • Loading branch information
vkononov and piotrmurach committed Aug 28, 2023
1 parent 8b1e421 commit 11dd7f1
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 19 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Change Log

### Fixed
* Fix defining object finalizer in Ruby 3.1 to reference a UUID string
by Vadim Kononov(@vkononov)

## [v0.14.0] - 2020-09-12

### Added
Expand Down
50 changes: 31 additions & 19 deletions lib/finite_machine/observer.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# frozen_string_literal: true

require "securerandom"

require_relative "async_call"
require_relative "callable"
require_relative "hook_event"
Expand All @@ -13,20 +15,6 @@ module FiniteMachine
class Observer < GenericDSL
include Safety

# Clean up callback queue
#
# @api private
def self.cleanup_callback_queue
proc do
begin
if callback_queue.alive?
callback_queue.shutdown
end
rescue MessageQueueDeadError
end
end
end

# The current state machine
attr_reader :machine

Expand All @@ -44,11 +32,6 @@ def initialize(machine)
@hooks = Hooks.new

@machine.subscribe(self)
ObjectSpace.define_finalizer(self, self.class.cleanup_callback_queue)
end

def callback_queue
@callback_queue ||= MessageQueue.new
end

# Evaluate in current context
Expand Down Expand Up @@ -204,6 +187,35 @@ def defer(callable, trans_event, *data)
callback_queue << async_call
end

# Get an existing callback queue or create a new one
#
# @return [FiniteMachine::MessageQueue]
#
# @api private
def callback_queue
@callback_queue ||= MessageQueue.new.tap do
@queue_id = SecureRandom.uuid
ObjectSpace.define_finalizer(@queue_id, proc do
cleanup_callback_queue
end)
end
end

# Clean up the callback queue
#
# @return [Boolean, nil]
#
# @api private
def cleanup_callback_queue
ObjectSpace.undefine_finalizer(@queue_id) if @queue_id
return unless @callback_queue && callback_queue.alive?

begin
callback_queue.shutdown
rescue MessageQueueDeadError
end
end

# Create callable instance
#
# @api private
Expand Down

0 comments on commit 11dd7f1

Please sign in to comment.