process_shared 0.1.10 → 0.2.0

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 159a6ddc012e5c9ae969b505375b65c4ce28211d
4
+ data.tar.gz: d1ecb4700906ad8b9534fd3535f4e4e33e34bc52
5
+ SHA512:
6
+ metadata.gz: 1ab8b394e065e57c42bfb403a7f37262651fd3c8a81ee98c2fd7d7bec812afb9e305ab598b43ee3321a09407bfd901da8397abc4baa3e24bd64d20ef3b5ca3ef
7
+ data.tar.gz: 266a1ed392b960097f638c59389e7fc5b1de1c08c4be28ace68f6987293801b1da38542f8354951cd20fa9f307e8e8bcb133ea6905572fffd8b90a1100a1b21b
@@ -0,0 +1,123 @@
1
+ [![Gem Version][gemv-img]][gemv]
2
+ [![Build Status][travis-img]][travis]
3
+ [![Dependency Status][gemnasium-img]][gemnasium]
4
+ [![Code Climate][codeclimate-img]][codeclimate]
5
+ [gemv]: https://rubygems.org/gems/process_shared
6
+ [gemv-img]: https://badge.fury.io/rb/process_shared.png
7
+ [travis]: https://travis-ci.org/pmahoney/process_shared
8
+ [travis-img]: https://travis-ci.org/pmahoney/process_shared.png
9
+ [gemnasium]: https://gemnasium.com/pmahoney/process_shared
10
+ [gemnasium-img]: https://gemnasium.com/pmahoney/process_shared.png
11
+ [codeclimate]: https://codeclimate.com/github/pmahoney/process_shared
12
+ [codeclimate-img]: https://codeclimate.com/github/pmahoney/process_shared.png
13
+
14
+ process_shared
15
+ ==============
16
+
17
+ Concurrency primitives that may be used in a cross-process way to
18
+ coordinate share memory between processes.
19
+
20
+ FFI is used to access POSIX semaphore on Linux or Mach semaphores on
21
+ Mac. Atop these semaphores are implemented ProcessShared::Semaphore,
22
+ ProcessShared::Mutex. POSIX shared memory is used to implement
23
+ ProcessShared::SharedMemory.
24
+
25
+ On Linux, POSIX semaphores support `sem_timedwait()` which can wait on
26
+ a semaphore but stop waiting after a timeout.
27
+
28
+ Mac OS X's implementation of POSIX semaphores does not support
29
+ timeouts. But, the Mach layer in Mac OS X has its own semaphores that
30
+ do support timeouts. Thus, process_shared implements a moderate
31
+ subset of the Mach API, which is quite a bit different from POSIX.
32
+ Namely, semaphores created in one process are not available in child
33
+ processes created via `fork()`. Mach does provide the means to copy
34
+ capabilities between tasks (Mach equivalent to processes).
35
+ process_shared overrides Ruby's `fork` methods so that semaphores are
36
+ copied from parent to child to emulate the POSIX behavior.
37
+
38
+ This is an incomplete work in progress.
39
+
40
+ License
41
+ -------
42
+
43
+ MIT
44
+
45
+ Install
46
+ -------
47
+
48
+ Install the gem with:
49
+
50
+ gem install process_shared
51
+
52
+ Usage
53
+ -----
54
+
55
+ ```ruby
56
+ require 'process_shared'
57
+
58
+ mutex = ProcessShared::Mutex.new
59
+ mem = ProcessShared::SharedMemory.new(:int) # extends FFI::Pointer
60
+ mem.put_int(0, 0)
61
+
62
+ pid1 = fork do
63
+ puts "in process 1 (#{Process.pid})"
64
+ 10.times do
65
+ sleep 0.01
66
+ mutex.synchronize do
67
+ value = mem.get_int(0)
68
+ sleep 0.01
69
+ puts "process 1 (#{Process.pid}) incrementing"
70
+ mem.put_int(0, value + 1)
71
+ end
72
+ end
73
+ end
74
+
75
+ pid2 = fork do
76
+ puts "in process 2 (#{Process.pid})"
77
+ 10.times do
78
+ sleep 0.01
79
+ mutex.synchronize do
80
+ value = mem.get_int(0)
81
+ sleep 0.01
82
+ puts "process 2 (#{Process.pid}) decrementing"
83
+ mem.put_int(0, value - 1)
84
+ end
85
+ end
86
+ end
87
+
88
+ Process.wait(pid1)
89
+ Process.wait(pid2)
90
+
91
+ puts "value should be zero: #{mem.get_int(0)}"
92
+ ```
93
+
94
+ Transfer Objects Across Processes
95
+ ---------------------------------
96
+
97
+ ```ruby
98
+ # allocate a sufficient memory block
99
+ mem = ProcessShared::SharedMemory.new(1024)
100
+
101
+ # sub process can write (serialize) object to memory (with bounds checking)
102
+ pid = fork do
103
+ mem.write_object(['a', 'b'])
104
+ end
105
+
106
+ Process.wait(pid)
107
+
108
+ # parent process can read the object back (synchronizing access
109
+ # with a Mutex left as an excercie to reader)
110
+
111
+ mem.read_object.must_equal ['a', 'b']
112
+ ```
113
+
114
+ Todo
115
+ ----
116
+
117
+ * Test ConditionVariable
118
+ * Implement optional override of core Thread/Mutex classes
119
+ * Extend to win32? (See Python's processing library)
120
+ * Add finalizer to Mutex? (finalizer on Semaphore objects may be enough) or a method to
121
+ explicitly close and release resources?
122
+ * Test semantics of crashing processes who still hold locks, etc.
123
+ * Is SharedArray with Enumerable mixing sufficient Array-like interface?
@@ -1,3 +1,5 @@
1
+ require 'forwardable'
2
+
1
3
  module ProcessShared
2
4
  module SynchronizableSemaphore
3
5
  # Yield the block after decrementing the semaphore, ensuring that
@@ -12,5 +14,72 @@ module ProcessShared
12
14
  post
13
15
  end
14
16
  end
17
+
18
+ # Expose an unchecked mutex-like interface using only this semaphore.
19
+ #
20
+ # @return [FasterUncheckedMutex] An unchecked mutex facade for this semaphore
21
+ def to_mtx
22
+ FasterUncheckedMutex.new(self)
23
+ end
24
+
25
+ private
26
+
27
+ # @api private
28
+ #
29
+ # Presents a mutex-like facade over a semaphore.
30
+ # @see SynchronizableSemaphore#to_mtx
31
+ #
32
+ # NOTE: Unlocking a locked mutex from a different process or thread than
33
+ # that which locked it will result in undefined behavior, whereas with the
34
+ # Mutex class, this error will be detected and an exception raised.
35
+ #
36
+ # It is recommended to develop using the Mutex class, which is checked, and
37
+ # to use this unchecked variant only to optimized performance for code paths
38
+ # that have been determined to have correct lock/unlock behavior.
39
+ class FasterUncheckedMutex
40
+ extend Forwardable
41
+
42
+ def initialize(sem)
43
+ @sem = sem
44
+ end
45
+
46
+ # @return [Boolean] +true+ if currently locked
47
+ def locked?
48
+ acquired = try_lock
49
+ unlock if acquired
50
+ !acquired
51
+ end
52
+
53
+ # Releases the lock and sleeps timeout seconds if it is given and
54
+ # non-nil or forever.
55
+ #
56
+ # TODO: de-duplicate this from Mutex#sleep
57
+ #
58
+ # @return [Numeric]
59
+ def sleep(timeout = nil)
60
+ unlock
61
+ begin
62
+ timeout ? Kernel.sleep(timeout) : Kernel.sleep
63
+ ensure
64
+ lock
65
+ end
66
+ end
67
+
68
+ # @return [Boolean] +true+ if lock was acquired, +false+ if already locked
69
+ def try_lock
70
+ @sem.try_wait
71
+ true
72
+ rescue Errno::EAGAIN
73
+ false
74
+ end
75
+
76
+ # delegate to methods with different names
77
+ def_delegator :@sem, :wait, :lock
78
+ def_delegator :@sem, :post, :unlock
79
+
80
+ # delegate to methods with the same names
81
+ def_delegators :@sem, :synchronize, :close
82
+ end
83
+
15
84
  end
16
85
  end
@@ -11,11 +11,7 @@ module ProcessShared
11
11
  include LockBehavior
12
12
 
13
13
  before :each do
14
- @lock = Semaphore.new
15
- class << @lock
16
- alias_method :lock, :wait
17
- alias_method :unlock, :post
18
- end
14
+ @lock = Semaphore.new.to_mtx
19
15
  end
20
16
 
21
17
  after :each do
@@ -191,5 +187,47 @@ module ProcessShared
191
187
  end
192
188
  end
193
189
  end
190
+
191
+ describe '#to_mtx' do
192
+ before :each do
193
+ @mtx = Semaphore.new.to_mtx
194
+ end
195
+
196
+ # NOTE:
197
+ # - #lock / #unlock covered by LockingBehavior above
198
+ # - #synchronize covered elsewhere as well?
199
+
200
+ describe '#locked?' do
201
+ it 'returns true when locked' do
202
+ @mtx.synchronize { @mtx.locked?.must_equal true }
203
+ end
204
+
205
+ it 'returns false when not locked' do
206
+ @mtx.locked?.must_equal false
207
+ end
208
+
209
+ it 'does not itself acquire lock' do
210
+ @mtx.locked?.must_equal false
211
+ @mtx.locked?.must_equal false # check again to make sure lock not acquired
212
+ end
213
+ end
214
+
215
+ describe '#sleep' do
216
+ # TODO: add tests for #sleep
217
+ end
218
+
219
+ describe '#try_lock' do
220
+ it 'returns true and acquires lock when unlocked' do
221
+ @mtx.try_lock.must_equal true
222
+ @mtx.locked?.must_equal true
223
+ @mtx.unlock
224
+ end
225
+
226
+ it 'returns false when already locked' do
227
+ @mtx.synchronize { @mtx.try_lock.must_equal false }
228
+ end
229
+ end
230
+
231
+ end
194
232
  end
195
233
  end
metadata CHANGED
@@ -1,142 +1,139 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: process_shared
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.10
5
- prerelease:
4
+ version: 0.2.0
6
5
  platform: ruby
7
6
  authors:
8
7
  - Patrick Mahoney
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2013-12-27 00:00:00.000000000 Z
11
+ date: 2013-12-31 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: ffi
16
15
  requirement: !ruby/object:Gem::Requirement
17
- none: false
18
16
  requirements:
19
- - - ~>
17
+ - - "~>"
20
18
  - !ruby/object:Gem::Version
21
19
  version: '1.0'
22
20
  type: :runtime
23
21
  prerelease: false
24
22
  version_requirements: !ruby/object:Gem::Requirement
25
- none: false
26
23
  requirements:
27
- - - ~>
24
+ - - "~>"
28
25
  - !ruby/object:Gem::Version
29
26
  version: '1.0'
30
27
  - !ruby/object:Gem::Dependency
31
28
  name: ci_reporter
32
29
  requirement: !ruby/object:Gem::Requirement
33
- none: false
34
30
  requirements:
35
- - - ! '>='
31
+ - - ">="
36
32
  - !ruby/object:Gem::Version
37
33
  version: '0'
38
34
  type: :development
39
35
  prerelease: false
40
36
  version_requirements: !ruby/object:Gem::Requirement
41
- none: false
42
37
  requirements:
43
- - - ! '>='
38
+ - - ">="
44
39
  - !ruby/object:Gem::Version
45
40
  version: '0'
46
41
  - !ruby/object:Gem::Dependency
47
42
  name: flog
48
43
  requirement: !ruby/object:Gem::Requirement
49
- none: false
50
44
  requirements:
51
- - - ! '>='
45
+ - - ">="
52
46
  - !ruby/object:Gem::Version
53
47
  version: '0'
54
48
  type: :development
55
49
  prerelease: false
56
50
  version_requirements: !ruby/object:Gem::Requirement
57
- none: false
58
51
  requirements:
59
- - - ! '>='
52
+ - - ">="
60
53
  - !ruby/object:Gem::Version
61
54
  version: '0'
62
55
  - !ruby/object:Gem::Dependency
63
56
  name: minitest
64
57
  requirement: !ruby/object:Gem::Requirement
65
- none: false
66
58
  requirements:
67
- - - ! '>='
59
+ - - ">="
68
60
  - !ruby/object:Gem::Version
69
61
  version: '0'
70
62
  type: :development
71
63
  prerelease: false
72
64
  version_requirements: !ruby/object:Gem::Requirement
73
- none: false
74
65
  requirements:
75
- - - ! '>='
66
+ - - ">="
76
67
  - !ruby/object:Gem::Version
77
68
  version: '0'
78
69
  - !ruby/object:Gem::Dependency
79
70
  name: minitest-matchers
80
71
  requirement: !ruby/object:Gem::Requirement
81
- none: false
82
72
  requirements:
83
- - - ! '>='
73
+ - - ">="
84
74
  - !ruby/object:Gem::Version
85
75
  version: '0'
86
76
  type: :development
87
77
  prerelease: false
88
78
  version_requirements: !ruby/object:Gem::Requirement
89
- none: false
90
79
  requirements:
91
- - - ! '>='
80
+ - - ">="
92
81
  - !ruby/object:Gem::Version
93
82
  version: '0'
94
83
  - !ruby/object:Gem::Dependency
95
84
  name: rake
96
85
  requirement: !ruby/object:Gem::Requirement
97
- none: false
98
86
  requirements:
99
- - - ! '>='
87
+ - - ">="
100
88
  - !ruby/object:Gem::Version
101
89
  version: '0'
102
90
  type: :development
103
91
  prerelease: false
104
92
  version_requirements: !ruby/object:Gem::Requirement
105
- none: false
106
93
  requirements:
107
- - - ! '>='
94
+ - - ">="
108
95
  - !ruby/object:Gem::Version
109
96
  version: '0'
110
97
  - !ruby/object:Gem::Dependency
111
98
  name: rake-compiler
112
99
  requirement: !ruby/object:Gem::Requirement
113
- none: false
114
100
  requirements:
115
- - - ! '>='
101
+ - - ">="
116
102
  - !ruby/object:Gem::Version
117
103
  version: '0'
118
104
  type: :development
119
105
  prerelease: false
120
106
  version_requirements: !ruby/object:Gem::Requirement
121
- none: false
122
107
  requirements:
123
- - - ! '>='
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: redcarpet
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
124
123
  - !ruby/object:Gem::Version
125
124
  version: '0'
126
125
  - !ruby/object:Gem::Dependency
127
126
  name: version
128
127
  requirement: !ruby/object:Gem::Requirement
129
- none: false
130
128
  requirements:
131
- - - ! '>='
129
+ - - ">="
132
130
  - !ruby/object:Gem::Version
133
131
  version: '0'
134
132
  type: :development
135
133
  prerelease: false
136
134
  version_requirements: !ruby/object:Gem::Requirement
137
- none: false
138
135
  requirements:
139
- - - ! '>='
136
+ - - ">="
140
137
  - !ruby/object:Gem::Version
141
138
  version: '0'
142
139
  description: FFI wrapper around portable semaphore library with mutex and condition
@@ -147,100 +144,93 @@ extensions:
147
144
  - ext/helper/extconf.rb
148
145
  - ext/pthread_sync_helper/extconf.rb
149
146
  extra_rdoc_files:
150
- - README.rdoc
147
+ - README.md
151
148
  - ChangeLog
152
149
  - COPYING
153
150
  files:
154
- - lib/process_shared.rb
155
- - lib/mach/port.rb
151
+ - COPYING
152
+ - ChangeLog
153
+ - README.md
154
+ - ext/helper/extconf.rb
155
+ - ext/helper/helper.c
156
+ - ext/pthread_sync_helper/extconf.rb
157
+ - ext/pthread_sync_helper/pthread_sync_helper.c
158
+ - ext/semaphore.c
159
+ - lib/mach.rb
160
+ - lib/mach/clock.rb
161
+ - lib/mach/error.rb
162
+ - lib/mach/functions.rb
156
163
  - lib/mach/host.rb
164
+ - lib/mach/port.rb
165
+ - lib/mach/semaphore.rb
157
166
  - lib/mach/task.rb
158
- - lib/mach/error.rb
159
167
  - lib/mach/time_spec.rb
160
- - lib/mach/clock.rb
161
168
  - lib/mach/types.rb
162
- - lib/mach/semaphore.rb
163
- - lib/mach/functions.rb
164
- - lib/process_shared/posix/shared_memory.rb
165
- - lib/process_shared/posix/shared_array.rb
166
- - lib/process_shared/posix/time_val.rb
169
+ - lib/process_shared.rb
170
+ - lib/process_shared/abstract_semaphore.rb
171
+ - lib/process_shared/binary_semaphore.rb
172
+ - lib/process_shared/condition_variable.rb
173
+ - lib/process_shared/define_singleton_method.rb
174
+ - lib/process_shared/mach.rb
175
+ - lib/process_shared/mach/semaphore.rb
176
+ - lib/process_shared/monitor.rb
177
+ - lib/process_shared/monitor_mixin.rb
178
+ - lib/process_shared/mutex.rb
179
+ - lib/process_shared/object_buffer.rb
180
+ - lib/process_shared/open_with_self.rb
167
181
  - lib/process_shared/posix/errno.rb
168
- - lib/process_shared/posix/time_spec.rb
169
- - lib/process_shared/posix/semaphore.rb
170
182
  - lib/process_shared/posix/libc.rb
183
+ - lib/process_shared/posix/semaphore.rb
184
+ - lib/process_shared/posix/shared_array.rb
185
+ - lib/process_shared/posix/shared_memory.rb
186
+ - lib/process_shared/posix/time_spec.rb
187
+ - lib/process_shared/posix/time_val.rb
171
188
  - lib/process_shared/posix_call.rb
172
- - lib/process_shared/shared_array.rb
173
- - lib/process_shared/mach/semaphore.rb
174
189
  - lib/process_shared/process_error.rb
175
- - lib/process_shared/condition_variable.rb
176
- - lib/process_shared/define_singleton_method.rb
177
- - lib/process_shared/thread.rb
190
+ - lib/process_shared/rt.rb
191
+ - lib/process_shared/shared_array.rb
178
192
  - lib/process_shared/shared_memory_io.rb
179
- - lib/process_shared/mach.rb
180
- - lib/process_shared/time_spec.rb
181
- - lib/process_shared/abstract_semaphore.rb
182
- - lib/process_shared/binary_semaphore.rb
183
- - lib/process_shared/mutex.rb
184
- - lib/process_shared/monitor_mixin.rb
185
193
  - lib/process_shared/synchronizable_semaphore.rb
186
- - lib/process_shared/rt.rb
187
- - lib/process_shared/open_with_self.rb
188
- - lib/process_shared/object_buffer.rb
189
- - lib/process_shared/monitor.rb
190
- - lib/mach.rb
194
+ - lib/process_shared/thread.rb
195
+ - lib/process_shared/time_spec.rb
191
196
  - lib/scratch.rb
192
- - ext/helper/helper.c
193
- - ext/semaphore.c
194
- - ext/pthread_sync_helper/pthread_sync_helper.c
195
- - ext/helper/extconf.rb
196
- - ext/pthread_sync_helper/extconf.rb
197
- - spec/spec_helper.rb
198
197
  - spec/mach/port_spec.rb
199
- - spec/mach/task_spec.rb
200
- - spec/mach/semaphore_spec.rb
201
- - spec/mach/scratch2.rb
202
198
  - spec/mach/scratch.rb
203
- - spec/process_shared/shared_memory_spec.rb
204
- - spec/process_shared/shared_memory_io_spec.rb
205
- - spec/process_shared/lock_behavior.rb
199
+ - spec/mach/scratch2.rb
200
+ - spec/mach/semaphore_spec.rb
201
+ - spec/mach/task_spec.rb
206
202
  - spec/process_shared/binary_semaphore_spec.rb
207
- - spec/process_shared/shared_array_spec.rb
208
- - spec/process_shared/mutex_spec.rb
209
- - spec/process_shared/monitor_spec.rb
203
+ - spec/process_shared/condition_variable_spec.rb
204
+ - spec/process_shared/lock_behavior.rb
210
205
  - spec/process_shared/monitor_mixin_spec.rb
206
+ - spec/process_shared/monitor_spec.rb
207
+ - spec/process_shared/mutex_spec.rb
211
208
  - spec/process_shared/semaphore_spec.rb
212
- - spec/process_shared/condition_variable_spec.rb
213
- - README.rdoc
214
- - ChangeLog
215
- - COPYING
209
+ - spec/process_shared/shared_array_spec.rb
210
+ - spec/process_shared/shared_memory_io_spec.rb
211
+ - spec/process_shared/shared_memory_spec.rb
212
+ - spec/spec_helper.rb
216
213
  homepage: https://github.com/pmahoney/process_shared
217
214
  licenses: []
215
+ metadata: {}
218
216
  post_install_message:
219
217
  rdoc_options: []
220
218
  require_paths:
221
219
  - lib
222
220
  required_ruby_version: !ruby/object:Gem::Requirement
223
- none: false
224
221
  requirements:
225
- - - ! '>='
222
+ - - ">="
226
223
  - !ruby/object:Gem::Version
227
224
  version: '0'
228
- segments:
229
- - 0
230
- hash: -288015031839380416
231
225
  required_rubygems_version: !ruby/object:Gem::Requirement
232
- none: false
233
226
  requirements:
234
- - - ! '>='
227
+ - - ">="
235
228
  - !ruby/object:Gem::Version
236
229
  version: '0'
237
- segments:
238
- - 0
239
- hash: -288015031839380416
240
230
  requirements: []
241
231
  rubyforge_project:
242
- rubygems_version: 1.8.23
232
+ rubygems_version: 2.2.0
243
233
  signing_key:
244
- specification_version: 3
234
+ specification_version: 4
245
235
  summary: process-shared synchronization primitives
246
236
  test_files: []
@@ -1,99 +0,0 @@
1
- == Description
2
-
3
- Concurrency primitives that may be used in a cross-process way to
4
- coordinate share memory between processes.
5
-
6
- FFI is used to access POSIX semaphore on Linux or Mach semaphores on
7
- Mac. Atop these semaphores are implemented ProcessShared::Semaphore,
8
- ProcessShared::Mutex. POSIX shared memory is used to implement
9
- ProcessShared::SharedMemory.
10
-
11
- On Linux, POSIX semaphores support <tt>sem_timedwait()</tt> which can wait on
12
- a semaphore but stop waiting after a timeout.
13
-
14
- Mac OS X's implementation of POSIX semaphores does not support
15
- timeouts. But, the Mach layer in Mac OS X has its own semaphores that
16
- do support timeouts. Thus, process_shared implements a moderate
17
- subset of the Mach API, which is quite a bit different from POSIX.
18
- Namely, semaphores created in one process are not available in child
19
- processes created via <tt>fork()</tt>. Mach does provide the means to copy
20
- capabilities between tasks (Mach equivalent to processes).
21
- process_shared overrides Ruby's <tt>fork</tt> methods so that semaphores are
22
- copied from parent to child to emulate the POSIX behavior.
23
-
24
- This is an incomplete work in progress.
25
-
26
- == License
27
-
28
- MIT
29
-
30
- == Install
31
- Install the gem with:
32
-
33
- gem install process_shared
34
-
35
- == Usage
36
-
37
- require 'process_shared'
38
-
39
- mutex = ProcessShared::Mutex.new
40
- mem = ProcessShared::SharedMemory.new(:int) # extends FFI::Pointer
41
- mem.put_int(0, 0)
42
-
43
- pid1 = fork do
44
- puts "in process 1 (#{Process.pid})"
45
- 10.times do
46
- sleep 0.01
47
- mutex.synchronize do
48
- value = mem.get_int(0)
49
- sleep 0.01
50
- puts "process 1 (#{Process.pid}) incrementing"
51
- mem.put_int(0, value + 1)
52
- end
53
- end
54
- end
55
-
56
- pid2 = fork do
57
- puts "in process 2 (#{Process.pid})"
58
- 10.times do
59
- sleep 0.01
60
- mutex.synchronize do
61
- value = mem.get_int(0)
62
- sleep 0.01
63
- puts "process 2 (#{Process.pid}) decrementing"
64
- mem.put_int(0, value - 1)
65
- end
66
- end
67
- end
68
-
69
- Process.wait(pid1)
70
- Process.wait(pid2)
71
-
72
- puts "value should be zero: #{mem.get_int(0)}"
73
-
74
- == Transfer Objects Across Processes
75
-
76
- # allocate a sufficient memory block
77
- mem = ProcessShared::SharedMemory.new(1024)
78
-
79
- # sub process can write (serialize) object to memory (with bounds checking)
80
- pid = fork do
81
- mem.write_object(['a', 'b'])
82
- end
83
-
84
- Process.wait(pid)
85
-
86
- # parent process can read the object back (synchronizing access
87
- # with a Mutex left as an excercie to reader)
88
-
89
- mem.read_object.must_equal ['a', 'b']
90
-
91
- == Todo
92
-
93
- * Test ConditionVariable
94
- * Implement optional override of core Thread/Mutex classes
95
- * Extend to win32? (See Python's processing library)
96
- * Add finalizer to Mutex? (finalizer on Semaphore objects may be enough) or a method to
97
- explicitly close and release resources?
98
- * Test semantics of crashing processes who still hold locks, etc.
99
- * Is SharedArray with Enumerable mixing sufficient Array-like interface?