Async::SafeSourceAsyncSafeMonitor

class Monitor

The core monitoring implementation using TracePoint.

This class tracks object ownership across fibers, detecting when an object is accessed from a different fiber than the one that originally created or accessed it.

The monitor uses a TracePoint on :call events to track all method calls, and maintains a registry of which fiber "owns" each object. Uses weak references to avoid preventing garbage collection of tracked objects.

Definitions

def initialize

Initialize a new monitor instance.

Implementation

def initialize
	@owners = ObjectSpace::WeakMap.new
	@mutex = Thread::Mutex.new
	@trace_point = nil
end

def enable!

Enable the monitor by activating the TracePoint.

Implementation

def enable!
	@trace_point ||= TracePoint.trace(:call, &method(:check_access))
end

def disable!

Disable the monitor by deactivating the TracePoint.

Implementation

def disable!
	if trace_point = @trace_point
		@trace_point = nil
		trace_point.disable
	end
end

def transfer(*objects)

Explicitly transfer ownership of objects to the current fiber.

Signature

parameter objects Array(Object)

The objects to transfer.

Implementation

def transfer(*objects)
	@mutex.synchronize do
		current = Fiber.current
		
		objects.each do |object|
			@owners[object] = current
		end
	end
end

def check_access(trace_point)

Check if the current access is allowed or constitutes a violation.

Signature

parameter trace_point TracePoint

The trace point containing access information.

Implementation

def check_access(trace_point)
	object = trace_point.self
	
	# Skip tracking class/module methods:
	return if object.is_a?(Class) || object.is_a?(Module)
	
	# Skip frozen objects:
	return if object.frozen?
	
	method = trace_point.method_id
	klass = trace_point.defined_class
	
	# Check the object's actual class:
	klass = object.class
	
	# Check if the class or method is marked as async-safe:
	if klass.async_safe?(method)
		return
	end
	
	# Track ownership:
	current = Fiber.current
	
	@mutex.synchronize do
		if owner = @owners[object]
			# Violation if accessed from different fiber:
			if owner != current
				raise ViolationError.new(
					target: object,
					method: method,
					owner: owner,
					current: current,
				)
			end
		else
			# First access - record owner:
			@owners[object] = current
		end
	end
end