process_shared 0.1.10 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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?