Move README to markdown; add yardopts to match.

This commit is contained in:
Patrick Mahoney 2013-12-30 22:59:51 -06:00
parent e1a4dec198
commit 36405307fd
4 changed files with 113 additions and 100 deletions

1
.yardopts Normal file
View File

@ -0,0 +1 @@
-m markdown -M redcarpet

110
README.md Normal file
View File

@ -0,0 +1,110 @@
process_shared
==============
Concurrency primitives that may be used in a cross-process way to
coordinate share memory between processes.
FFI is used to access POSIX semaphore on Linux or Mach semaphores on
Mac. Atop these semaphores are implemented ProcessShared::Semaphore,
ProcessShared::Mutex. POSIX shared memory is used to implement
ProcessShared::SharedMemory.
On Linux, POSIX semaphores support `sem_timedwait()` which can wait on
a semaphore but stop waiting after a timeout.
Mac OS X's implementation of POSIX semaphores does not support
timeouts. But, the Mach layer in Mac OS X has its own semaphores that
do support timeouts. Thus, process_shared implements a moderate
subset of the Mach API, which is quite a bit different from POSIX.
Namely, semaphores created in one process are not available in child
processes created via `fork()`. Mach does provide the means to copy
capabilities between tasks (Mach equivalent to processes).
process_shared overrides Ruby's `fork` methods so that semaphores are
copied from parent to child to emulate the POSIX behavior.
This is an incomplete work in progress.
License
-------
MIT
Install
-------
Install the gem with:
gem install process_shared
Usage
-----
```ruby
require 'process_shared'
mutex = ProcessShared::Mutex.new
mem = ProcessShared::SharedMemory.new(:int) # extends FFI::Pointer
mem.put_int(0, 0)
pid1 = fork do
puts "in process 1 (#{Process.pid})"
10.times do
sleep 0.01
mutex.synchronize do
value = mem.get_int(0)
sleep 0.01
puts "process 1 (#{Process.pid}) incrementing"
mem.put_int(0, value + 1)
end
end
end
pid2 = fork do
puts "in process 2 (#{Process.pid})"
10.times do
sleep 0.01
mutex.synchronize do
value = mem.get_int(0)
sleep 0.01
puts "process 2 (#{Process.pid}) decrementing"
mem.put_int(0, value - 1)
end
end
end
Process.wait(pid1)
Process.wait(pid2)
puts "value should be zero: #{mem.get_int(0)}"
```
Transfer Objects Across Processes
---------------------------------
```ruby
# allocate a sufficient memory block
mem = ProcessShared::SharedMemory.new(1024)
# sub process can write (serialize) object to memory (with bounds checking)
pid = fork do
mem.write_object(['a', 'b'])
end
Process.wait(pid)
# parent process can read the object back (synchronizing access
# with a Mutex left as an excercie to reader)
mem.read_object.must_equal ['a', 'b']
```
Todo
----
* Test ConditionVariable
* Implement optional override of core Thread/Mutex classes
* Extend to win32? (See Python's processing library)
* Add finalizer to Mutex? (finalizer on Semaphore objects may be enough) or a method to
explicitly close and release resources?
* Test semantics of crashing processes who still hold locks, etc.
* Is SharedArray with Enumerable mixing sufficient Array-like interface?

View File

@ -1,99 +0,0 @@
== Description
Concurrency primitives that may be used in a cross-process way to
coordinate share memory between processes.
FFI is used to access POSIX semaphore on Linux or Mach semaphores on
Mac. Atop these semaphores are implemented ProcessShared::Semaphore,
ProcessShared::Mutex. POSIX shared memory is used to implement
ProcessShared::SharedMemory.
On Linux, POSIX semaphores support <tt>sem_timedwait()</tt> which can wait on
a semaphore but stop waiting after a timeout.
Mac OS X's implementation of POSIX semaphores does not support
timeouts. But, the Mach layer in Mac OS X has its own semaphores that
do support timeouts. Thus, process_shared implements a moderate
subset of the Mach API, which is quite a bit different from POSIX.
Namely, semaphores created in one process are not available in child
processes created via <tt>fork()</tt>. Mach does provide the means to copy
capabilities between tasks (Mach equivalent to processes).
process_shared overrides Ruby's <tt>fork</tt> methods so that semaphores are
copied from parent to child to emulate the POSIX behavior.
This is an incomplete work in progress.
== License
MIT
== Install
Install the gem with:
gem install process_shared
== Usage
require 'process_shared'
mutex = ProcessShared::Mutex.new
mem = ProcessShared::SharedMemory.new(:int) # extends FFI::Pointer
mem.put_int(0, 0)
pid1 = fork do
puts "in process 1 (#{Process.pid})"
10.times do
sleep 0.01
mutex.synchronize do
value = mem.get_int(0)
sleep 0.01
puts "process 1 (#{Process.pid}) incrementing"
mem.put_int(0, value + 1)
end
end
end
pid2 = fork do
puts "in process 2 (#{Process.pid})"
10.times do
sleep 0.01
mutex.synchronize do
value = mem.get_int(0)
sleep 0.01
puts "process 2 (#{Process.pid}) decrementing"
mem.put_int(0, value - 1)
end
end
end
Process.wait(pid1)
Process.wait(pid2)
puts "value should be zero: #{mem.get_int(0)}"
== Transfer Objects Across Processes
# allocate a sufficient memory block
mem = ProcessShared::SharedMemory.new(1024)
# sub process can write (serialize) object to memory (with bounds checking)
pid = fork do
mem.write_object(['a', 'b'])
end
Process.wait(pid)
# parent process can read the object back (synchronizing access
# with a Mutex left as an excercie to reader)
mem.read_object.must_equal ['a', 'b']
== Todo
* Test ConditionVariable
* Implement optional override of core Thread/Mutex classes
* Extend to win32? (See Python's processing library)
* Add finalizer to Mutex? (finalizer on Semaphore objects may be enough) or a method to
explicitly close and release resources?
* Test semantics of crashing processes who still hold locks, etc.
* Is SharedArray with Enumerable mixing sufficient Array-like interface?

View File

@ -3,7 +3,7 @@ Gem::Specification.new do |s|
s.version = File.read(File.expand_path('../VERSION', __FILE__)) s.version = File.read(File.expand_path('../VERSION', __FILE__))
s.platform = Gem::Platform::RUBY s.platform = Gem::Platform::RUBY
s.has_rdoc = true s.has_rdoc = true
s.extra_rdoc_files = ["README.rdoc", "ChangeLog", "COPYING"] s.extra_rdoc_files = ["README.md", "ChangeLog", "COPYING"]
s.summary = 'process-shared synchronization primitives' s.summary = 'process-shared synchronization primitives'
s.description = 'FFI wrapper around portable semaphore library with mutex and condition vars built on top.' s.description = 'FFI wrapper around portable semaphore library with mutex and condition vars built on top.'
s.author = 'Patrick Mahoney' s.author = 'Patrick Mahoney'
@ -20,5 +20,6 @@ Gem::Specification.new do |s|
s.add_development_dependency('minitest-matchers') s.add_development_dependency('minitest-matchers')
s.add_development_dependency('rake') s.add_development_dependency('rake')
s.add_development_dependency('rake-compiler') s.add_development_dependency('rake-compiler')
s.add_development_dependency('redcarpet') # for yardoc
s.add_development_dependency('version') s.add_development_dependency('version')
end end