Compare commits
10 Commits
af27697623
...
857bb2ae85
Author | SHA1 | Date |
---|---|---|
邱博亞 | 857bb2ae85 | |
Patrick Mahoney | 9ef0acb093 | |
Patrick Mahoney | 1803466eae | |
Patrick Mahoney | 2fe552335a | |
Patrick Mahoney | 751a8d0305 | |
Patrick Mahoney | ba45f856ba | |
Patrick Mahoney | 1a05bcd924 | |
Patrick Mahoney | 25f9bb2cae | |
Patrick Mahoney | 88dcebf5f7 | |
Patrick Mahoney | 3acc7afc34 |
57
README.md
57
README.md
|
@ -14,26 +14,57 @@
|
|||
process_shared
|
||||
==============
|
||||
|
||||
Concurrency primitives that may be used in a cross-process way to
|
||||
coordinate share memory between processes.
|
||||
Cross-process concurrency primitives that may be used to coordinate
|
||||
shared memory between processes.
|
||||
|
||||
```ruby
|
||||
require 'process_shared'
|
||||
|
||||
mutex = ProcessShared::Mutex.new
|
||||
cond = ProcessShared::ConditionVariable.new
|
||||
|
||||
mem = ProcessShared::SharedMemory.new(:int32, 2) # extends FFI::Pointer
|
||||
|
||||
pid1 = fork do
|
||||
nums = mutex.synchronize do
|
||||
cond.wait(mutex)
|
||||
mem.get_array_of_int(0, 2)
|
||||
end
|
||||
puts "process #{Process.pid} received #{nums}"
|
||||
end
|
||||
|
||||
pid2 = fork do
|
||||
nums = [12345, 67890]
|
||||
mutex.synchronize do
|
||||
puts "process #{Process.pid} sending #{nums}"
|
||||
mem.put_array_of_int(0, nums)
|
||||
cond.signal
|
||||
end
|
||||
end
|
||||
|
||||
Process.waitall
|
||||
```
|
||||
|
||||
[API Documentation](http://www.rubydoc.info/github/pmahoney/process_shared/master)
|
||||
|
||||
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.
|
||||
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.
|
||||
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). In a giant hack, **on OS
|
||||
X, `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.
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ module Mach
|
|||
# Replace methods in +syms+ with error checking wrappers that
|
||||
# invoke the original method and raise a {SystemCallError}.
|
||||
#
|
||||
# The original method is invoked, and it's return value is passed
|
||||
# The original method is invoked, and its return value is passed
|
||||
# to the block (or a default check). The block should return true
|
||||
# if the return value indicates an error state.
|
||||
def self.error_check(*syms, &is_err)
|
||||
|
|
|
@ -16,9 +16,26 @@ module ProcessShared
|
|||
FFI::Platform::LIBSUFFIX
|
||||
end
|
||||
|
||||
ffi_lib File.join(File.expand_path(File.dirname(__FILE__)),
|
||||
'helper.' + suffix)
|
||||
|
||||
# Ruby 2.1 (also 2.0?) puts extensions into, e.g.
|
||||
# $GEM_HOME/extensions/x86_64-linux/2.1.0/static/$gemname-$gemversion,
|
||||
# so they aren't in the same dir as this file. Normally that's
|
||||
# fine since you're just 'require'ing them, but 'helper.so'
|
||||
# isn't a real Ruby extension; it's just a shared lib to
|
||||
# export some constants that are only available as macros in
|
||||
# libc.
|
||||
#
|
||||
# Fallback to attempting to load from same directory as this file.
|
||||
helper = 'process_shared/posix/helper.' + suffix
|
||||
fallback_path = File.expand_path(File.dirname(__FILE__))
|
||||
path = $LOAD_PATH.find(fallback_path) do |path|
|
||||
File.exists?(File.join(path, helper))
|
||||
end
|
||||
|
||||
if defined?(Truffle)
|
||||
ffi_lib(File.join(::Truffle::Boot.toolchain_paths(:LD_LIBRARY_PATH), 'libgraalvm-llvm.so.1'), File.join(path, helper))
|
||||
else
|
||||
ffi_lib File.join(path, helper)
|
||||
end
|
||||
[:o_rdwr,
|
||||
:o_creat,
|
||||
:o_excl,
|
||||
|
|
|
@ -1,29 +0,0 @@
|
|||
# require 'process_shared/libc' - circular dependency here...
|
||||
|
||||
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.
|
||||
#
|
||||
# Errors are detected if the block returns true when called with
|
||||
# the original method's return value.
|
||||
def error_check(*syms, &is_err)
|
||||
unless block_given?
|
||||
is_err = lambda { |v| (v == -1) }
|
||||
end
|
||||
|
||||
syms.each do |sym|
|
||||
method = self.method(sym)
|
||||
define_singleton_method(sym) do |*args|
|
||||
ret = method.call(*args)
|
||||
if is_err.call(ret)
|
||||
raise SystemCallError.new("error in #{sym}", LibC.errno)
|
||||
else
|
||||
ret
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,21 +0,0 @@
|
|||
require 'process_shared/posix_call'
|
||||
require 'process_shared/psem'
|
||||
|
||||
module ProcessShared
|
||||
module RT
|
||||
extend FFI::Library
|
||||
extend PosixCall
|
||||
|
||||
# FIXME: mac and linux OK, but what about everything else?
|
||||
if FFI::Platform.mac?
|
||||
ffi_lib 'c'
|
||||
else
|
||||
ffi_lib 'rt'
|
||||
end
|
||||
|
||||
attach_function :shm_open, [:string, :int, :mode_t], :int
|
||||
attach_function :shm_unlink, [:string], :int
|
||||
|
||||
error_check :shm_open, :shm_unlink
|
||||
end
|
||||
end
|
|
@ -14,7 +14,7 @@ Gem::Specification.new do |s|
|
|||
|
||||
s.add_dependency('ffi', '~> 1.0')
|
||||
|
||||
s.add_development_dependency('ci_reporter')
|
||||
s.add_development_dependency('ci_reporter_minitest', '~> 1.0')
|
||||
s.add_development_dependency('flog')
|
||||
s.add_development_dependency('minitest')
|
||||
s.add_development_dependency('minitest-matchers')
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
require 'rubygems' if RUBY_VERSION =~ /^1.8/
|
||||
gem 'minitest'
|
||||
require 'minitest/spec'
|
||||
require 'minitest/autorun'
|
||||
require 'minitest/spec'
|
||||
require 'minitest/matchers'
|
||||
|
||||
require 'process_shared'
|
||||
|
|
Loading…
Reference in New Issue