Expand documentation; fix markup.

This commit is contained in:
Patrick Mahoney 2011-12-12 22:02:00 -06:00
parent 27e8aeb4c4
commit d102786090
6 changed files with 57 additions and 20 deletions

View File

@ -8,17 +8,28 @@ module ProcessShared
include ProcessShared::PSem include ProcessShared::PSem
public public
# Generate a name for a semaphore. # Generate a name for a semaphore. If +name+ is given, it is used
# as the name (and so a semaphore could be shared by arbitrary
# processes not forked from one another). Otherwise, a name is
# generated containing +middle+ and the process id.
#
# @param [String] middle arbitrary string used in the middle
# @param [String] name if given, used as the name
# @return [String] name, or the generated name
def self.gen_name(middle, name = nil) def self.gen_name(middle, name = nil)
if name if name
name name
else else
@count ||= 0 @count ||= 0
@count += 1 @count += 1
"ps-#{middle}-#{Process.pid}-#{@count}" "ps-#{middle}-#{::Process.pid}-#{@count}"
end end
end end
# Make a Proc suitable for use as a finalizer that will call
# +psem_unlink+ on +name+ and ignore system errors.
#
# @return [Proc] a finalizer
def self.make_finalizer(name) def self.make_finalizer(name)
proc { ProcessShared::PSem.psem_unlink(name, nil) } proc { ProcessShared::PSem.psem_unlink(name, nil) }
end end

View File

@ -2,12 +2,15 @@ require 'process_shared/psem'
require 'process_shared/semaphore' require 'process_shared/semaphore'
module ProcessShared module ProcessShared
# BoundedSemaphore is identical to Semaphore except that its value
# is not permitted to rise above a maximum. When the value is at
# the maximum, calls to #post will have no effect.
class BoundedSemaphore < Semaphore class BoundedSemaphore < Semaphore
# With no associated block, open is a synonym for # With no associated block, open is a synonym for
# Semaphore.new. If the optional code block is given, it will be # Semaphore.new. If the optional code block is given, it will be
# passed `sem` as an argument, and the Semaphore object will # passed +sem+ as an argument, and the Semaphore object will
# automatically be closed when the block terminates. In this # automatically be closed when the block terminates. In this
# instance, Semaphore.open returns the value of the block. # instance, BoundedSemaphore.open returns the value of the block.
# #
# @param [Integer] value the initial semaphore value # @param [Integer] value the initial semaphore value
# @param [String] name not currently supported # @param [String] name not currently supported
@ -15,9 +18,9 @@ module ProcessShared
new(maxvalue, value, name).with_self(&block) new(maxvalue, value, name).with_self(&block)
end end
# Create a new semaphore with initial value `value`. After # Create a new semaphore with initial value +value+. After
# Kernel#fork, the semaphore will be shared across two (or more) # {Kernel#fork}, the semaphore will be shared across two (or more)
# processes. The semaphore must be closed with #close in each # processes. The semaphore must be closed with {#close} in each
# process that no longer needs the semaphore. # process that no longer needs the semaphore.
# #
# (An object finalizer is registered that will close the semaphore # (An object finalizer is registered that will close the semaphore

View File

@ -4,12 +4,27 @@ require 'process_shared/shared_memory'
require 'process_shared/process_error' require 'process_shared/process_error'
module ProcessShared module ProcessShared
# This Mutex class is implemented as a BoundedSemaphore with a
# maximum value of 1. Additionally, the locking process is tracked,
# and {ProcessError} is raised if either {#unlock} is called by a
# process different from the locking process, or if {#lock} is
# called while the process already holds the lock (i.e. the mutex is
# not re-entrant). This tracking is not without performance cost,
# of course (current implementation uses an additional
# {BoundedSemaphore} and {SharedMemory} segment).
#
# The API is intended to be identical to the {::Mutex} in the core
# Ruby library.
#
# TODO: the core Ruby api has no #close method, but this Mutex must
# release its {BoundedSemaphore} and {SharedMemory} resources. For
# now, rely on the object finalizers of those objects...
class Mutex class Mutex
include WithSelf # include WithSelf
def self.open(&block) # def self.open(&block)
new.with_self(&block) # new.with_self(&block)
end # end
def initialize def initialize
@internal_sem = BoundedSemaphore.new(1) @internal_sem = BoundedSemaphore.new(1)
@ -73,6 +88,8 @@ module ProcessShared
# Acquire the lock, yield the block, then ensure the lock is # Acquire the lock, yield the block, then ensure the lock is
# unlocked. # unlocked.
#
# @return [Object] the result of the block
def synchronize def synchronize
lock lock
begin begin

View File

@ -2,9 +2,9 @@
module ProcessShared module ProcessShared
module PosixCall module PosixCall
# Replace methods in `syms` with error checking wrappers that # Replace methods in +syms+ with error checking wrappers that
# invoke the original method and raise a SystemCallError with the # invoke the original method and raise a {SystemCallError} with
# current errno if the return value is an error. # the current errno if the return value is an error.
# #
# Errors are detected if the block returns true when called with # Errors are detected if the block returns true when called with
# the original method's return value. # the original method's return value.

View File

@ -5,7 +5,7 @@ module ProcessShared
class Semaphore < AbstractSemaphore class Semaphore < AbstractSemaphore
# With no associated block, open is a synonym for # With no associated block, open is a synonym for
# Semaphore.new. If the optional code block is given, it will be # Semaphore.new. If the optional code block is given, it will be
# passed `sem` as an argument, and the Semaphore object will # passed +sem+ as an argument, and the Semaphore object will
# automatically be closed when the block terminates. In this # automatically be closed when the block terminates. In this
# instance, Semaphore.open returns the value of the block. # instance, Semaphore.open returns the value of the block.
# #
@ -15,7 +15,7 @@ module ProcessShared
new(value, name).with_self(&block) new(value, name).with_self(&block)
end end
# Create a new semaphore with initial value `value`. After # Create a new semaphore with initial value +value+. After
# Kernel#fork, the semaphore will be shared across two (or more) # Kernel#fork, the semaphore will be shared across two (or more)
# processes. The semaphore must be closed with #close in each # processes. The semaphore must be closed with #close in each
# process that no longer needs the semaphore. # process that no longer needs the semaphore.
@ -33,7 +33,7 @@ module ProcessShared
end end
# Decrement the value of the semaphore. If the value is zero, # Decrement the value of the semaphore. If the value is zero,
# wait until another process increments via #post. # wait until another process increments via {#post}.
def wait def wait
psem_wait(sem, err) psem_wait(sem, err)
end end
@ -44,7 +44,8 @@ module ProcessShared
psem_post(sem, err) psem_post(sem, err)
end end
# Get the current value of the semaphore. # Get the current value of the semaphore. Raises {Errno::NOTSUP} on
# platforms that don't support this (e.g. Mac OS X).
# #
# @return [Integer] the current value of the semaphore. # @return [Integer] the current value of the semaphore.
def value def value
@ -53,6 +54,11 @@ module ProcessShared
int.get_int(0) int.get_int(0)
end end
# Release the resources associated with this semaphore. Calls to
# other methods are undefined after {#close} has been called.
#
# Close must be called when the semaphore is no longer needed. An
# object finalizer will close the semaphore as a last resort.
def close def close
psem_close(sem, err) psem_close(sem, err)
end end

View File

@ -2,8 +2,8 @@ module ProcessShared
module WithSelf module WithSelf
# With no associated block, return self. If the optional code # With no associated block, return self. If the optional code
# block is given, it will be passed `self` as an argument, and the # block is given, it will be passed `self` as an argument, and the
# self object will automatically be closed (by invoking `close` on # self object will automatically be closed (by invoking +close+ on
# `self`) when the block terminates. In this instance, value of # +self+) when the block terminates. In this instance, value of
# the block is returned. # the block is returned.
def with_self def with_self
if block_given? if block_given?