process_shared 0.0.4 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/ext/helper/extconf.rb +14 -0
- data/ext/helper/helper.c +70 -0
- data/lib/mach.rb +12 -0
- data/lib/mach/clock.rb +24 -0
- data/lib/mach/error.rb +56 -0
- data/lib/mach/functions.rb +342 -0
- data/lib/mach/host.rb +28 -0
- data/lib/mach/port.rb +143 -0
- data/lib/mach/semaphore.rb +74 -0
- data/lib/mach/task.rb +42 -0
- data/lib/mach/time_spec.rb +16 -0
- data/lib/process_shared.rb +17 -1
- data/lib/process_shared/binary_semaphore.rb +18 -7
- data/lib/process_shared/mach.rb +93 -0
- data/lib/process_shared/mach/semaphore.rb +39 -0
- data/lib/process_shared/posix/errno.rb +40 -0
- data/lib/process_shared/posix/libc.rb +78 -0
- data/lib/process_shared/posix/semaphore.rb +125 -0
- data/lib/process_shared/posix/shared_array.rb +71 -0
- data/lib/process_shared/posix/shared_memory.rb +82 -0
- data/lib/process_shared/posix/time_spec.rb +13 -0
- data/lib/process_shared/posix/time_val.rb +23 -0
- data/lib/process_shared/semaphore.rb +26 -73
- data/lib/process_shared/shared_array.rb +3 -1
- data/lib/process_shared/shared_memory.rb +10 -52
- data/lib/process_shared/time_spec.rb +22 -0
- data/spec/mach/port_spec.rb +21 -0
- data/spec/mach/scratch.rb +67 -0
- data/spec/mach/scratch2.rb +78 -0
- data/spec/mach/semaphore_spec.rb +60 -0
- data/spec/mach/task_spec.rb +31 -0
- data/spec/process_shared/scratch.rb +21 -0
- data/spec/process_shared/semaphore_spec.rb +12 -11
- data/spec/process_shared/shared_memory_spec.rb +1 -1
- metadata +46 -36
- data/ext/libpsem/bsem.c +0 -188
- data/ext/libpsem/bsem.h +0 -32
- data/ext/libpsem/constants.c +0 -22
- data/ext/libpsem/constants.h +0 -18
- data/ext/libpsem/extconf.rb +0 -40
- data/ext/libpsem/mempcpy.c +0 -7
- data/ext/libpsem/mempcpy.h +0 -13
- data/ext/libpsem/mutex.c +0 -15
- data/ext/libpsem/mutex.h +0 -14
- data/ext/libpsem/psem.c +0 -15
- data/ext/libpsem/psem.h +0 -45
- data/ext/libpsem/psem_error.c +0 -46
- data/ext/libpsem/psem_error.h +0 -11
- data/ext/libpsem/psem_posix.c +0 -160
- data/ext/libpsem/psem_posix.h +0 -10
- data/lib/process_shared/bounded_semaphore.rb +0 -46
- data/lib/process_shared/libc.rb +0 -36
- data/lib/process_shared/libpsem.bundle +0 -0
- data/lib/process_shared/libpsem.so +0 -0
- data/lib/process_shared/psem.rb +0 -113
- data/spec/process_shared/bounded_semaphore_spec.rb +0 -48
- data/spec/process_shared/libc_spec.rb +0 -9
- data/spec/process_shared/psem_spec.rb +0 -136
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'ffi'
|
2
|
+
|
3
|
+
require 'process_shared/posix/time_spec'
|
4
|
+
|
5
|
+
module ProcessShared
|
6
|
+
module Posix
|
7
|
+
class TimeVal < FFI::Struct
|
8
|
+
US_PER_NS = 1000
|
9
|
+
|
10
|
+
layout(:tv_sec, :time_t,
|
11
|
+
:tv_usec, :suseconds_t)
|
12
|
+
|
13
|
+
def to_time_spec
|
14
|
+
ts = TimeSpec.new
|
15
|
+
|
16
|
+
ts[:tv_sec] = self[:tv_sec];
|
17
|
+
ts[:tv_nsec] = self[:tv_usec] * US_PER_NS
|
18
|
+
|
19
|
+
ts
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -1,84 +1,37 @@
|
|
1
|
-
require 'process_shared/
|
2
|
-
require 'process_shared/abstract_semaphore'
|
1
|
+
require 'process_shared/with_self'
|
3
2
|
|
4
3
|
module ProcessShared
|
5
|
-
|
6
|
-
|
7
|
-
# Semaphore.new. If the optional code block is given, it will be
|
8
|
-
# passed +sem+ as an argument, and the Semaphore object will
|
9
|
-
# automatically be closed when the block terminates. In this
|
10
|
-
# instance, Semaphore.open returns the value of the block.
|
11
|
-
#
|
12
|
-
# @param [Integer] value the initial semaphore value
|
13
|
-
# @param [String] name not currently supported
|
14
|
-
def self.open(value = 1, name = nil, &block)
|
15
|
-
new(value, name).with_self(&block)
|
16
|
-
end
|
4
|
+
module Semaphore
|
5
|
+
include ProcessShared::WithSelf
|
17
6
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
# resort).
|
26
|
-
#
|
27
|
-
# @param [Integer] value the initial semaphore value
|
28
|
-
# @param [String] name not currently supported
|
29
|
-
def initialize(value = 1, name = nil)
|
30
|
-
init(PSem.sizeof_psem_t, 'psem', name) do |sem_name|
|
31
|
-
psem_open(sem, sem_name, value, err)
|
7
|
+
class << self
|
8
|
+
# the implementation to use to create semaphores. impl is set
|
9
|
+
# based on the platform in 'process_shared'
|
10
|
+
attr_accessor :impl
|
11
|
+
|
12
|
+
def new(*args)
|
13
|
+
impl.new(*args)
|
32
14
|
end
|
33
|
-
end
|
34
15
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
#
|
45
|
-
# @param timeout [Numeric] the maximum seconds to wait, or nil to not wait
|
46
|
-
#
|
47
|
-
# @return If +timeout+ is nil and the semaphore cannot be
|
48
|
-
# decremented immediately, raise Errno::EAGAIN. If +timeout+
|
49
|
-
# passed before the semaphore could be decremented, raise
|
50
|
-
# Errno::ETIMEDOUT.
|
51
|
-
def try_wait(timeout = nil)
|
52
|
-
if timeout
|
53
|
-
psem_timedwait(sem, timeout, err)
|
54
|
-
else
|
55
|
-
psem_trywait(sem, err)
|
16
|
+
# With no associated block, open is a synonym for
|
17
|
+
# Semaphore.new. If the optional code block is given, it will be
|
18
|
+
# passed +sem+ as an argument, and the Semaphore object will
|
19
|
+
# automatically be closed when the block terminates. In this
|
20
|
+
# instance, Semaphore.open returns the value of the block.
|
21
|
+
#
|
22
|
+
# @param [Integer] value the initial semaphore value
|
23
|
+
def open(value = 1, &block)
|
24
|
+
new(value).with_self(&block)
|
56
25
|
end
|
57
26
|
end
|
58
27
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
# platforms that don't support this (e.g. Mac OS X).
|
67
|
-
#
|
68
|
-
# @return [Integer] the current value of the semaphore.
|
69
|
-
def value
|
70
|
-
int = FFI::MemoryPointer.new(:int)
|
71
|
-
psem_getvalue(sem, int, err)
|
72
|
-
int.get_int(0)
|
73
|
-
end
|
74
|
-
|
75
|
-
# Release the resources associated with this semaphore. Calls to
|
76
|
-
# other methods are undefined after {#close} has been called.
|
77
|
-
#
|
78
|
-
# Close must be called when the semaphore is no longer needed. An
|
79
|
-
# object finalizer will close the semaphore as a last resort.
|
80
|
-
def close
|
81
|
-
psem_close(sem, err)
|
28
|
+
def synchronize
|
29
|
+
wait
|
30
|
+
begin
|
31
|
+
yield
|
32
|
+
ensure
|
33
|
+
post
|
34
|
+
end
|
82
35
|
end
|
83
36
|
end
|
84
37
|
end
|
@@ -1,58 +1,21 @@
|
|
1
|
-
require 'process_shared/rt'
|
2
|
-
require 'process_shared/libc'
|
3
1
|
require 'process_shared/with_self'
|
4
2
|
require 'process_shared/shared_memory_io'
|
5
3
|
|
6
4
|
module ProcessShared
|
7
5
|
# Memory block shared across processes.
|
8
|
-
|
9
|
-
include WithSelf
|
6
|
+
module SharedMemory
|
7
|
+
include ProcessShared::WithSelf
|
10
8
|
|
11
|
-
|
9
|
+
class << self
|
10
|
+
attr_accessor :impl
|
12
11
|
|
13
|
-
|
14
|
-
|
15
|
-
end
|
16
|
-
|
17
|
-
def self.make_finalizer(addr, size, fd)
|
18
|
-
proc do
|
19
|
-
pointer = FFI::Pointer.new(addr)
|
20
|
-
LibC.munmap(pointer, size)
|
21
|
-
LibC.close(fd)
|
12
|
+
def new(*args)
|
13
|
+
impl.new(*args)
|
22
14
|
end
|
23
|
-
end
|
24
|
-
|
25
|
-
def initialize(type_or_count = 1, count = 1)
|
26
|
-
@type, @count = case type_or_count
|
27
|
-
when Symbol
|
28
|
-
[type_or_count, count]
|
29
|
-
else
|
30
|
-
[:uchar, type_or_count]
|
31
|
-
end
|
32
|
-
|
33
|
-
@type_size = FFI.type_size(@type)
|
34
|
-
@size = @type_size * @count
|
35
15
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
0777)
|
40
|
-
RT.shm_unlink(name)
|
41
|
-
|
42
|
-
LibC.ftruncate(@fd, @size)
|
43
|
-
@pointer = LibC.mmap(nil,
|
44
|
-
@size,
|
45
|
-
LibC::PROT_READ | LibC::PROT_WRITE,
|
46
|
-
LibC::MAP_SHARED,
|
47
|
-
@fd,
|
48
|
-
0).
|
49
|
-
slice(0, size) # slice to get FFI::Pointer that knows its size
|
50
|
-
# (and thus does bounds checking)
|
51
|
-
|
52
|
-
@finalize = self.class.make_finalizer(@pointer.address, @size, @fd)
|
53
|
-
ObjectSpace.define_finalizer(self, @finalize)
|
54
|
-
|
55
|
-
super(@pointer)
|
16
|
+
def open(size, &block)
|
17
|
+
new(size).with_self(&block)
|
18
|
+
end
|
56
19
|
end
|
57
20
|
|
58
21
|
# Write the serialization of +obj+ (using Marshal.dump) to this
|
@@ -99,12 +62,7 @@ module ProcessShared
|
|
99
62
|
Marshal.load(to_shm_io)
|
100
63
|
end
|
101
64
|
|
102
|
-
|
103
|
-
ObjectSpace.undefine_finalizer(self)
|
104
|
-
@finalize.call
|
105
|
-
end
|
106
|
-
|
107
|
-
private
|
65
|
+
protected
|
108
66
|
|
109
67
|
def to_shm_io
|
110
68
|
SharedMemoryIO.new(self)
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module ProcessShared
|
2
|
+
module TimeSpec
|
3
|
+
NS_PER_S = 1e9
|
4
|
+
US_PER_NS = 1000
|
5
|
+
TV_NSEC_MAX = (NS_PER_S - 1)
|
6
|
+
|
7
|
+
# Assuming self responds to setting the value of [:tv_sec] and
|
8
|
+
# [:tv_nsec], add +secs+ to the time spec.
|
9
|
+
def add_seconds!(float_sec)
|
10
|
+
# add timeout in seconds to abs_timeout; careful with rounding
|
11
|
+
sec = float_sec.floor
|
12
|
+
nsec = ((float_sec - sec) * NS_PER_S).floor
|
13
|
+
|
14
|
+
self[:tv_sec] += sec
|
15
|
+
self[:tv_nsec] += nsec
|
16
|
+
while self[:tv_nsec] > TV_NSEC_MAX
|
17
|
+
self[:tv_sec] += 1
|
18
|
+
self[:tv_nsec] -= NS_PER_S
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'mach/port'
|
3
|
+
|
4
|
+
module Mach
|
5
|
+
describe Port do
|
6
|
+
it 'creates a port' do
|
7
|
+
port = Port.new
|
8
|
+
port.destroy
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'raises exception with invalid args' do
|
12
|
+
p = proc { Port.new(:right => 1234) }
|
13
|
+
p.must_raise Error::FAILURE
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'inserts rights' do
|
17
|
+
port = Port.new
|
18
|
+
port.insert_right(:make_send)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
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
|
+
|
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'ffi'
|
2
|
+
|
3
|
+
require 'mach'
|
4
|
+
require 'mach/functions'
|
5
|
+
|
6
|
+
include Mach
|
7
|
+
include Mach::Functions
|
8
|
+
|
9
|
+
def setup_recv_port
|
10
|
+
port = new_memory_pointer :mach_port_t
|
11
|
+
mach_port_allocate(mach_task_self, :receive, port)
|
12
|
+
p = port.get_uint(0)
|
13
|
+
mach_port_insert_right(mach_task_self, p, p, :make_send)
|
14
|
+
p
|
15
|
+
end
|
16
|
+
|
17
|
+
def send_port(remote_port, port)
|
18
|
+
puts "send_port: (in #{mach_task_self}) sending #{port} -> #{remote_port}"
|
19
|
+
msg = FFI::Struct.new(nil,
|
20
|
+
:header, MsgHeader,
|
21
|
+
:body, MsgBody,
|
22
|
+
:task_port, MsgPortDescriptor)
|
23
|
+
msg[:header].tap do |h|
|
24
|
+
h[:remote_port] = remote_port
|
25
|
+
h[:local_port] = 0
|
26
|
+
h[:bits] = (MachMsgType[:copy_send] | (0 << 8)) | 0x80000000 # MACH_MSGH_BITS_COMPLEX
|
27
|
+
h[:size] = msg.size
|
28
|
+
end
|
29
|
+
|
30
|
+
msg[:body][:descriptor_count] = 1
|
31
|
+
|
32
|
+
msg[:task_port].tap do |p|
|
33
|
+
p[:name] = port
|
34
|
+
p[:disposition] = MachMsgType[:copy_send]
|
35
|
+
p[:type] = 0 # MACH_MSG_PORT_DESCRIPTOR;
|
36
|
+
end
|
37
|
+
|
38
|
+
mach_msg_send(msg)
|
39
|
+
end
|
40
|
+
|
41
|
+
def recv_port(recv_port)
|
42
|
+
msg = FFI::Struct.new(nil,
|
43
|
+
:header, MsgHeader,
|
44
|
+
:body, MsgBody,
|
45
|
+
:task_port, MsgPortDescriptor,
|
46
|
+
:trailer, MsgTrailer)
|
47
|
+
|
48
|
+
mach_msg(msg, 2, 0, msg.size, recv_port, 0, 0)
|
49
|
+
|
50
|
+
msg.size.times do |i|
|
51
|
+
print "%02x " % msg.to_ptr.get_uint8(i)
|
52
|
+
end
|
53
|
+
puts
|
54
|
+
|
55
|
+
msg[:task_port][:name]
|
56
|
+
end
|
57
|
+
|
58
|
+
def sampling_fork
|
59
|
+
parent_recv_port = setup_recv_port
|
60
|
+
task_set_special_port(mach_task_self, :bootstrap, parent_recv_port)
|
61
|
+
|
62
|
+
fork do
|
63
|
+
parent_recv_port_p = new_memory_pointer :mach_port_t
|
64
|
+
task_get_special_port(mach_task_self, :bootstrap, parent_recv_port_p)
|
65
|
+
parent_recv_port = parent_recv_port_p.get_uint(0)
|
66
|
+
puts "child self:#{mach_task_self} parent_recv:#{parent_recv_port}"
|
67
|
+
|
68
|
+
child_recv_port = setup_recv_port
|
69
|
+
puts "child sending #{mach_task_self}"
|
70
|
+
send_port(parent_recv_port, mach_task_self)
|
71
|
+
end
|
72
|
+
|
73
|
+
task_set_special_port(mach_task_self, :bootstrap, Mach::Functions::bootstrap_port)
|
74
|
+
child_task = recv_port(parent_recv_port)
|
75
|
+
puts "parent received #{child_task}"
|
76
|
+
end
|
77
|
+
|
78
|
+
sampling_fork
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'mach'
|
3
|
+
|
4
|
+
module Mach
|
5
|
+
describe 'low level semaphore functions' do
|
6
|
+
include Functions
|
7
|
+
|
8
|
+
it 'raises exception with invalid args' do
|
9
|
+
p = proc { semaphore_create(mach_task_self, nil, 1234, 1) }
|
10
|
+
p.must_raise Error::INVALID_ARGUMENT
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
describe Semaphore do
|
15
|
+
it 'creates a semaphore' do
|
16
|
+
sem = Semaphore.new
|
17
|
+
sem.destroy
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'raises exception with invalid args' do
|
21
|
+
p = proc { Semaphore.new(:sync_policy => :no_such) }
|
22
|
+
p.must_raise ArgumentError # Error::INVALID_ARGUMENT
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'signals/waits in same task' do
|
26
|
+
sem = Semaphore.new(:value => 0)
|
27
|
+
sem.signal
|
28
|
+
sem.wait
|
29
|
+
sem.destroy
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'coordinates access to shared resource between two tasks' do
|
33
|
+
sem = Semaphore.new(:value => 0)
|
34
|
+
|
35
|
+
port = Port.new
|
36
|
+
port.insert_right(:make_send)
|
37
|
+
Task.self.set_bootstrap_port(port)
|
38
|
+
|
39
|
+
child = fork do
|
40
|
+
parent_port = Task.self.get_bootstrap_port
|
41
|
+
Task.self.copy_send(parent_port)
|
42
|
+
# parent will copy send rights to sem into child task
|
43
|
+
sleep 0.5
|
44
|
+
sem.signal
|
45
|
+
Kernel.exit!
|
46
|
+
end
|
47
|
+
|
48
|
+
child_task_port = port.receive_right
|
49
|
+
|
50
|
+
start = Time.now.to_f
|
51
|
+
sem.insert_right(:copy_send, :ipc_space => child_task_port)
|
52
|
+
sem.wait
|
53
|
+
elapsed = Time.now.to_f - start
|
54
|
+
|
55
|
+
Process.wait child
|
56
|
+
|
57
|
+
elapsed.must be_gt(0.4)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|