process_shared 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/COPYING +19 -0
- data/ChangeLog +0 -0
- data/README.rdoc +69 -0
- data/ext/libpsem/bsem.c +188 -0
- data/ext/libpsem/bsem.h +32 -0
- data/ext/libpsem/constants.c +22 -0
- data/ext/libpsem/constants.h +18 -0
- data/ext/libpsem/extconf.rb +36 -0
- data/ext/libpsem/mempcpy.c +7 -0
- data/ext/libpsem/mempcpy.h +13 -0
- data/ext/libpsem/mutex.c +15 -0
- data/ext/libpsem/mutex.h +14 -0
- data/ext/libpsem/psem.c +15 -0
- data/ext/libpsem/psem.h +43 -0
- data/ext/libpsem/psem_error.c +46 -0
- data/ext/libpsem/psem_error.h +11 -0
- data/ext/libpsem/psem_posix.c +130 -0
- data/ext/libpsem/psem_posix.h +10 -0
- data/ext/pthread_sync_helper/extconf.rb +9 -0
- data/ext/pthread_sync_helper/pthread_sync_helper.c +43 -0
- data/ext/semaphore.c +623 -0
- data/lib/process_shared.rb +6 -0
- data/lib/process_shared/abstract_semaphore.rb +50 -0
- data/lib/process_shared/bounded_semaphore.rb +43 -0
- data/lib/process_shared/condition_variable.rb +27 -0
- data/lib/process_shared/libc.rb +36 -0
- data/lib/process_shared/libpsem.bundle +0 -0
- data/lib/process_shared/libpsem.so +0 -0
- data/lib/process_shared/mutex.rb +103 -0
- data/lib/process_shared/posix_call.rb +29 -0
- data/lib/process_shared/process_error.rb +3 -0
- data/lib/process_shared/psem.rb +109 -0
- data/lib/process_shared/rt.rb +21 -0
- data/lib/process_shared/semaphore.rb +60 -0
- data/lib/process_shared/shared_memory.rb +45 -0
- data/lib/process_shared/thread.rb +30 -0
- data/lib/process_shared/with_self.rb +20 -0
- data/lib/scratch.rb +300 -0
- data/spec/process_shared/bounded_semaphore_spec.rb +48 -0
- data/spec/process_shared/libc_spec.rb +9 -0
- data/spec/process_shared/mutex_spec.rb +74 -0
- data/spec/process_shared/psem_spec.rb +136 -0
- data/spec/process_shared/semaphore_spec.rb +76 -0
- data/spec/process_shared/shared_memory_spec.rb +36 -0
- data/spec/spec_helper.rb +35 -0
- metadata +139 -0
@@ -0,0 +1,74 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'process_shared/mutex'
|
3
|
+
require 'process_shared/shared_memory'
|
4
|
+
|
5
|
+
module ProcessShared
|
6
|
+
describe Mutex do
|
7
|
+
it 'protects access to a shared variable' do
|
8
|
+
mutex = Mutex.new
|
9
|
+
mem = SharedMemory.new(:char)
|
10
|
+
mem.put_char(0, 0)
|
11
|
+
|
12
|
+
pids = []
|
13
|
+
10.times do |i|
|
14
|
+
inc = (-1) ** i # half the procs increment; half decrement
|
15
|
+
pids << fork do
|
16
|
+
10.times do
|
17
|
+
mutex.lock
|
18
|
+
begin
|
19
|
+
mem.put_char(0, mem.get_char(0) + inc)
|
20
|
+
sleep 0.001
|
21
|
+
ensure
|
22
|
+
mutex.unlock
|
23
|
+
end
|
24
|
+
end
|
25
|
+
Kernel.exit!
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
pids.each { |pid| ::Process.wait(pid) }
|
30
|
+
|
31
|
+
mem.get_char(0).must_equal(0)
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'protects access to a shared variable with synchronize' do
|
35
|
+
mutex = Mutex.new
|
36
|
+
mem = SharedMemory.new(:char)
|
37
|
+
mem.put_char(0, 0)
|
38
|
+
|
39
|
+
pids = []
|
40
|
+
10.times do |i|
|
41
|
+
inc = (-1) ** i # half the procs increment; half decrement
|
42
|
+
pids << fork do
|
43
|
+
10.times do
|
44
|
+
mutex.synchronize do
|
45
|
+
mem.put_char(0, mem.get_char(0) + inc)
|
46
|
+
sleep 0.001
|
47
|
+
end
|
48
|
+
end
|
49
|
+
Kernel.exit!
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
pids.each { |pid| ::Process.wait(pid) }
|
54
|
+
|
55
|
+
mem.get_char(0).must_equal(0)
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'raises exception when unlocked by other process' do
|
59
|
+
mutex = Mutex.new
|
60
|
+
|
61
|
+
pid = Kernel.fork do
|
62
|
+
mutex.lock
|
63
|
+
sleep 0.2
|
64
|
+
mutex.unlock
|
65
|
+
Kernel.exit!
|
66
|
+
end
|
67
|
+
|
68
|
+
sleep 0.1
|
69
|
+
proc { mutex.unlock }.must_raise(ProcessError)
|
70
|
+
|
71
|
+
::Process.wait(pid)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,136 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'process_shared/psem'
|
3
|
+
|
4
|
+
module ProcessShared
|
5
|
+
describe PSem do
|
6
|
+
before do
|
7
|
+
extend PSem
|
8
|
+
end
|
9
|
+
|
10
|
+
before(:each) do
|
11
|
+
@err = FFI::MemoryPointer.new(:pointer)
|
12
|
+
end
|
13
|
+
|
14
|
+
describe '.psem_open' do
|
15
|
+
it 'opens a psem' do
|
16
|
+
psem = FFI::MemoryPointer.new(PSem.sizeof_psem_t)
|
17
|
+
psem_open(psem, "psem-test", 1, @err)
|
18
|
+
psem_unlink("psem-test", @err)
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'raises excpetion if name alredy exists' do
|
22
|
+
psem1 = FFI::MemoryPointer.new(PSem.sizeof_psem_t)
|
23
|
+
psem2 = FFI::MemoryPointer.new(PSem.sizeof_psem_t)
|
24
|
+
psem_open(psem1, "psem-test", 1, @err)
|
25
|
+
proc { psem_open(psem2, "psem-test", 1, @err) }.must_raise(Errno::EEXIST)
|
26
|
+
|
27
|
+
psem_unlink("psem-test", @err)
|
28
|
+
psem_open(psem2, "psem-test", 1, @err)
|
29
|
+
psem_unlink("psem-test", @err)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe '.psem_wait' do
|
34
|
+
before(:each) do
|
35
|
+
@psem = FFI::MemoryPointer.new(PSem.sizeof_psem_t)
|
36
|
+
psem_open(@psem, 'psem-test', 1, @err)
|
37
|
+
psem_unlink('psem-test', @err)
|
38
|
+
|
39
|
+
@int = FFI::MemoryPointer.new(:int)
|
40
|
+
end
|
41
|
+
|
42
|
+
after(:each) do
|
43
|
+
#psem_close(@psem, @err)
|
44
|
+
end
|
45
|
+
|
46
|
+
def value
|
47
|
+
psem_getvalue(@psem, @int, @err)
|
48
|
+
@int.get_int(0)
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'decrements psem value' do
|
52
|
+
value.must_equal 1
|
53
|
+
psem_wait(@psem, @err)
|
54
|
+
value.must_equal(0)
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'waits until another process posts' do
|
58
|
+
psem_wait(@psem, @err)
|
59
|
+
|
60
|
+
# child exits with ~ time spent waiting
|
61
|
+
child = fork do
|
62
|
+
start = Time.now
|
63
|
+
psem_wait(@psem, @err)
|
64
|
+
exit (Time.now - start).ceil
|
65
|
+
end
|
66
|
+
|
67
|
+
t = 1.5
|
68
|
+
sleep t
|
69
|
+
psem_post(@psem, @err)
|
70
|
+
_pid, status = Process.wait2(child)
|
71
|
+
status.exitstatus.must_equal 2
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
describe '.bsem_open' do
|
76
|
+
it 'opens a bsem' do
|
77
|
+
bsem = FFI::MemoryPointer.new(PSem.sizeof_bsem_t)
|
78
|
+
bsem_open(bsem, "bsem-test", 1, 1, @err)
|
79
|
+
bsem_unlink("bsem-test", @err)
|
80
|
+
end
|
81
|
+
|
82
|
+
it 'raises excpetion if name alredy exists' do
|
83
|
+
bsem1 = FFI::MemoryPointer.new(PSem.sizeof_bsem_t)
|
84
|
+
bsem2 = FFI::MemoryPointer.new(PSem.sizeof_bsem_t)
|
85
|
+
bsem_open(bsem1, "bsem-test", 1, 1, @err)
|
86
|
+
proc { bsem_open(bsem2, "bsem-test", 1, 1, @err) }.must_raise(Errno::EEXIST)
|
87
|
+
|
88
|
+
bsem_unlink("bsem-test", @err)
|
89
|
+
bsem_open(bsem2, "bsem-test", 1, 1, @err)
|
90
|
+
bsem_unlink("bsem-test", @err)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
describe '.bsem_wait' do
|
95
|
+
before(:each) do
|
96
|
+
@bsem = FFI::MemoryPointer.new(PSem.sizeof_bsem_t)
|
97
|
+
bsem_open(@bsem, 'bsem-test', 1, 1, @err)
|
98
|
+
bsem_unlink('bsem-test', @err)
|
99
|
+
|
100
|
+
@int = FFI::MemoryPointer.new(:int)
|
101
|
+
end
|
102
|
+
|
103
|
+
after do
|
104
|
+
#bsem_close(@bsem, @err)
|
105
|
+
end
|
106
|
+
|
107
|
+
def value
|
108
|
+
bsem_getvalue(@bsem, @int, @err)
|
109
|
+
@int.get_int(0)
|
110
|
+
end
|
111
|
+
|
112
|
+
it 'decrements bsem value' do
|
113
|
+
value.must_equal 1
|
114
|
+
bsem_wait(@bsem, @err)
|
115
|
+
value.must_equal 0
|
116
|
+
end
|
117
|
+
|
118
|
+
it 'waits until another process posts' do
|
119
|
+
bsem_wait(@bsem, @err)
|
120
|
+
|
121
|
+
# child exits with ~ time spent waiting
|
122
|
+
child = fork do
|
123
|
+
start = Time.now
|
124
|
+
bsem_wait(@bsem, @err)
|
125
|
+
exit (Time.now - start).ceil
|
126
|
+
end
|
127
|
+
|
128
|
+
t = 1.5
|
129
|
+
sleep t
|
130
|
+
bsem_post(@bsem, @err)
|
131
|
+
_pid, status = Process.wait2(child)
|
132
|
+
status.exitstatus.must_equal 2
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
require 'ffi'
|
4
|
+
require 'process_shared/semaphore'
|
5
|
+
require 'process_shared/shared_memory'
|
6
|
+
|
7
|
+
module ProcessShared
|
8
|
+
describe Semaphore do
|
9
|
+
it 'coordinates access to shared object' do
|
10
|
+
nprocs = 4 # number of processes
|
11
|
+
nincrs = 1000 # each process increments nincrs times
|
12
|
+
|
13
|
+
do_increments = lambda do |mem, sem|
|
14
|
+
nincrs.times do
|
15
|
+
sem.wait
|
16
|
+
begin
|
17
|
+
val = mem.get_int(0)
|
18
|
+
# ensure other procs have a chance to interfere
|
19
|
+
sleep 0.001 if rand(100) == 0
|
20
|
+
mem.put_int(0, val + 1)
|
21
|
+
rescue => e
|
22
|
+
"#{Process.pid} die'ing because #{e}"
|
23
|
+
ensure
|
24
|
+
sem.post
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# Make sure it fails with no synchronization
|
30
|
+
no_sem = Object.new
|
31
|
+
class << no_sem
|
32
|
+
def wait; end
|
33
|
+
def post; end
|
34
|
+
end
|
35
|
+
SharedMemory.open(FFI.type_size(:int)) do |mem|
|
36
|
+
pids = []
|
37
|
+
nprocs.times do
|
38
|
+
pids << fork { do_increments.call(mem, no_sem); exit }
|
39
|
+
end
|
40
|
+
|
41
|
+
pids.each { |p| Process.wait(p) }
|
42
|
+
# puts "mem is #{mem.get_int(0)}"
|
43
|
+
mem.get_int(0).must be_lt(nprocs * nincrs)
|
44
|
+
end
|
45
|
+
|
46
|
+
# Now try with synchronization
|
47
|
+
SharedMemory.open(FFI.type_size(:int)) do |mem|
|
48
|
+
pids = []
|
49
|
+
Semaphore.open do |sem|
|
50
|
+
nprocs.times do
|
51
|
+
pids << fork { do_increments.call(mem, sem); exit }
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
pids.each { |p| Process.wait(p) }
|
56
|
+
mem.get_int(0).must_equal(nprocs * nincrs)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
describe '#post and #wait' do
|
61
|
+
it 'increments and decrements the value' do
|
62
|
+
Semaphore.open(0) do |sem|
|
63
|
+
10.times do |i|
|
64
|
+
sem.post
|
65
|
+
sem.value.must_equal(i + 1)
|
66
|
+
end
|
67
|
+
|
68
|
+
10.times do |i|
|
69
|
+
sem.wait
|
70
|
+
sem.value.must_equal(10 - i - 1)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'process_shared/shared_memory'
|
3
|
+
|
4
|
+
module ProcessShared
|
5
|
+
describe SharedMemory do
|
6
|
+
it 'shares memory across processes' do
|
7
|
+
mem = SharedMemory.new(1)
|
8
|
+
mem.put_char(0, 0)
|
9
|
+
mem.get_char(0).must_equal(0)
|
10
|
+
|
11
|
+
pid = fork do
|
12
|
+
mem.put_char(0, 123)
|
13
|
+
Kernel.exit!
|
14
|
+
end
|
15
|
+
|
16
|
+
::Process.wait(pid)
|
17
|
+
|
18
|
+
mem.get_char(0).must_equal(123)
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'initializes with type symbol' do
|
22
|
+
mem = SharedMemory.new(:int)
|
23
|
+
mem.put_int(0, 0)
|
24
|
+
mem.get_int(0).must_equal(0)
|
25
|
+
|
26
|
+
pid = fork do
|
27
|
+
mem.put_int(0, 1234567)
|
28
|
+
Kernel.exit!
|
29
|
+
end
|
30
|
+
|
31
|
+
::Process.wait(pid)
|
32
|
+
|
33
|
+
mem.get_int(0).must_equal(1234567)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
gem 'minitest'
|
2
|
+
require 'minitest/spec'
|
3
|
+
require 'minitest/autorun'
|
4
|
+
require 'minitest/matchers'
|
5
|
+
|
6
|
+
class RangeMatcher
|
7
|
+
def initialize(operator, limit)
|
8
|
+
@operator = operator.to_sym
|
9
|
+
@limit = limit
|
10
|
+
end
|
11
|
+
|
12
|
+
def description
|
13
|
+
"be #{operator} #{@limit}"
|
14
|
+
end
|
15
|
+
|
16
|
+
def matches?(subject)
|
17
|
+
subject.send(@operator, @limit)
|
18
|
+
end
|
19
|
+
|
20
|
+
def failure_message_for_should
|
21
|
+
"expected #{operator} #{@limit}"
|
22
|
+
end
|
23
|
+
|
24
|
+
def failure_message_for_should_not
|
25
|
+
"expected not #{operator} #{@limit}"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def be_lt(value)
|
30
|
+
RangeMatcher.new('<', value)
|
31
|
+
end
|
32
|
+
|
33
|
+
def be_lte(value)
|
34
|
+
RangeMatcher.new('<=', value)
|
35
|
+
end
|
metadata
ADDED
@@ -0,0 +1,139 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: process_shared
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Patrick Mahoney
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2011-12-12 00:00:00.000000000Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: ffi
|
16
|
+
requirement: &19463720 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '1.0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *19463720
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: rake-compiler
|
27
|
+
requirement: &19462580 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ! '>='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0'
|
33
|
+
type: :development
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *19462580
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: minitest
|
38
|
+
requirement: &19461120 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ! '>='
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0'
|
44
|
+
type: :development
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *19461120
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: minitest-matchers
|
49
|
+
requirement: &19459840 !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ! '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
type: :development
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: *19459840
|
58
|
+
description: FFI wrapper around portable semaphore library with mutex and condition
|
59
|
+
vars built on top.
|
60
|
+
email: pat@polycrystal.org
|
61
|
+
executables: []
|
62
|
+
extensions:
|
63
|
+
- ext/pthread_sync_helper/extconf.rb
|
64
|
+
- ext/libpsem/extconf.rb
|
65
|
+
extra_rdoc_files:
|
66
|
+
- README.rdoc
|
67
|
+
- ChangeLog
|
68
|
+
- COPYING
|
69
|
+
files:
|
70
|
+
- lib/scratch.rb
|
71
|
+
- lib/process_shared.rb
|
72
|
+
- lib/process_shared/abstract_semaphore.rb
|
73
|
+
- lib/process_shared/posix_call.rb
|
74
|
+
- lib/process_shared/process_error.rb
|
75
|
+
- lib/process_shared/thread.rb
|
76
|
+
- lib/process_shared/mutex.rb
|
77
|
+
- lib/process_shared/semaphore.rb
|
78
|
+
- lib/process_shared/rt.rb
|
79
|
+
- lib/process_shared/psem.rb
|
80
|
+
- lib/process_shared/with_self.rb
|
81
|
+
- lib/process_shared/condition_variable.rb
|
82
|
+
- lib/process_shared/bounded_semaphore.rb
|
83
|
+
- lib/process_shared/libc.rb
|
84
|
+
- lib/process_shared/shared_memory.rb
|
85
|
+
- lib/process_shared/libpsem.bundle
|
86
|
+
- lib/process_shared/libpsem.so
|
87
|
+
- ext/pthread_sync_helper/pthread_sync_helper.c
|
88
|
+
- ext/semaphore.c
|
89
|
+
- ext/libpsem/mempcpy.c
|
90
|
+
- ext/libpsem/psem_error.c
|
91
|
+
- ext/libpsem/bsem.c
|
92
|
+
- ext/libpsem/psem_posix.c
|
93
|
+
- ext/libpsem/psem.c
|
94
|
+
- ext/libpsem/mutex.c
|
95
|
+
- ext/libpsem/constants.c
|
96
|
+
- ext/libpsem/bsem.h
|
97
|
+
- ext/libpsem/psem_error.h
|
98
|
+
- ext/libpsem/mutex.h
|
99
|
+
- ext/libpsem/constants.h
|
100
|
+
- ext/libpsem/psem.h
|
101
|
+
- ext/libpsem/psem_posix.h
|
102
|
+
- ext/libpsem/mempcpy.h
|
103
|
+
- ext/pthread_sync_helper/extconf.rb
|
104
|
+
- ext/libpsem/extconf.rb
|
105
|
+
- spec/process_shared/psem_spec.rb
|
106
|
+
- spec/process_shared/shared_memory_spec.rb
|
107
|
+
- spec/process_shared/mutex_spec.rb
|
108
|
+
- spec/process_shared/bounded_semaphore_spec.rb
|
109
|
+
- spec/process_shared/semaphore_spec.rb
|
110
|
+
- spec/process_shared/libc_spec.rb
|
111
|
+
- spec/spec_helper.rb
|
112
|
+
- README.rdoc
|
113
|
+
- ChangeLog
|
114
|
+
- COPYING
|
115
|
+
homepage: https://github.com/pmahoney/process_shared
|
116
|
+
licenses: []
|
117
|
+
post_install_message:
|
118
|
+
rdoc_options: []
|
119
|
+
require_paths:
|
120
|
+
- lib
|
121
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
122
|
+
none: false
|
123
|
+
requirements:
|
124
|
+
- - ! '>='
|
125
|
+
- !ruby/object:Gem::Version
|
126
|
+
version: '0'
|
127
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
128
|
+
none: false
|
129
|
+
requirements:
|
130
|
+
- - ! '>='
|
131
|
+
- !ruby/object:Gem::Version
|
132
|
+
version: '0'
|
133
|
+
requirements: []
|
134
|
+
rubyforge_project:
|
135
|
+
rubygems_version: 1.8.10
|
136
|
+
signing_key:
|
137
|
+
specification_version: 3
|
138
|
+
summary: process-shared synchronization primitives
|
139
|
+
test_files: []
|