process_shared 0.2.0 → 0.2.1a

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,71 +0,0 @@
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
@@ -1,29 +0,0 @@
1
- # require 'process_shared/libc' - circular dependency here...
2
-
3
- module ProcessShared
4
- module PosixCall
5
- # Replace methods in +syms+ with error checking wrappers that
6
- # invoke the original method and raise a {SystemCallError} with
7
- # the current errno if the return value is an error.
8
- #
9
- # Errors are detected if the block returns true when called with
10
- # the original method's return value.
11
- def error_check(*syms, &is_err)
12
- unless block_given?
13
- is_err = lambda { |v| (v == -1) }
14
- end
15
-
16
- syms.each do |sym|
17
- method = self.method(sym)
18
- define_singleton_method(sym) do |*args|
19
- ret = method.call(*args)
20
- if is_err.call(ret)
21
- raise SystemCallError.new("error in #{sym}", LibC.errno)
22
- else
23
- ret
24
- end
25
- end
26
- end
27
- end
28
- end
29
- end
@@ -1,21 +0,0 @@
1
- require 'process_shared/posix_call'
2
- require 'process_shared/psem'
3
-
4
- module ProcessShared
5
- module RT
6
- extend FFI::Library
7
- extend PosixCall
8
-
9
- # FIXME: mac and linux OK, but what about everything else?
10
- if FFI::Platform.mac?
11
- ffi_lib 'c'
12
- else
13
- ffi_lib 'rt'
14
- end
15
-
16
- attach_function :shm_open, [:string, :int, :mode_t], :int
17
- attach_function :shm_unlink, [:string], :int
18
-
19
- error_check :shm_open, :shm_unlink
20
- end
21
- end
@@ -1,30 +0,0 @@
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
@@ -1,300 +0,0 @@
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