Initial commit.
This commit is contained in:
commit
cf45ab9891
42 files changed
+1704
No files matched your search
@@ -0,0 +1,48 @@
|
||||
require 'spec_helper'
|
||||
require 'process_shared/bounded_semaphore'
|
||||
|
||||
module ProcessShared
|
||||
describe BoundedSemaphore do
|
||||
it 'never rises above its max value' do
|
||||
max = 10
|
||||
BoundedSemaphore.open(max) do |sem|
|
||||
pids = []
|
||||
10.times do |i|
|
||||
pids << fork do
|
||||
100.times do
|
||||
if rand(3) == 0
|
||||
sem.wait
|
||||
else
|
||||
sem.post
|
||||
end
|
||||
end
|
||||
|
||||
exit i
|
||||
end
|
||||
end
|
||||
|
||||
100.times do
|
||||
sem.value.must be_lte(max)
|
||||
end
|
||||
|
||||
pids.each { |pid| Process.wait(pid) }
|
||||
end
|
||||
end
|
||||
|
||||
describe '#post and #wait' do
|
||||
it 'increments and decrements the value' do
|
||||
Semaphore.open(0) do |sem|
|
||||
10.times do |i|
|
||||
sem.post
|
||||
sem.value.must_equal(i + 1)
|
||||
end
|
||||
|
||||
10.times do |i|
|
||||
sem.wait
|
||||
sem.value.must_equal(10 - i - 1)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,9 @@
|
||||
require 'process_shared/libc'
|
||||
|
||||
module ProcessShared
|
||||
describe LibC do
|
||||
it 'throws exceptions with invalid args' do
|
||||
proc { LibC.mmap nil,2,0,0,1,0 }.must_raise(Errno::EINVAL)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,74 @@
|
||||
require 'spec_helper'
|
||||
require 'process_shared/mutex'
|
||||
require 'process_shared/shared_memory'
|
||||
|
||||
module ProcessShared
|
||||
describe Mutex do
|
||||
it 'protects access to a shared variable' do
|
||||
mutex = Mutex.new
|
||||
mem = SharedMemory.new(:char)
|
||||
mem.put_char(0, 0)
|
||||
|
||||
pids = []
|
||||
10.times do |i|
|
||||
inc = (-1) ** i # half the procs increment; half decrement
|
||||
pids << fork do
|
||||
10.times do
|
||||
mutex.lock
|
||||
begin
|
||||
mem.put_char(0, mem.get_char(0) + inc)
|
||||
sleep 0.001
|
||||
ensure
|
||||
mutex.unlock
|
||||
end
|
||||
end
|
||||
Kernel.exit!
|
||||
end
|
||||
end
|
||||
|
||||
pids.each { |pid| ::Process.wait(pid) }
|
||||
|
||||
mem.get_char(0).must_equal(0)
|
||||
end
|
||||
|
||||
it 'protects access to a shared variable with synchronize' do
|
||||
mutex = Mutex.new
|
||||
mem = SharedMemory.new(:char)
|
||||
mem.put_char(0, 0)
|
||||
|
||||
pids = []
|
||||
10.times do |i|
|
||||
inc = (-1) ** i # half the procs increment; half decrement
|
||||
pids << fork do
|
||||
10.times do
|
||||
mutex.synchronize do
|
||||
mem.put_char(0, mem.get_char(0) + inc)
|
||||
sleep 0.001
|
||||
end
|
||||
end
|
||||
Kernel.exit!
|
||||
end
|
||||
end
|
||||
|
||||
pids.each { |pid| ::Process.wait(pid) }
|
||||
|
||||
mem.get_char(0).must_equal(0)
|
||||
end
|
||||
|
||||
it 'raises exception when unlocked by other process' do
|
||||
mutex = Mutex.new
|
||||
|
||||
pid = Kernel.fork do
|
||||
mutex.lock
|
||||
sleep 0.2
|
||||
mutex.unlock
|
||||
Kernel.exit!
|
||||
end
|
||||
|
||||
sleep 0.1
|
||||
proc { mutex.unlock }.must_raise(ProcessError)
|
||||
|
||||
::Process.wait(pid)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,136 @@
|
||||
require 'spec_helper'
|
||||
require 'process_shared/psem'
|
||||
|
||||
module ProcessShared
|
||||
describe PSem do
|
||||
before do
|
||||
extend PSem
|
||||
end
|
||||
|
||||
before(:each) do
|
||||
@err = FFI::MemoryPointer.new(:pointer)
|
||||
end
|
||||
|
||||
describe '.psem_open' do
|
||||
it 'opens a psem' do
|
||||
psem = FFI::MemoryPointer.new(PSem.sizeof_psem_t)
|
||||
psem_open(psem, "psem-test", 1, @err)
|
||||
psem_unlink("psem-test", @err)
|
||||
end
|
||||
|
||||
it 'raises excpetion if name alredy exists' do
|
||||
psem1 = FFI::MemoryPointer.new(PSem.sizeof_psem_t)
|
||||
psem2 = FFI::MemoryPointer.new(PSem.sizeof_psem_t)
|
||||
psem_open(psem1, "psem-test", 1, @err)
|
||||
proc { psem_open(psem2, "psem-test", 1, @err) }.must_raise(Errno::EEXIST)
|
||||
|
||||
psem_unlink("psem-test", @err)
|
||||
psem_open(psem2, "psem-test", 1, @err)
|
||||
psem_unlink("psem-test", @err)
|
||||
end
|
||||
end
|
||||
|
||||
describe '.psem_wait' do
|
||||
before(:each) do
|
||||
@psem = FFI::MemoryPointer.new(PSem.sizeof_psem_t)
|
||||
psem_open(@psem, 'psem-test', 1, @err)
|
||||
psem_unlink('psem-test', @err)
|
||||
|
||||
@int = FFI::MemoryPointer.new(:int)
|
||||
end
|
||||
|
||||
after(:each) do
|
||||
#psem_close(@psem, @err)
|
||||
end
|
||||
|
||||
def value
|
||||
psem_getvalue(@psem, @int, @err)
|
||||
@int.get_int(0)
|
||||
end
|
||||
|
||||
it 'decrements psem value' do
|
||||
value.must_equal 1
|
||||
psem_wait(@psem, @err)
|
||||
value.must_equal(0)
|
||||
end
|
||||
|
||||
it 'waits until another process posts' do
|
||||
psem_wait(@psem, @err)
|
||||
|
||||
# child exits with ~ time spent waiting
|
||||
child = fork do
|
||||
start = Time.now
|
||||
psem_wait(@psem, @err)
|
||||
exit (Time.now - start).ceil
|
||||
end
|
||||
|
||||
t = 1.5
|
||||
sleep t
|
||||
psem_post(@psem, @err)
|
||||
_pid, status = Process.wait2(child)
|
||||
status.exitstatus.must_equal 2
|
||||
end
|
||||
end
|
||||
|
||||
describe '.bsem_open' do
|
||||
it 'opens a bsem' do
|
||||
bsem = FFI::MemoryPointer.new(PSem.sizeof_bsem_t)
|
||||
bsem_open(bsem, "bsem-test", 1, 1, @err)
|
||||
bsem_unlink("bsem-test", @err)
|
||||
end
|
||||
|
||||
it 'raises excpetion if name alredy exists' do
|
||||
bsem1 = FFI::MemoryPointer.new(PSem.sizeof_bsem_t)
|
||||
bsem2 = FFI::MemoryPointer.new(PSem.sizeof_bsem_t)
|
||||
bsem_open(bsem1, "bsem-test", 1, 1, @err)
|
||||
proc { bsem_open(bsem2, "bsem-test", 1, 1, @err) }.must_raise(Errno::EEXIST)
|
||||
|
||||
bsem_unlink("bsem-test", @err)
|
||||
bsem_open(bsem2, "bsem-test", 1, 1, @err)
|
||||
bsem_unlink("bsem-test", @err)
|
||||
end
|
||||
end
|
||||
|
||||
describe '.bsem_wait' do
|
||||
before(:each) do
|
||||
@bsem = FFI::MemoryPointer.new(PSem.sizeof_bsem_t)
|
||||
bsem_open(@bsem, 'bsem-test', 1, 1, @err)
|
||||
bsem_unlink('bsem-test', @err)
|
||||
|
||||
@int = FFI::MemoryPointer.new(:int)
|
||||
end
|
||||
|
||||
after do
|
||||
#bsem_close(@bsem, @err)
|
||||
end
|
||||
|
||||
def value
|
||||
bsem_getvalue(@bsem, @int, @err)
|
||||
@int.get_int(0)
|
||||
end
|
||||
|
||||
it 'decrements bsem value' do
|
||||
value.must_equal 1
|
||||
bsem_wait(@bsem, @err)
|
||||
value.must_equal 0
|
||||
end
|
||||
|
||||
it 'waits until another process posts' do
|
||||
bsem_wait(@bsem, @err)
|
||||
|
||||
# child exits with ~ time spent waiting
|
||||
child = fork do
|
||||
start = Time.now
|
||||
bsem_wait(@bsem, @err)
|
||||
exit (Time.now - start).ceil
|
||||
end
|
||||
|
||||
t = 1.5
|
||||
sleep t
|
||||
bsem_post(@bsem, @err)
|
||||
_pid, status = Process.wait2(child)
|
||||
status.exitstatus.must_equal 2
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,76 @@
|
||||
require 'spec_helper'
|
||||
|
||||
require 'ffi'
|
||||
require 'process_shared/semaphore'
|
||||
require 'process_shared/shared_memory'
|
||||
|
||||
module ProcessShared
|
||||
describe Semaphore do
|
||||
it 'coordinates access to shared object' do
|
||||
nprocs = 4 # number of processes
|
||||
nincrs = 1000 # each process increments nincrs times
|
||||
|
||||
do_increments = lambda do |mem, sem|
|
||||
nincrs.times do
|
||||
sem.wait
|
||||
begin
|
||||
val = mem.get_int(0)
|
||||
# ensure other procs have a chance to interfere
|
||||
sleep 0.001 if rand(100) == 0
|
||||
mem.put_int(0, val + 1)
|
||||
rescue => e
|
||||
"#{Process.pid} die'ing because #{e}"
|
||||
ensure
|
||||
sem.post
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Make sure it fails with no synchronization
|
||||
no_sem = Object.new
|
||||
class << no_sem
|
||||
def wait; end
|
||||
def post; end
|
||||
end
|
||||
SharedMemory.open(FFI.type_size(:int)) do |mem|
|
||||
pids = []
|
||||
nprocs.times do
|
||||
pids << fork { do_increments.call(mem, no_sem); exit }
|
||||
end
|
||||
|
||||
pids.each { |p| Process.wait(p) }
|
||||
# puts "mem is #{mem.get_int(0)}"
|
||||
mem.get_int(0).must be_lt(nprocs * nincrs)
|
||||
end
|
||||
|
||||
# Now try with synchronization
|
||||
SharedMemory.open(FFI.type_size(:int)) do |mem|
|
||||
pids = []
|
||||
Semaphore.open do |sem|
|
||||
nprocs.times do
|
||||
pids << fork { do_increments.call(mem, sem); exit }
|
||||
end
|
||||
end
|
||||
|
||||
pids.each { |p| Process.wait(p) }
|
||||
mem.get_int(0).must_equal(nprocs * nincrs)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#post and #wait' do
|
||||
it 'increments and decrements the value' do
|
||||
Semaphore.open(0) do |sem|
|
||||
10.times do |i|
|
||||
sem.post
|
||||
sem.value.must_equal(i + 1)
|
||||
end
|
||||
|
||||
10.times do |i|
|
||||
sem.wait
|
||||
sem.value.must_equal(10 - i - 1)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,36 @@
|
||||
require 'spec_helper'
|
||||
require 'process_shared/shared_memory'
|
||||
|
||||
module ProcessShared
|
||||
describe SharedMemory do
|
||||
it 'shares memory across processes' do
|
||||
mem = SharedMemory.new(1)
|
||||
mem.put_char(0, 0)
|
||||
mem.get_char(0).must_equal(0)
|
||||
|
||||
pid = fork do
|
||||
mem.put_char(0, 123)
|
||||
Kernel.exit!
|
||||
end
|
||||
|
||||
::Process.wait(pid)
|
||||
|
||||
mem.get_char(0).must_equal(123)
|
||||
end
|
||||
|
||||
it 'initializes with type symbol' do
|
||||
mem = SharedMemory.new(:int)
|
||||
mem.put_int(0, 0)
|
||||
mem.get_int(0).must_equal(0)
|
||||
|
||||
pid = fork do
|
||||
mem.put_int(0, 1234567)
|
||||
Kernel.exit!
|
||||
end
|
||||
|
||||
::Process.wait(pid)
|
||||
|
||||
mem.get_int(0).must_equal(1234567)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,35 @@
|
||||
gem 'minitest'
|
||||
require 'minitest/spec'
|
||||
require 'minitest/autorun'
|
||||
require 'minitest/matchers'
|
||||
|
||||
class RangeMatcher
|
||||
def initialize(operator, limit)
|
||||
@operator = operator.to_sym
|
||||
@limit = limit
|
||||
end
|
||||
|
||||
def description
|
||||
"be #{operator} #{@limit}"
|
||||
end
|
||||
|
||||
def matches?(subject)
|
||||
subject.send(@operator, @limit)
|
||||
end
|
||||
|
||||
def failure_message_for_should
|
||||
"expected #{operator} #{@limit}"
|
||||
end
|
||||
|
||||
def failure_message_for_should_not
|
||||
"expected not #{operator} #{@limit}"
|
||||
end
|
||||
end
|
||||
|
||||
def be_lt(value)
|
||||
RangeMatcher.new('<', value)
|
||||
end
|
||||
|
||||
def be_lte(value)
|
||||
RangeMatcher.new('<=', value)
|
||||
end
|
||||
Reference in new issue
Block a user