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,45 @@
|
|
1
|
+
require 'process_shared/rt'
|
2
|
+
require 'process_shared/libc'
|
3
|
+
require 'process_shared/with_self'
|
4
|
+
|
5
|
+
module ProcessShared
|
6
|
+
# Memory block shared across processes. TODO: finalizer that closes...
|
7
|
+
class SharedMemory < FFI::Pointer
|
8
|
+
include WithSelf
|
9
|
+
|
10
|
+
attr_reader :size, :fd
|
11
|
+
|
12
|
+
def self.open(size, &block)
|
13
|
+
new(size).with_self(&block)
|
14
|
+
end
|
15
|
+
|
16
|
+
def initialize(size)
|
17
|
+
@size = case size
|
18
|
+
when Symbol
|
19
|
+
FFI.type_size(size)
|
20
|
+
else
|
21
|
+
size
|
22
|
+
end
|
23
|
+
|
24
|
+
name = "/ps-shm#{rand(10000)}"
|
25
|
+
@fd = RT.shm_open(name,
|
26
|
+
LibC::O_CREAT | LibC::O_RDWR | LibC::O_EXCL,
|
27
|
+
0777)
|
28
|
+
RT.shm_unlink(name)
|
29
|
+
|
30
|
+
LibC.ftruncate(@fd, @size)
|
31
|
+
@pointer = LibC.mmap(nil,
|
32
|
+
@size,
|
33
|
+
LibC::PROT_READ | LibC::PROT_WRITE,
|
34
|
+
LibC::MAP_SHARED,
|
35
|
+
@fd,
|
36
|
+
0)
|
37
|
+
super(@pointer)
|
38
|
+
end
|
39
|
+
|
40
|
+
def close
|
41
|
+
LibC.munmap(@pointer, @size)
|
42
|
+
LibC.close(@fd)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module ProcessShared
|
2
|
+
# API-compatible with Ruby Thread class but using ProcessShared
|
3
|
+
# primitives instead (i.e. each Thread will be a separate OS
|
4
|
+
# process).
|
5
|
+
class Process
|
6
|
+
class << self
|
7
|
+
# How the heck will I implement this...
|
8
|
+
def abort_on_exception
|
9
|
+
end
|
10
|
+
|
11
|
+
def abort_on_exception=(val)
|
12
|
+
end
|
13
|
+
|
14
|
+
# This can't really work since each thread is a separate process..
|
15
|
+
def current
|
16
|
+
end
|
17
|
+
|
18
|
+
def kill(process)
|
19
|
+
::Process.kill(process.pid)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def join(limit = nil)
|
24
|
+
if limit
|
25
|
+
else
|
26
|
+
::Process.wait(pid)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module ProcessShared
|
2
|
+
module WithSelf
|
3
|
+
# With no associated block, return self. If the optional code
|
4
|
+
# block is given, it will be passed `self` as an argument, and the
|
5
|
+
# self object will automatically be closed (by invoking `close` on
|
6
|
+
# `self`) when the block terminates. In this instance, value of
|
7
|
+
# the block is returned.
|
8
|
+
def with_self
|
9
|
+
if block_given?
|
10
|
+
begin
|
11
|
+
yield self
|
12
|
+
ensure
|
13
|
+
self.close
|
14
|
+
end
|
15
|
+
else
|
16
|
+
self
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
data/lib/scratch.rb
ADDED
@@ -0,0 +1,300 @@
|
|
1
|
+
require 'ffi'
|
2
|
+
|
3
|
+
module LibC
|
4
|
+
extend FFI::Library
|
5
|
+
|
6
|
+
ffi_lib FFI::Library::LIBC
|
7
|
+
|
8
|
+
attach_variable :errno, :int
|
9
|
+
|
10
|
+
attach_function :mmap, [:pointer, :size_t, :int, :int, :int, :off_t], :pointer
|
11
|
+
attach_function :munmap, [:pointer, :size_t], :int
|
12
|
+
|
13
|
+
attach_function :ftruncate, [:int, :off_t], :int
|
14
|
+
|
15
|
+
class << self
|
16
|
+
def call(err_msg = 'error in system call', &block)
|
17
|
+
ret = yield
|
18
|
+
err = LibC.errno
|
19
|
+
|
20
|
+
if ret.kind_of?(Fixnum) and ret < 0
|
21
|
+
raise SystemCallError.new(err_msg, err)
|
22
|
+
end
|
23
|
+
|
24
|
+
ret
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
module RT
|
30
|
+
extend FFI::Library
|
31
|
+
|
32
|
+
ffi_lib 'rt'
|
33
|
+
|
34
|
+
attach_function :shm_open, [:string, :int, :mode_t], :int
|
35
|
+
attach_function :shm_unlink, [:string], :int
|
36
|
+
end
|
37
|
+
|
38
|
+
module PSem
|
39
|
+
extend FFI::Library
|
40
|
+
|
41
|
+
lib = File.join(File.expand_path(File.dirname(__FILE__)),
|
42
|
+
'process_shared/libpsem.' + FFI::Platform::LIBSUFFIX)
|
43
|
+
ffi_lib lib
|
44
|
+
|
45
|
+
attach_function :psem_alloc, [], :pointer
|
46
|
+
attach_function :psem_free, [:pointer], :void
|
47
|
+
|
48
|
+
attach_function :psem_open, [:pointer, :string, :uint, :uint], :int
|
49
|
+
attach_function :psem_close, [:pointer], :int
|
50
|
+
attach_function :psem_unlink, [:string], :int
|
51
|
+
attach_function :psem_post, [:pointer], :int
|
52
|
+
attach_function :psem_wait, [:pointer], :int
|
53
|
+
attach_function :psem_trywait, [:pointer], :int
|
54
|
+
attach_function :psem_timedwait, [:pointer, :pointer], :int
|
55
|
+
attach_function :psem_getvalue, [:pointer, :pointer], :int
|
56
|
+
|
57
|
+
attach_function :bsem_alloc, [], :pointer
|
58
|
+
attach_function :bsem_free, [:pointer], :void
|
59
|
+
|
60
|
+
attach_function :bsem_open, [:pointer, :string, :uint, :uint], :int
|
61
|
+
attach_function :bsem_close, [:pointer], :int
|
62
|
+
attach_function :bsem_unlink, [:string], :int
|
63
|
+
attach_function :bsem_post, [:pointer], :int
|
64
|
+
attach_function :bsem_wait, [:pointer], :int
|
65
|
+
attach_function :bsem_trywait, [:pointer], :int
|
66
|
+
attach_function :bsem_timedwait, [:pointer, :pointer], :int
|
67
|
+
attach_function :bsem_getvalue, [:pointer, :pointer], :int
|
68
|
+
|
69
|
+
class << self
|
70
|
+
include PSem
|
71
|
+
|
72
|
+
def test
|
73
|
+
bsem = bsem_alloc()
|
74
|
+
|
75
|
+
class << bsem
|
76
|
+
def value
|
77
|
+
@int ||= FFI::MemoryPointer.new(:int)
|
78
|
+
LibC.call { PSem.bsem_getvalue(self, @int) }
|
79
|
+
@int.get_int(0)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
puts "alloc'ed at #{bsem.inspect}"
|
84
|
+
puts LibC.call { bsem_open(bsem, "foobar", 1, 1) }
|
85
|
+
puts "opened at #{bsem.inspect}"
|
86
|
+
puts LibC.call { bsem_unlink("foobar") }
|
87
|
+
puts "unlinked"
|
88
|
+
|
89
|
+
puts "waiting for sem..."
|
90
|
+
puts "value is #{bsem.value}"
|
91
|
+
LibC.call { bsem_wait(bsem) }
|
92
|
+
puts "acquired!"
|
93
|
+
puts "value is #{bsem.value}"
|
94
|
+
LibC.call { bsem_post(bsem) }
|
95
|
+
puts "posted!"
|
96
|
+
puts "value is #{bsem.value}"
|
97
|
+
|
98
|
+
puts LibC.call { bsem_close(bsem) }
|
99
|
+
bsem_free(bsem)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
module PThread
|
105
|
+
extend FFI::Library
|
106
|
+
|
107
|
+
ffi_lib '/lib/x86_64-linux-gnu/libpthread-2.13.so' # 'pthread'
|
108
|
+
|
109
|
+
attach_function :pthread_mutex_init, [:pointer, :pointer], :int
|
110
|
+
attach_function :pthread_mutex_lock, [:pointer], :int
|
111
|
+
attach_function :pthread_mutex_trylock, [:pointer], :int
|
112
|
+
attach_function :pthread_mutex_unlock, [:pointer], :int
|
113
|
+
attach_function :pthread_mutex_destroy, [:pointer], :int
|
114
|
+
|
115
|
+
attach_function :pthread_mutexattr_init, [:pointer], :int
|
116
|
+
attach_function :pthread_mutexattr_settype, [:pointer, :int], :int
|
117
|
+
attach_function :pthread_mutexattr_gettype, [:pointer, :pointer], :int
|
118
|
+
|
119
|
+
attach_function :pthread_mutexattr_setpshared, [:pointer, :int], :int
|
120
|
+
|
121
|
+
class << self
|
122
|
+
def call(err_msg = 'error in pthreads', &block)
|
123
|
+
ret = yield
|
124
|
+
raise SystemCallError.new(err_msg, ret) unless ret == 0
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
module Helper
|
129
|
+
extend FFI::Library
|
130
|
+
|
131
|
+
# FIXME: this might not alwasy be ".so"
|
132
|
+
lib = File.join(File.expand_path(File.dirname(__FILE__)), 'pthread_sync_helper.so')
|
133
|
+
ffi_lib lib
|
134
|
+
|
135
|
+
attach_variable :sizeof_pthread_mutex_t, :size_t
|
136
|
+
attach_variable :sizeof_pthread_mutexattr_t, :size_t
|
137
|
+
|
138
|
+
attach_variable :o_rdwr, :int
|
139
|
+
attach_variable :o_creat, :int
|
140
|
+
|
141
|
+
[:pthread_process_shared,
|
142
|
+
|
143
|
+
:o_rdwr,
|
144
|
+
:o_creat,
|
145
|
+
|
146
|
+
:prot_read,
|
147
|
+
:prot_write,
|
148
|
+
:prot_exec,
|
149
|
+
:prot_none,
|
150
|
+
|
151
|
+
:map_shared,
|
152
|
+
:map_private].each do |sym|
|
153
|
+
attach_variable sym, :int
|
154
|
+
end
|
155
|
+
|
156
|
+
attach_variable :map_failed, :pointer
|
157
|
+
|
158
|
+
PTHREAD_PROCESS_SHARED = pthread_process_shared
|
159
|
+
|
160
|
+
O_RDWR = o_rdwr
|
161
|
+
O_CREAT = o_creat
|
162
|
+
|
163
|
+
PROT_READ = prot_read
|
164
|
+
PROT_WRITE = prot_write
|
165
|
+
PROT_EXEC = prot_exec
|
166
|
+
PROT_NONE = prot_none
|
167
|
+
|
168
|
+
MAP_FAILED = map_failed
|
169
|
+
MAP_SHARED = map_shared
|
170
|
+
MAP_PRIVATE = map_private
|
171
|
+
end
|
172
|
+
|
173
|
+
class Mutex
|
174
|
+
include PThread
|
175
|
+
include PThread::Helper
|
176
|
+
|
177
|
+
class << self
|
178
|
+
def alloc
|
179
|
+
FFI::MemoryPointer.new(Helper.sizeof_pthread_mutex_t)
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
def initialize(mutex = Mutex.alloc, attr = nil)
|
184
|
+
@mutex = mutex
|
185
|
+
PThread.call { pthread_mutex_init(@mutex, attr) }
|
186
|
+
end
|
187
|
+
|
188
|
+
def destroy
|
189
|
+
PThread.call { pthread_mutex_destroy(@mutex) }
|
190
|
+
end
|
191
|
+
|
192
|
+
def lock
|
193
|
+
PThread.call { pthread_mutex_lock(@mutex) }
|
194
|
+
end
|
195
|
+
|
196
|
+
def try_lock
|
197
|
+
PThread.call { pthread_mutex_trylock(@mutex) }
|
198
|
+
end
|
199
|
+
|
200
|
+
def unlock
|
201
|
+
PThread.call { pthread_mutex_unlock(@mutex) }
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
class MutexAttr
|
206
|
+
include PThread
|
207
|
+
include PThread::Helper
|
208
|
+
|
209
|
+
class << self
|
210
|
+
def alloc
|
211
|
+
FFI::MemoryPointer.new(Helper.sizeof_pthread_mutexattr_t)
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
def initialize(ptr = MutexAttr.alloc)
|
216
|
+
puts "have #{ptr}"
|
217
|
+
@ptr = ptr
|
218
|
+
PThread.call { pthread_mutexattr_init(@ptr) }
|
219
|
+
self.type = type if type
|
220
|
+
end
|
221
|
+
|
222
|
+
def pointer
|
223
|
+
@ptr
|
224
|
+
end
|
225
|
+
|
226
|
+
def pshared=(val)
|
227
|
+
PThread.call { pthread_mutexattr_setpshared(@ptr, val) }
|
228
|
+
end
|
229
|
+
|
230
|
+
def type=(type)
|
231
|
+
PThread.call { pthread_mutexattr_settype(@ptr, type) }
|
232
|
+
end
|
233
|
+
|
234
|
+
def type
|
235
|
+
t = FFI::MemoryPointer.new(:int)
|
236
|
+
PThread.call { pthread_mutexattr_gettype(@ptr, t) }
|
237
|
+
t.get_int(0)
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
class Int < FFI::Struct
|
242
|
+
layout :val => :int
|
243
|
+
end
|
244
|
+
|
245
|
+
class << self
|
246
|
+
include Helper
|
247
|
+
|
248
|
+
def test
|
249
|
+
puts "hi #{Helper.sizeof_pthread_mutex_t}"
|
250
|
+
puts Mutex.new
|
251
|
+
|
252
|
+
|
253
|
+
fd = LibC.call { RT.shm_open("/foo", O_CREAT | O_RDWR, 0777) }
|
254
|
+
LibC.call { LibC.ftruncate(fd, 100) }
|
255
|
+
pointer = LibC.mmap(nil, 100, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)
|
256
|
+
puts pointer
|
257
|
+
puts pointer == MAP_FAILED
|
258
|
+
puts MAP_FAILED
|
259
|
+
|
260
|
+
attr = MutexAttr.new
|
261
|
+
attr.pshared = PTHREAD_PROCESS_SHARED
|
262
|
+
|
263
|
+
mutex = Mutex.new(pointer, attr.pointer)
|
264
|
+
|
265
|
+
|
266
|
+
fd = LibC.call { RT.shm_open("/someint", O_CREAT | O_RDWR, 0777) }
|
267
|
+
LibC.call { LibC.ftruncate(fd, 100) }
|
268
|
+
pointer = LibC.mmap(nil, 100, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)
|
269
|
+
abort "failed" if pointer == MAP_FAILED
|
270
|
+
|
271
|
+
value = Int.new(pointer)
|
272
|
+
value[:val] = 0
|
273
|
+
puts "int[0]: #{value[:val]}"
|
274
|
+
|
275
|
+
puts "parent has mutex #{mutex}"
|
276
|
+
|
277
|
+
n = 10000
|
278
|
+
|
279
|
+
child = fork do
|
280
|
+
puts "child and I have mutex: #{mutex}"
|
281
|
+
|
282
|
+
n.times do |i|
|
283
|
+
mutex.lock
|
284
|
+
value[:val] = (value[:val] + 1)
|
285
|
+
mutex.unlock
|
286
|
+
end
|
287
|
+
end
|
288
|
+
|
289
|
+
n.times do |i|
|
290
|
+
mutex.lock
|
291
|
+
value[:val] = (value[:val] + 1)
|
292
|
+
mutex.unlock
|
293
|
+
end
|
294
|
+
|
295
|
+
Process.wait(child)
|
296
|
+
|
297
|
+
puts "value is now #{value[:val]}"
|
298
|
+
end
|
299
|
+
end
|
300
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'process_shared/bounded_semaphore'
|
3
|
+
|
4
|
+
module ProcessShared
|
5
|
+
describe BoundedSemaphore do
|
6
|
+
it 'never rises above its max value' do
|
7
|
+
max = 10
|
8
|
+
BoundedSemaphore.open(max) do |sem|
|
9
|
+
pids = []
|
10
|
+
10.times do |i|
|
11
|
+
pids << fork do
|
12
|
+
100.times do
|
13
|
+
if rand(3) == 0
|
14
|
+
sem.wait
|
15
|
+
else
|
16
|
+
sem.post
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
exit i
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
100.times do
|
25
|
+
sem.value.must be_lte(max)
|
26
|
+
end
|
27
|
+
|
28
|
+
pids.each { |pid| Process.wait(pid) }
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe '#post and #wait' do
|
33
|
+
it 'increments and decrements the value' do
|
34
|
+
Semaphore.open(0) do |sem|
|
35
|
+
10.times do |i|
|
36
|
+
sem.post
|
37
|
+
sem.value.must_equal(i + 1)
|
38
|
+
end
|
39
|
+
|
40
|
+
10.times do |i|
|
41
|
+
sem.wait
|
42
|
+
sem.value.must_equal(10 - i - 1)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|