diff --git a/lib/process_shared/abstract_semaphore.rb b/lib/process_shared/abstract_semaphore.rb index 31662c0..20e3077 100644 --- a/lib/process_shared/abstract_semaphore.rb +++ b/lib/process_shared/abstract_semaphore.rb @@ -8,17 +8,28 @@ module ProcessShared include ProcessShared::PSem 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) if name name else @count ||= 0 @count += 1 - "ps-#{middle}-#{Process.pid}-#{@count}" + "ps-#{middle}-#{::Process.pid}-#{@count}" 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) proc { ProcessShared::PSem.psem_unlink(name, nil) } end diff --git a/lib/process_shared/bounded_semaphore.rb b/lib/process_shared/bounded_semaphore.rb index 812e33a..6131679 100644 --- a/lib/process_shared/bounded_semaphore.rb +++ b/lib/process_shared/bounded_semaphore.rb @@ -2,12 +2,15 @@ require 'process_shared/psem' require 'process_shared/semaphore' 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 # With no associated block, open is a synonym for # 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 - # 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 [String] name not currently supported @@ -15,9 +18,9 @@ module ProcessShared new(maxvalue, value, name).with_self(&block) end - # Create a new semaphore with initial value `value`. After - # Kernel#fork, the semaphore will be shared across two (or more) - # processes. The semaphore must be closed with #close in each + # Create a new semaphore with initial value +value+. After + # {Kernel#fork}, the semaphore will be shared across two (or more) + # processes. The semaphore must be closed with {#close} in each # process that no longer needs the semaphore. # # (An object finalizer is registered that will close the semaphore diff --git a/lib/process_shared/mutex.rb b/lib/process_shared/mutex.rb index e93cc1e..9956898 100644 --- a/lib/process_shared/mutex.rb +++ b/lib/process_shared/mutex.rb @@ -4,12 +4,27 @@ require 'process_shared/shared_memory' require 'process_shared/process_error' 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 - include WithSelf + # include WithSelf - def self.open(&block) - new.with_self(&block) - end + # def self.open(&block) + # new.with_self(&block) + # end def initialize @internal_sem = BoundedSemaphore.new(1) @@ -73,6 +88,8 @@ module ProcessShared # Acquire the lock, yield the block, then ensure the lock is # unlocked. + # + # @return [Object] the result of the block def synchronize lock begin diff --git a/lib/process_shared/posix_call.rb b/lib/process_shared/posix_call.rb index 5d1bc34..e3ed9b7 100644 --- a/lib/process_shared/posix_call.rb +++ b/lib/process_shared/posix_call.rb @@ -2,9 +2,9 @@ module ProcessShared module PosixCall - # Replace methods in `syms` with error checking wrappers that - # invoke the original method and raise a SystemCallError with the - # current errno if the return value is an error. + # Replace methods in +syms+ with error checking wrappers that + # invoke the original method and raise a {SystemCallError} with + # the current errno if the return value is an error. # # Errors are detected if the block returns true when called with # the original method's return value. diff --git a/lib/process_shared/semaphore.rb b/lib/process_shared/semaphore.rb index 7c07371..0ed712c 100644 --- a/lib/process_shared/semaphore.rb +++ b/lib/process_shared/semaphore.rb @@ -5,7 +5,7 @@ module ProcessShared class Semaphore < AbstractSemaphore # With no associated block, open is a synonym for # 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 # instance, Semaphore.open returns the value of the block. # @@ -15,7 +15,7 @@ module ProcessShared new(value, name).with_self(&block) 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) # processes. The semaphore must be closed with #close in each # process that no longer needs the semaphore. @@ -33,7 +33,7 @@ module ProcessShared end # 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 psem_wait(sem, err) end @@ -44,7 +44,8 @@ module ProcessShared psem_post(sem, err) 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. def value @@ -53,6 +54,11 @@ module ProcessShared int.get_int(0) 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 psem_close(sem, err) end diff --git a/lib/process_shared/with_self.rb b/lib/process_shared/with_self.rb index 80d2a85..3e20b05 100644 --- a/lib/process_shared/with_self.rb +++ b/lib/process_shared/with_self.rb @@ -2,8 +2,8 @@ module ProcessShared module WithSelf # With no associated block, return self. If the optional code # block is given, it will be passed `self` as an argument, and the - # self object will automatically be closed (by invoking `close` on - # `self`) when the block terminates. In this instance, value of + # self object will automatically be closed (by invoking +close+ on + # +self+) when the block terminates. In this instance, value of # the block is returned. def with_self if block_given?