ichannel 5.2.0 → 6.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.travis.yml +3 -0
- data/ChangeLog.txt +26 -11
- data/README.md +3 -3
- data/lib/ichannel/unix_socket.rb +174 -0
- data/lib/ichannel/version.rb +1 -1
- data/lib/ichannel.rb +5 -153
- data/test/ichannel_class_test.rb +24 -1
- metadata +5 -4
data/.travis.yml
CHANGED
data/ChangeLog.txt
CHANGED
@@ -1,50 +1,65 @@
|
|
1
|
+
== v0.6.0
|
2
|
+
- IChannel::UNIXSocket can now be serialized by Marshal.
|
3
|
+
IChannel::UNIXSocket can be serialized by Marshal but there's a gotcha: it
|
4
|
+
only really works on the same machine between one or more Ruby processes.
|
5
|
+
The UNIXSocket#marshal_dump method is implemented to dump the FDs in an array,
|
6
|
+
so this feature won't fair well across different machines or time but I've
|
7
|
+
found this feature useful nonetheless.
|
8
|
+
|
9
|
+
- add IChannel.unix(…)
|
10
|
+
Returns an instance of IChannel::UNIXSocket.
|
11
|
+
|
12
|
+
- add IChannel::UNIXSocket
|
13
|
+
rename IChannel as IChannel::UNIXSocket in preparation for multiple
|
14
|
+
backends(UNIXSocket, Redis, …).
|
15
|
+
|
1
16
|
== v5.2.0
|
2
|
-
|
17
|
+
- add IChannel#last_msg.
|
3
18
|
Reads the last message written to the channel by reading until the channel
|
4
19
|
is empty. The last message is cached and reset to nil on call to #close.
|
5
20
|
|
6
21
|
== v5.1.1.1, v5.1.1.2, v5.1.1.3, v5.1.1.4, v5.1.1.5
|
7
|
-
|
22
|
+
- doc improvements
|
8
23
|
a set of releases that improved the README & api docs.
|
9
24
|
|
10
25
|
== v5.1.1
|
11
|
-
|
26
|
+
- Change socket type to use TCP.
|
12
27
|
The use of UDP could result in bugs because of its unordered nature.
|
13
28
|
|
14
29
|
== v5.1.0
|
15
|
-
|
30
|
+
- Remove restriction on size of message.
|
16
31
|
IChannel#get can read a message of any size(before hand it was limited to
|
17
32
|
1MB in size). Thanks to @quezacoatl.
|
18
33
|
|
19
|
-
|
34
|
+
- IChannel#readable? no longer blocks.
|
20
35
|
IChannel#readable? no longer blocks for 0.1 seconds on IO.select call.
|
21
36
|
Thanks to quezacoatl(https://github.com/quezacoatl) for the initial
|
22
37
|
implementation.
|
23
38
|
|
24
39
|
== v5.0.0
|
25
|
-
|
40
|
+
- Remove IChannel#empty?
|
26
41
|
I think the #readable? method is all you need, and is a much more
|
27
42
|
accurate description of what the method is asking. We cannot determine
|
28
43
|
if the channel is really empty, but we can ask if it is readable at the
|
29
44
|
time you ask.
|
30
45
|
|
31
46
|
== v4.1.0
|
32
|
-
|
47
|
+
- Add IChannel#readable?
|
33
48
|
A method that can tell you whether or not a read would block.
|
34
49
|
When it returns true, a read shouldn't block, on the other hand
|
35
50
|
if it were false it'd likely block by the time you call #get.
|
36
51
|
|
37
52
|
== v4.0.0
|
38
|
-
|
53
|
+
- Modify IChannel#empty?
|
39
54
|
It now returns true in case the underlying UNIXSocket being used as a
|
40
55
|
reader is closed.
|
41
56
|
|
42
57
|
== v3.1.0
|
43
|
-
|
58
|
+
- Add IChannel#empty?.
|
44
59
|
IChannel#empty? returns true when the channel is empty(nothing to read).
|
45
60
|
|
46
|
-
|
61
|
+
- Micro speed improvement on #write!, & #recv! operations.
|
47
62
|
By passing nil instead of creating two empty arrays for every read/write
|
48
63
|
operation we should see a very small improvement in their performance.
|
49
64
|
|
50
|
-
|
65
|
+
- Add ChangeLog.txt
|
data/README.md
CHANGED
@@ -32,7 +32,7 @@ A demo of how to pass ruby objects through a channel and also between processes.
|
|
32
32
|
in this example:
|
33
33
|
|
34
34
|
```ruby
|
35
|
-
channel = IChannel.
|
35
|
+
channel = IChannel.unix Marshal
|
36
36
|
pid = fork do
|
37
37
|
channel.put Process.pid
|
38
38
|
channel.put 'Hello!'
|
@@ -48,7 +48,7 @@ Knowing when a channel is readable can be useful so that you can avoid a
|
|
48
48
|
blocking read. This (bad) example demonstrates how to do that:
|
49
49
|
|
50
50
|
```ruby
|
51
|
-
channel = IChannel.
|
51
|
+
channel = IChannel.unix Marshal
|
52
52
|
pid = fork do
|
53
53
|
sleep 3
|
54
54
|
channel.put 42
|
@@ -75,7 +75,7 @@ module MyMessagePack
|
|
75
75
|
MessagePack.unpack(msg)
|
76
76
|
end
|
77
77
|
end
|
78
|
-
channel = IChannel.
|
78
|
+
channel = IChannel.unix MyMessagePack
|
79
79
|
```
|
80
80
|
|
81
81
|
__PLATFORM SUPPORT__
|
@@ -0,0 +1,174 @@
|
|
1
|
+
require 'socket'
|
2
|
+
class IChannel
|
3
|
+
class UNIXSocket
|
4
|
+
SEP = '_$_'
|
5
|
+
if respond_to? :private_constant
|
6
|
+
private_constant :SEP
|
7
|
+
end
|
8
|
+
|
9
|
+
#
|
10
|
+
# @param [#dump,#load] serializer
|
11
|
+
# Any object that implements dump, & load.
|
12
|
+
#
|
13
|
+
def initialize(serializer = Marshal, adapter_options)
|
14
|
+
@serializer = serializer
|
15
|
+
@last_msg = nil
|
16
|
+
@reader, @writer = ::UNIXSocket.pair :STREAM
|
17
|
+
end
|
18
|
+
|
19
|
+
#
|
20
|
+
# @return [Boolean]
|
21
|
+
# Returns true when the channel is closed.
|
22
|
+
#
|
23
|
+
def closed?
|
24
|
+
@reader.closed? && @writer.closed?
|
25
|
+
end
|
26
|
+
|
27
|
+
#
|
28
|
+
# Close the channel.
|
29
|
+
#
|
30
|
+
# @return [Boolean]
|
31
|
+
# Returns true when the channel has been closed.
|
32
|
+
#
|
33
|
+
def close
|
34
|
+
unless closed?
|
35
|
+
@reader.close
|
36
|
+
@writer.close
|
37
|
+
@last_msg = nil
|
38
|
+
true
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
#
|
43
|
+
# Add an object to the channel.
|
44
|
+
#
|
45
|
+
# @raise [IOError]
|
46
|
+
# When the channel is closed.
|
47
|
+
#
|
48
|
+
# @param [Object] object
|
49
|
+
# An object to add to the channel.
|
50
|
+
#
|
51
|
+
def write(object)
|
52
|
+
write!(object, nil)
|
53
|
+
end
|
54
|
+
alias_method :put, :write
|
55
|
+
|
56
|
+
#
|
57
|
+
# Add an object to the channel.
|
58
|
+
#
|
59
|
+
# Unlike {#write}, which waits indefinitely until the channel becomes writable,
|
60
|
+
# this method will raise an IOError when _timeout_ seconds elapse and
|
61
|
+
# the channel remains unwritable.
|
62
|
+
#
|
63
|
+
# @param
|
64
|
+
# (see IChannel#write)
|
65
|
+
#
|
66
|
+
# @param [Numeric] timeout
|
67
|
+
# The number of seconds to wait for the channel to become writable.
|
68
|
+
#
|
69
|
+
# @raise (see IChannel#write)
|
70
|
+
#
|
71
|
+
# @raise [IOError]
|
72
|
+
# When _timeout_ seconds elapse & the channel remains unwritable.
|
73
|
+
#
|
74
|
+
def write!(object, timeout = 0.1)
|
75
|
+
if @writer.closed?
|
76
|
+
raise IOError, 'The channel cannot be written to (closed).'
|
77
|
+
end
|
78
|
+
_, writable, _ = IO.select nil, [@writer], nil, timeout
|
79
|
+
if writable
|
80
|
+
msg = @serializer.dump(object)
|
81
|
+
writable[0].syswrite "#{msg}#{SEP}"
|
82
|
+
else
|
83
|
+
raise IOError, 'The channel cannot be written to.'
|
84
|
+
end
|
85
|
+
end
|
86
|
+
alias_method :put!, :write!
|
87
|
+
|
88
|
+
#
|
89
|
+
# Reads the last message written to the channel by reading until the channel
|
90
|
+
# is empty. The last message is cached and reset to nil on call to {#close}.
|
91
|
+
#
|
92
|
+
# @return [Object]
|
93
|
+
# Returns the last message to be written to the channel.
|
94
|
+
#
|
95
|
+
def last_msg
|
96
|
+
while readable?
|
97
|
+
@last_msg = get
|
98
|
+
end
|
99
|
+
@last_msg
|
100
|
+
end
|
101
|
+
|
102
|
+
#
|
103
|
+
# Receive an object from the channel.
|
104
|
+
#
|
105
|
+
# @raise [IOError]
|
106
|
+
# When the channel is closed.
|
107
|
+
#
|
108
|
+
# @return [Object]
|
109
|
+
# The object read from the channel.
|
110
|
+
#
|
111
|
+
def recv
|
112
|
+
recv!(nil)
|
113
|
+
end
|
114
|
+
alias_method :get, :recv
|
115
|
+
|
116
|
+
#
|
117
|
+
# Receive an object from the channel.
|
118
|
+
#
|
119
|
+
# Unlike {#recv}, which waits indefinitely until the channel becomes readable,
|
120
|
+
# this method will raise an IOError when _timeout_ seconds elapse and the
|
121
|
+
# channel remains unreadable.
|
122
|
+
#
|
123
|
+
# @param [Numeric] timeout
|
124
|
+
# The number of seconds to wait for the channel to become readable.
|
125
|
+
#
|
126
|
+
# @raise [IOError]
|
127
|
+
# (see IChannel#recv)
|
128
|
+
#
|
129
|
+
# @raise [IOError]
|
130
|
+
# When _timeout_ seconds elapse & the channel remains unreadable.
|
131
|
+
#
|
132
|
+
# @return [Object]
|
133
|
+
# The object read from the channel.
|
134
|
+
#
|
135
|
+
def recv!(timeout = 0.1)
|
136
|
+
if @reader.closed?
|
137
|
+
raise IOError, 'The channel cannot be read from (closed).'
|
138
|
+
end
|
139
|
+
readable, _ = IO.select [@reader], nil, nil, timeout
|
140
|
+
if readable
|
141
|
+
msg = readable[0].readline(SEP).chomp SEP
|
142
|
+
@serializer.load msg
|
143
|
+
else
|
144
|
+
raise IOError, 'The channel cannot be read from.'
|
145
|
+
end
|
146
|
+
end
|
147
|
+
alias_method :get!, :recv!
|
148
|
+
|
149
|
+
#
|
150
|
+
# @return [Boolean]
|
151
|
+
# Returns true when the channel is readable.
|
152
|
+
#
|
153
|
+
def readable?
|
154
|
+
if closed?
|
155
|
+
false
|
156
|
+
else
|
157
|
+
readable, _ = IO.select [@reader], nil, nil, 0
|
158
|
+
!! readable
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
# @api private
|
163
|
+
def marshal_load(array)
|
164
|
+
@serializer, reader, writer, @last_msg = array
|
165
|
+
@reader = ::UNIXSocket.for_fd(reader)
|
166
|
+
@writer = ::UNIXSocket.for_fd(writer)
|
167
|
+
end
|
168
|
+
|
169
|
+
# @api private
|
170
|
+
def marshal_dump
|
171
|
+
[@serializer, @reader.to_i, @writer.to_i, @last_msg]
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
data/lib/ichannel/version.rb
CHANGED
data/lib/ichannel.rb
CHANGED
@@ -1,159 +1,11 @@
|
|
1
|
-
require 'socket'
|
2
1
|
class IChannel
|
3
|
-
|
4
|
-
if respond_to? :private_constant
|
5
|
-
private_constant :SEP
|
6
|
-
end
|
7
|
-
#
|
8
|
-
# @param [#dump,#load] serializer
|
9
|
-
# Any object that implements dump, & load.
|
10
|
-
#
|
11
|
-
def initialize(serializer)
|
12
|
-
@reader, @writer = UNIXSocket.pair :STREAM
|
13
|
-
@serializer = serializer
|
14
|
-
@last_msg = nil
|
15
|
-
end
|
16
|
-
|
17
|
-
#
|
18
|
-
# @return [Boolean]
|
19
|
-
# Returns true when the channel is closed.
|
20
|
-
#
|
21
|
-
def closed?
|
22
|
-
@reader.closed? && @writer.closed?
|
23
|
-
end
|
24
|
-
|
25
|
-
#
|
26
|
-
# Close the channel.
|
27
|
-
#
|
28
|
-
# @return [Boolean]
|
29
|
-
# Returns true when the channel has been closed.
|
30
|
-
#
|
31
|
-
def close
|
32
|
-
unless closed?
|
33
|
-
@reader.close
|
34
|
-
@writer.close
|
35
|
-
@last_msg = nil
|
36
|
-
true
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
#
|
41
|
-
# Add an object to the channel.
|
42
|
-
#
|
43
|
-
# @raise [IOError]
|
44
|
-
# When the channel is closed.
|
45
|
-
#
|
46
|
-
# @param [Object] object
|
47
|
-
# An object to add to the channel.
|
48
|
-
#
|
49
|
-
def write(object)
|
50
|
-
write!(object, nil)
|
51
|
-
end
|
52
|
-
alias_method :put, :write
|
53
|
-
|
54
|
-
#
|
55
|
-
# Add an object to the channel.
|
56
|
-
#
|
57
|
-
# Unlike {#write}, which waits indefinitely until the channel becomes writable,
|
58
|
-
# this method will raise an IOError when _timeout_ seconds elapse and
|
59
|
-
# the channel remains unwritable.
|
60
|
-
#
|
61
|
-
# @param
|
62
|
-
# (see IChannel#write)
|
63
|
-
#
|
64
|
-
# @param [Numeric] timeout
|
65
|
-
# The number of seconds to wait for the channel to become writable.
|
66
|
-
#
|
67
|
-
# @raise (see IChannel#write)
|
68
|
-
#
|
69
|
-
# @raise [IOError]
|
70
|
-
# When _timeout_ seconds elapse & the channel remains unwritable.
|
71
|
-
#
|
72
|
-
def write!(object, timeout = 0.1)
|
73
|
-
if @writer.closed?
|
74
|
-
raise IOError, 'The channel cannot be written to (closed).'
|
75
|
-
end
|
76
|
-
_, writable, _ = IO.select nil, [@writer], nil, timeout
|
77
|
-
if writable
|
78
|
-
msg = @serializer.dump(object)
|
79
|
-
writable[0].syswrite "#{msg}#{SEP}"
|
80
|
-
else
|
81
|
-
raise IOError, 'The channel cannot be written to.'
|
82
|
-
end
|
83
|
-
end
|
84
|
-
alias_method :put!, :write!
|
85
|
-
|
86
|
-
#
|
87
|
-
# Reads the last message written to the channel by reading until the channel
|
88
|
-
# is empty. The last message is cached and reset to nil on call to {#close}.
|
89
|
-
#
|
90
|
-
# @return [Object]
|
91
|
-
# Returns the last message to be written to the channel.
|
92
|
-
#
|
93
|
-
def last_msg
|
94
|
-
while readable?
|
95
|
-
@last_msg = get
|
96
|
-
end
|
97
|
-
@last_msg
|
98
|
-
end
|
99
|
-
|
100
|
-
#
|
101
|
-
# Receive an object from the channel.
|
102
|
-
#
|
103
|
-
# @raise [IOError]
|
104
|
-
# When the channel is closed.
|
105
|
-
#
|
106
|
-
# @return [Object]
|
107
|
-
# The object read from the channel.
|
108
|
-
#
|
109
|
-
def recv
|
110
|
-
recv!(nil)
|
111
|
-
end
|
112
|
-
alias_method :get, :recv
|
2
|
+
require_relative "ichannel/unix_socket"
|
113
3
|
|
114
|
-
|
115
|
-
|
116
|
-
#
|
117
|
-
# Unlike {#recv}, which waits indefinitely until the channel becomes readable,
|
118
|
-
# this method will raise an IOError when _timeout_ seconds elapse and the
|
119
|
-
# channel remains unreadable.
|
120
|
-
#
|
121
|
-
# @param [Numeric] timeout
|
122
|
-
# The number of seconds to wait for the channel to become readable.
|
123
|
-
#
|
124
|
-
# @raise [IOError]
|
125
|
-
# (see IChannel#recv)
|
126
|
-
#
|
127
|
-
# @raise [IOError]
|
128
|
-
# When _timeout_ seconds elapse & the channel remains unreadable.
|
129
|
-
#
|
130
|
-
# @return [Object]
|
131
|
-
# The object read from the channel.
|
132
|
-
#
|
133
|
-
def recv!(timeout = 0.1)
|
134
|
-
if @reader.closed?
|
135
|
-
raise IOError, 'The channel cannot be read from (closed).'
|
136
|
-
end
|
137
|
-
readable, _ = IO.select [@reader], nil, nil, timeout
|
138
|
-
if readable
|
139
|
-
msg = readable[0].readline(SEP).chomp SEP
|
140
|
-
@serializer.load msg
|
141
|
-
else
|
142
|
-
raise IOError, 'The channel cannot be read from.'
|
143
|
-
end
|
4
|
+
def self.unix(options)
|
5
|
+
UNIXSocket.new(options)
|
144
6
|
end
|
145
|
-
alias_method :get!, :recv!
|
146
7
|
|
147
|
-
|
148
|
-
|
149
|
-
# Returns true when the channel is readable.
|
150
|
-
#
|
151
|
-
def readable?
|
152
|
-
if closed?
|
153
|
-
false
|
154
|
-
else
|
155
|
-
readable, _ = IO.select [@reader], nil, nil, 0
|
156
|
-
!! readable
|
157
|
-
end
|
8
|
+
def self.redis(options)
|
9
|
+
raise NotImplementedError
|
158
10
|
end
|
159
11
|
end
|
data/test/ichannel_class_test.rb
CHANGED
@@ -2,13 +2,36 @@ require_relative 'setup'
|
|
2
2
|
class IChannelTest < Test::Unit::TestCase
|
3
3
|
def setup
|
4
4
|
serializer = Object.const_get ENV["SERIALIZER"] || "Marshal"
|
5
|
-
@channel = IChannel.
|
5
|
+
@channel = IChannel.unix serializer
|
6
6
|
end
|
7
7
|
|
8
8
|
def teardown
|
9
9
|
@channel.close
|
10
10
|
end
|
11
11
|
|
12
|
+
def test_interface
|
13
|
+
%w(write
|
14
|
+
write!
|
15
|
+
get
|
16
|
+
get!
|
17
|
+
close
|
18
|
+
closed?
|
19
|
+
readable?
|
20
|
+
last_msg
|
21
|
+
put
|
22
|
+
put!
|
23
|
+
recv
|
24
|
+
recv!).each do |method|
|
25
|
+
assert @channel.respond_to? method
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_serialization
|
30
|
+
@channel.put 42
|
31
|
+
dump = Marshal.dump @channel
|
32
|
+
assert_equal 42, Marshal.load(dump).get
|
33
|
+
end
|
34
|
+
|
12
35
|
def test_last_msg
|
13
36
|
@channel.put %w(a)
|
14
37
|
@channel.put %w(b)
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ichannel
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 6.0.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-04-
|
12
|
+
date: 2013-04-20 00:00:00.000000000 Z
|
13
13
|
dependencies: []
|
14
14
|
description: ! "A modern and easy-to-use interprocess communication \n primitive."
|
15
15
|
email:
|
@@ -29,6 +29,7 @@ files:
|
|
29
29
|
- Rakefile
|
30
30
|
- ichannel.gemspec
|
31
31
|
- lib/ichannel.rb
|
32
|
+
- lib/ichannel/unix_socket.rb
|
32
33
|
- lib/ichannel/version.rb
|
33
34
|
- test/ichannel_class_test.rb
|
34
35
|
- test/setup.rb
|
@@ -46,7 +47,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
46
47
|
version: '0'
|
47
48
|
segments:
|
48
49
|
- 0
|
49
|
-
hash:
|
50
|
+
hash: -4148613930352415051
|
50
51
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
51
52
|
none: false
|
52
53
|
requirements:
|
@@ -55,7 +56,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
55
56
|
version: '0'
|
56
57
|
segments:
|
57
58
|
- 0
|
58
|
-
hash:
|
59
|
+
hash: -4148613930352415051
|
59
60
|
requirements: []
|
60
61
|
rubyforge_project:
|
61
62
|
rubygems_version: 1.8.23
|