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.
- checksums.yaml +7 -0
- data/README.md +123 -0
- data/lib/process_shared/synchronizable_semaphore.rb +69 -0
- data/spec/process_shared/semaphore_spec.rb +43 -5
- metadata +84 -94
- data/README.rdoc +0 -99
checksums.yaml
ADDED
@@ -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
|
data/README.md
ADDED
@@ -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.
|
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-
|
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.
|
147
|
+
- README.md
|
151
148
|
- ChangeLog
|
152
149
|
- COPYING
|
153
150
|
files:
|
154
|
-
-
|
155
|
-
-
|
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/
|
163
|
-
- lib/
|
164
|
-
- lib/process_shared/
|
165
|
-
- lib/process_shared/
|
166
|
-
- lib/process_shared/
|
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/
|
176
|
-
- lib/process_shared/
|
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/
|
187
|
-
- lib/process_shared/
|
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/
|
204
|
-
- spec/
|
205
|
-
- spec/
|
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/
|
208
|
-
- spec/process_shared/
|
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/
|
213
|
-
-
|
214
|
-
-
|
215
|
-
-
|
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:
|
232
|
+
rubygems_version: 2.2.0
|
243
233
|
signing_key:
|
244
|
-
specification_version:
|
234
|
+
specification_version: 4
|
245
235
|
summary: process-shared synchronization primitives
|
246
236
|
test_files: []
|
data/README.rdoc
DELETED
@@ -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?
|