Add posix-y compatibility extension class over Mach::Semaphore.

This commit is contained in:
Patrick Mahoney 2012-02-01 21:36:15 -06:00
parent 680527cbab
commit 8158666e90
3 changed files with 133 additions and 2 deletions

View File

@ -0,0 +1,93 @@
require 'set'
require 'mach'
module ProcessShared
module Mach
include ::Mach
# The set of ports that should be shared to forked child
# processes.
#
# FIXME: protect with (original ruby) mutex?
def self.shared_ports
@shared_ports ||= Set.new
end
def self.after_fork_child
parent_port = Task.self.get_bootstrap_port
# give parent permission to send to child's task port
Task.self.copy_send(parent_port)
# create a second port and give the parent permission to send
port = Port.new
port.insert_right(:make_send)
port.copy_send(parent_port)
# parent copies sem, mutex port permissions directly to child
# task port
# wait for parent to send orig bootstrap port
orig_bootstrap = port.receive_right
Task.self.set_special_port(:bootstrap, orig_bootstrap)
end
def self.after_fork_parent(port)
child_task_port = port.receive_right
shared_ports.each do |p|
p.insert_right(:copy_send, :ipc_space => child_task_port)
end
child_port = port.receive_right
::Mach::bootstrap_port.copy_send(child_port)
end
end
end
module Kernel
# Override to call Process::fork.
def fork(*args, &block)
Process.fork(*args, &block)
end
end
module Process
class << self
unless respond_to? :__mach_original_fork__
alias_method :__mach_original_fork__, :fork
end
# Override to first copy all shared ports (semaphores, etc.) from
# parent process to child process.
def fork
# make a port for receiving message from child
port = Mach::Port.new
port.insert_right(:make_send)
Mach::Task.self.set_bootstrap_port(port)
if block_given?
pid = __mach_original_fork__ do
ProcessShared::Mach.after_fork_child
yield
end
ProcessShared::Mach.after_fork_parent(port)
pid
else
if pid = __mach_original_fork__
ProcessShared::Mach.after_fork_parent(port)
pid
else
ProcessShared::Mach.after_fork_child
nil
end
end
end
end
end
require 'mach/time_spec'
require 'process_shared/time_spec'
# Monkey patch to add #add_seconds! method
Mach::TimeSpec.send(:include, ProcessShared::TimeSpec)

View File

@ -0,0 +1,39 @@
require 'mach'
require 'mach/error'
require 'process_shared/mach'
module ProcessShared
module Mach
# Extends ::Mach::Semaphore to be compatible with ProcessShared::Semaphore
class Semaphore < ::Mach::Semaphore
include ProcessShared::Semaphore
def initialize(value = 1)
super(:value => value)
ProcessShared::Mach.shared_ports.add self
end
def try_wait(timeout = nil)
secs = timeout ? timeout : 0
begin
# TODO catch and convert exceptions...
timedwait(secs)
rescue Mach::Error::OPERATION_TIMED_OUT => e
klass = secs == 0 ? Errno::EAGAIN : Errno::ETIMEDOUT
raise klass, e.message
end
end
alias_method :post, :signal
def value
raise Errno::ENOTSUP
end
def close
# TODO
end
end
end
end

View File

@ -98,14 +98,13 @@ module ProcessShared
it 'returns after waiting if another processes posts' do it 'returns after waiting if another processes posts' do
Semaphore.open(0) do |sem| Semaphore.open(0) do |sem|
start = Time.now.to_f
pid = fork do pid = fork do
sleep 0.01 sleep 0.01
sem.post sem.post
Kernel.exit! Kernel.exit!
end end
start = Time.now.to_f
sem.try_wait(0.1) sem.try_wait(0.1)
(Time.now.to_f - start).must be_lt(0.1) (Time.now.to_f - start).must be_lt(0.1)