process_shared 0.1.7 → 0.1.8

Sign up to get free protection for your applications and to get access to all the features.
@@ -72,8 +72,8 @@ module Mach
72
72
 
73
73
  # Attach a function as with +attach_function+, but check the
74
74
  # return value and raise an exception on errors.
75
- def self.attach_mach_function(sym, argtypes, rettype)
76
- attach_function(sym, argtypes, rettype)
75
+ def self.attach_mach_function(sym, argtypes, rettype, options = nil)
76
+ attach_function(sym, argtypes, rettype, options)
77
77
  error_check(sym)
78
78
  end
79
79
 
@@ -208,10 +208,12 @@ module Mach
208
208
  :kern_return_t)
209
209
  attach_mach_function(:semaphore_wait,
210
210
  [:semaphore_t],
211
- :kern_return_t)
211
+ :kern_return_t,
212
+ :blocking => true)
212
213
  attach_mach_function(:semaphore_timedwait,
213
214
  [:semaphore_t, TimeSpec.val],
214
- :kern_return_t)
215
+ :kern_return_t,
216
+ :blocking => true)
215
217
 
216
218
  end
217
219
  end
@@ -26,9 +26,12 @@ module ProcessShared
26
26
 
27
27
  attach_function :sem_getvalue, [:sem_p, :pointer], :int
28
28
  attach_function :sem_post, [:sem_p], :int
29
- attach_function :sem_wait, [:sem_p], :int
30
- attach_function :sem_trywait, [:sem_p], :int
31
- attach_function :sem_timedwait, [:sem_p, TimeSpec], :int
29
+ attach_function :sem_wait, [:sem_p], :int, :blocking => true
30
+ attach_function :sem_trywait, [:sem_p], :int, :blocking => true
31
+
32
+ # Workaround bug which only appears to affect Ruby 1.8.7 and REE
33
+ BLOCKING_SEM_TIMEDWAIT = (RUBY_VERSION != '1.8.7')
34
+ attach_function :sem_timedwait, [:sem_p, TimeSpec], :int, :blocking => BLOCKING_SEM_TIMEDWAIT
32
35
 
33
36
  error_check(:sem_close, :sem_unlink, :sem_init, :sem_destroy,
34
37
  :sem_getvalue, :sem_post, :sem_wait, :sem_trywait,
@@ -0,0 +1,71 @@
1
+ module ProcessShared
2
+ module Posix
3
+ class SharedArray < SharedMemory
4
+ include Enumerable
5
+
6
+ # A fixed-size array in shared memory. Processes forked from this
7
+ # one will be able to read and write shared data to the array.
8
+ # Access should be synchronized using a {Mutex}, {Semaphore}, or
9
+ # other means.
10
+ #
11
+ # Note that {Enumerable} methods such as {#map}, {#sort},
12
+ # etc. return new {Array} objects rather than modifying the shared
13
+ # array.
14
+ #
15
+ # @param [Symbol] type_or_count the data type as a symbol
16
+ # understood by FFI (e.g. :int, :double)
17
+ #
18
+ # @param [Integer] count number of array elements
19
+ def initialize(type_or_count = 1, count = 1)
20
+ super(type_or_count, count)
21
+
22
+ # See https://github.com/ffi/ffi/issues/118
23
+ ffi_type = FFI.find_type(self.type)
24
+
25
+ name = if ffi_type.inspect =~ /FFI::Type::Builtin:(\w+)*/
26
+ # name will be something like int32
27
+ $1.downcase
28
+ end
29
+
30
+ unless name
31
+ raise ArgumentError, "could not find FFI::Type for #{self.type}"
32
+ end
33
+
34
+ getter = "get_#{name}"
35
+ setter = "put_#{name}"
36
+
37
+ # singleton class
38
+ sclass = class << self; self; end
39
+
40
+ unless sclass.method_defined?(getter)
41
+ raise ArgumentError, "no element getter for #{self.type} (#{getter})"
42
+ end
43
+
44
+ unless sclass.method_defined?(setter)
45
+ raise ArgumentError, "no element setter for #{self.type} (#{setter})"
46
+ end
47
+
48
+ sclass.send(:alias_method, :get_type, getter)
49
+ sclass.send(:alias_method, :put_type, setter)
50
+ end
51
+
52
+ def each
53
+ # NOTE: using @count because Enumerable defines its own count
54
+ # method...
55
+ @count.times { |i| yield self[i] }
56
+ end
57
+
58
+ def each_with_index
59
+ @count.times { |i| yield self[i], i }
60
+ end
61
+
62
+ def [](i)
63
+ get_type(i * self.type_size)
64
+ end
65
+
66
+ def []=(i, val)
67
+ put_type(i * self.type_size, val)
68
+ end
69
+ end
70
+ end
71
+ 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,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,67 @@
1
+ require 'mach'
2
+ require 'mach/functions'
3
+
4
+ include Mach
5
+ include Mach::Functions
6
+
7
+ sem = Semaphore.new(:value => 0)
8
+
9
+ # puts ">parent has sem #{sem.port}"
10
+
11
+ # fork do
12
+ # sleep 2 # make parent wait a bit
13
+ # puts "in child..."
14
+ # sem = Mach::Semaphore.new(:port => Mach::Task.self.get_bootstrap_port)
15
+ # puts "child signaling sem #{sem.port}"
16
+ # sem.signal
17
+ # end
18
+
19
+ # puts ">parent waiting on sem..."
20
+ # sem.wait
21
+ # puts ">parent done waiting!"
22
+
23
+ def struct(*layout)
24
+ yield FFI::Struct.new(nil, *layout)
25
+ end
26
+
27
+ puts "sizeof MsgPortDescriptor: #{MsgPortDescriptor.size}"
28
+ port = Port.new
29
+ port.insert_right(:make_send)
30
+
31
+ Task.self.set_bootstrap_port(port)
32
+ puts "> self:#{mach_task_self} bootstrap:#{port.port} (#{Mach::Functions::bootstrap_port.to_i})"
33
+
34
+ child = fork do
35
+ parent_port = Task.self.get_bootstrap_port
36
+ puts "in child... self:#{mach_task_self} bootstrap:#{parent_port.port}"
37
+
38
+ #port = Port.new
39
+ #port.copy_send(parent_port)
40
+
41
+ #sem = port.receive_right
42
+
43
+ Task.self.copy_send(parent_port)
44
+ puts "child sleeping"
45
+ sleep 2
46
+ puts "child signaling semaphore"
47
+ sem.signal
48
+ puts "child out"
49
+ sleep 2
50
+ end
51
+
52
+ sleep 0.1
53
+ if Process.wait(child, Process::WNOHANG)
54
+ puts "child died!"
55
+ #exit 1
56
+ end
57
+
58
+ Task.self.set_bootstrap_port(Mach::Functions::bootstrap_port)
59
+ child_task_port = port.receive_right
60
+ puts "parent: child task port is #{child_task_port}"
61
+
62
+ sem.insert_right(:copy_send, :ipc_space => child_task_port)
63
+
64
+ puts "parent waiting"
65
+ sem.wait
66
+ puts "parent done waiting!"
67
+