process_shared 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. data/COPYING +19 -0
  2. data/ChangeLog +0 -0
  3. data/README.rdoc +69 -0
  4. data/ext/libpsem/bsem.c +188 -0
  5. data/ext/libpsem/bsem.h +32 -0
  6. data/ext/libpsem/constants.c +22 -0
  7. data/ext/libpsem/constants.h +18 -0
  8. data/ext/libpsem/extconf.rb +36 -0
  9. data/ext/libpsem/mempcpy.c +7 -0
  10. data/ext/libpsem/mempcpy.h +13 -0
  11. data/ext/libpsem/mutex.c +15 -0
  12. data/ext/libpsem/mutex.h +14 -0
  13. data/ext/libpsem/psem.c +15 -0
  14. data/ext/libpsem/psem.h +43 -0
  15. data/ext/libpsem/psem_error.c +46 -0
  16. data/ext/libpsem/psem_error.h +11 -0
  17. data/ext/libpsem/psem_posix.c +130 -0
  18. data/ext/libpsem/psem_posix.h +10 -0
  19. data/ext/pthread_sync_helper/extconf.rb +9 -0
  20. data/ext/pthread_sync_helper/pthread_sync_helper.c +43 -0
  21. data/ext/semaphore.c +623 -0
  22. data/lib/process_shared.rb +6 -0
  23. data/lib/process_shared/abstract_semaphore.rb +50 -0
  24. data/lib/process_shared/bounded_semaphore.rb +43 -0
  25. data/lib/process_shared/condition_variable.rb +27 -0
  26. data/lib/process_shared/libc.rb +36 -0
  27. data/lib/process_shared/libpsem.bundle +0 -0
  28. data/lib/process_shared/libpsem.so +0 -0
  29. data/lib/process_shared/mutex.rb +103 -0
  30. data/lib/process_shared/posix_call.rb +29 -0
  31. data/lib/process_shared/process_error.rb +3 -0
  32. data/lib/process_shared/psem.rb +109 -0
  33. data/lib/process_shared/rt.rb +21 -0
  34. data/lib/process_shared/semaphore.rb +60 -0
  35. data/lib/process_shared/shared_memory.rb +45 -0
  36. data/lib/process_shared/thread.rb +30 -0
  37. data/lib/process_shared/with_self.rb +20 -0
  38. data/lib/scratch.rb +300 -0
  39. data/spec/process_shared/bounded_semaphore_spec.rb +48 -0
  40. data/spec/process_shared/libc_spec.rb +9 -0
  41. data/spec/process_shared/mutex_spec.rb +74 -0
  42. data/spec/process_shared/psem_spec.rb +136 -0
  43. data/spec/process_shared/semaphore_spec.rb +76 -0
  44. data/spec/process_shared/shared_memory_spec.rb +36 -0
  45. data/spec/spec_helper.rb +35 -0
  46. 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
@@ -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: []