Initial commit.

This commit is contained in:
Patrick Mahoney committed 2011-12-11 21:39:55 -06:00
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
+9
View File
@@ -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
+74
View File
@@ -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
+136
View File
@@ -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
+76
View File
@@ -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
+36
View File
@@ -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
+35
View File
@@ -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