ffi-rxs 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +7 -0
- data/AUTHORS.txt +21 -0
- data/Gemfile +3 -0
- data/README.rdoc +86 -0
- data/Rakefile +6 -0
- data/ext/README +5 -0
- data/ffi-rxs.gemspec +25 -0
- data/lib/ffi-rxs/constants.rb +104 -0
- data/lib/ffi-rxs/constants.rb~ +100 -0
- data/lib/ffi-rxs/context.rb +153 -0
- data/lib/ffi-rxs/context.rb~ +155 -0
- data/lib/ffi-rxs/device.rb~ +28 -0
- data/lib/ffi-rxs/exceptions.rb +47 -0
- data/lib/ffi-rxs/exceptions.rb~ +47 -0
- data/lib/ffi-rxs/libc.rb +19 -0
- data/lib/ffi-rxs/libc.rb~ +19 -0
- data/lib/ffi-rxs/libxs.rb +156 -0
- data/lib/ffi-rxs/libxs.rb~ +156 -0
- data/lib/ffi-rxs/message.rb +282 -0
- data/lib/ffi-rxs/message.rb~ +282 -0
- data/lib/ffi-rxs/poll.rb +212 -0
- data/lib/ffi-rxs/poll.rb~ +212 -0
- data/lib/ffi-rxs/poll_items.rb +120 -0
- data/lib/ffi-rxs/poll_items.rb~ +120 -0
- data/lib/ffi-rxs/socket.rb +659 -0
- data/lib/ffi-rxs/socket.rb~ +659 -0
- data/lib/ffi-rxs/util.rb +105 -0
- data/lib/ffi-rxs/util.rb~ +105 -0
- data/lib/ffi-rxs/version.rb +3 -0
- data/lib/ffi-rxs/version.rb~ +3 -0
- data/lib/ffi-rxs.rb +74 -0
- data/spec/context_spec.rb +138 -0
- data/spec/message_spec.rb +128 -0
- data/spec/multipart_spec.rb +108 -0
- data/spec/nonblocking_recv_spec.rb +309 -0
- data/spec/poll_spec.rb +168 -0
- data/spec/pushpull_spec.rb +113 -0
- data/spec/reqrep_spec.rb +66 -0
- data/spec/socket_spec.rb +496 -0
- data/spec/spec_helper.rb +57 -0
- metadata +126 -0
data/lib/ffi-rxs/poll.rb
ADDED
@@ -0,0 +1,212 @@
|
|
1
|
+
|
2
|
+
module XS
|
3
|
+
|
4
|
+
class Poller
|
5
|
+
include XS::Util
|
6
|
+
|
7
|
+
attr_reader :readables, :writables
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@items = XS::PollItems.new
|
11
|
+
@raw_to_socket = {}
|
12
|
+
@sockets = []
|
13
|
+
@readables = []
|
14
|
+
@writables = []
|
15
|
+
end
|
16
|
+
|
17
|
+
# Checks each registered socket for selectability based on the poll items'
|
18
|
+
# registered +events+. Will block for up to +timeout+ milliseconds
|
19
|
+
# A millisecond is 1/1000 of a second, so to block for 1 second
|
20
|
+
# pass the value "1000" to #poll.
|
21
|
+
#
|
22
|
+
# Pass "-1" or +:blocking+ for +timeout+ for this call to block
|
23
|
+
# indefinitely.
|
24
|
+
#
|
25
|
+
# This method will return *immediately* when there are no registered
|
26
|
+
# sockets. In that case, the +timeout+ parameter is not honored. To
|
27
|
+
# prevent a CPU busy-loop, the caller of this method should detect
|
28
|
+
# this possible condition (via #size) and throttle the call
|
29
|
+
# frequency.
|
30
|
+
#
|
31
|
+
# Returns 0 when there are no registered sockets that are readable
|
32
|
+
# or writable.
|
33
|
+
#
|
34
|
+
# Return 1 (or greater) to indicate the number of readable or writable
|
35
|
+
# sockets. These sockets should be processed using the #readables and
|
36
|
+
# #writables accessors.
|
37
|
+
#
|
38
|
+
# Returns -1 when there is an error. Use XS::Util.errno to get the related
|
39
|
+
# error number.
|
40
|
+
#
|
41
|
+
def poll timeout = :blocking
|
42
|
+
unless @items.empty?
|
43
|
+
timeout = adjust timeout
|
44
|
+
items_triggered = LibXS.xs_poll @items.address, @items.size, timeout
|
45
|
+
|
46
|
+
if Util.resultcode_ok?(items_triggered)
|
47
|
+
update_selectables
|
48
|
+
end
|
49
|
+
|
50
|
+
items_triggered
|
51
|
+
else
|
52
|
+
0
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# The non-blocking version of #poll. See the #poll description for
|
57
|
+
# potential exceptions.
|
58
|
+
#
|
59
|
+
# May return -1 when an error is encounted. Check XS::Util.errno
|
60
|
+
# to determine the underlying cause.
|
61
|
+
#
|
62
|
+
def poll_nonblock
|
63
|
+
poll 0
|
64
|
+
end
|
65
|
+
|
66
|
+
# Register the +sock+ for +events+. This method is idempotent meaning
|
67
|
+
# it can be called multiple times with the same data and the socket
|
68
|
+
# will only get registered at most once. Calling multiple times with
|
69
|
+
# different values for +events+ will OR the event information together.
|
70
|
+
#
|
71
|
+
def register sock, events = XS::POLLIN | XS::POLLOUT, fd = 0
|
72
|
+
return false if (sock.nil? && fd.zero?) || events.zero?
|
73
|
+
|
74
|
+
item = @items.get(@sockets.index(sock))
|
75
|
+
|
76
|
+
unless item
|
77
|
+
@sockets << sock
|
78
|
+
item = LibXS::PollItem.new
|
79
|
+
|
80
|
+
if sock.kind_of?(XS::Socket) || sock.kind_of?(Socket)
|
81
|
+
item[:socket] = sock.socket
|
82
|
+
item[:fd] = 0
|
83
|
+
else
|
84
|
+
item[:socket] = FFI::MemoryPointer.new(0)
|
85
|
+
item[:fd] = fd
|
86
|
+
end
|
87
|
+
|
88
|
+
@raw_to_socket[item.socket.address] = sock
|
89
|
+
@items << item
|
90
|
+
end
|
91
|
+
|
92
|
+
item[:events] |= events
|
93
|
+
end
|
94
|
+
|
95
|
+
# Deregister the +sock+ for +events+. When there are no events left,
|
96
|
+
# this also deletes the socket from the poll items.
|
97
|
+
#
|
98
|
+
def deregister sock, events, fd = 0
|
99
|
+
return unless sock || !fd.zero?
|
100
|
+
|
101
|
+
item = @items.get(@sockets.index(sock))
|
102
|
+
|
103
|
+
if item && (item[:events] & events) > 0
|
104
|
+
# change the value in place
|
105
|
+
item[:events] ^= events
|
106
|
+
|
107
|
+
delete sock if item[:events].zero?
|
108
|
+
true
|
109
|
+
else
|
110
|
+
false
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
# A helper method to register a +sock+ as readable events only.
|
115
|
+
#
|
116
|
+
def register_readable sock
|
117
|
+
register sock, XS::POLLIN, 0
|
118
|
+
end
|
119
|
+
|
120
|
+
# A helper method to register a +sock+ for writable events only.
|
121
|
+
#
|
122
|
+
def register_writable sock
|
123
|
+
register sock, XS::POLLOUT, 0
|
124
|
+
end
|
125
|
+
|
126
|
+
# A helper method to deregister a +sock+ for readable events.
|
127
|
+
#
|
128
|
+
def deregister_readable sock
|
129
|
+
deregister sock, XS::POLLIN, 0
|
130
|
+
end
|
131
|
+
|
132
|
+
# A helper method to deregister a +sock+ for writable events.
|
133
|
+
#
|
134
|
+
def deregister_writable sock
|
135
|
+
deregister sock, XS::POLLOUT, 0
|
136
|
+
end
|
137
|
+
|
138
|
+
# Deletes the +sock+ for all subscribed events. Called internally
|
139
|
+
# when a socket has been deregistered and has no more events
|
140
|
+
# registered anywhere.
|
141
|
+
#
|
142
|
+
# Can also be called directly to remove the socket from the polling
|
143
|
+
# array.
|
144
|
+
#
|
145
|
+
def delete sock
|
146
|
+
unless (size = @sockets.size).zero?
|
147
|
+
@sockets.delete_if { |socket| socket.socket.address == sock.socket.address }
|
148
|
+
socket_deleted = size != @sockets.size
|
149
|
+
|
150
|
+
item_deleted = @items.delete sock
|
151
|
+
|
152
|
+
raw_deleted = @raw_to_socket.delete(sock.socket.address)
|
153
|
+
|
154
|
+
socket_deleted && item_deleted && raw_deleted
|
155
|
+
|
156
|
+
else
|
157
|
+
false
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
def size(); @items.size; end
|
162
|
+
|
163
|
+
def inspect
|
164
|
+
@items.inspect
|
165
|
+
end
|
166
|
+
|
167
|
+
def to_s(); inspect; end
|
168
|
+
|
169
|
+
|
170
|
+
private
|
171
|
+
|
172
|
+
def items_hash hash
|
173
|
+
@items.each do |poll_item|
|
174
|
+
hash[@raw_to_socket[poll_item.socket.address]] = poll_item
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
def update_selectables
|
179
|
+
@readables.clear
|
180
|
+
@writables.clear
|
181
|
+
|
182
|
+
@items.each do |poll_item|
|
183
|
+
#FIXME: spec for sockets *and* file descriptors
|
184
|
+
if poll_item.readable?
|
185
|
+
@readables << (poll_item.socket.address.zero? ? poll_item.fd : @raw_to_socket[poll_item.socket.address])
|
186
|
+
end
|
187
|
+
|
188
|
+
if poll_item.writable?
|
189
|
+
@writables << (poll_item.socket.address.zero? ? poll_item.fd : @raw_to_socket[poll_item.socket.address])
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
# Convert the timeout value to something usable by
|
195
|
+
# the library.
|
196
|
+
#
|
197
|
+
# -1 or :blocking should be converted to -1.
|
198
|
+
#
|
199
|
+
# Users will pass in values measured as
|
200
|
+
# milliseconds, so we need to convert that value to
|
201
|
+
# microseconds for the library.
|
202
|
+
|
203
|
+
def adjust timeout
|
204
|
+
if :blocking == timeout || -1 == timeout
|
205
|
+
-1
|
206
|
+
else
|
207
|
+
timeout.to_i
|
208
|
+
end
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
end # module XS
|
@@ -0,0 +1,212 @@
|
|
1
|
+
|
2
|
+
module XS
|
3
|
+
|
4
|
+
class Poller
|
5
|
+
include XS::Util
|
6
|
+
|
7
|
+
attr_reader :readables, :writables
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@items = XS::PollItems.new
|
11
|
+
@raw_to_socket = {}
|
12
|
+
@sockets = []
|
13
|
+
@readables = []
|
14
|
+
@writables = []
|
15
|
+
end
|
16
|
+
|
17
|
+
# Checks each registered socket for selectability based on the poll items'
|
18
|
+
# registered +events+. Will block for up to +timeout+ milliseconds
|
19
|
+
# A millisecond is 1/1000 of a second, so to block for 1 second
|
20
|
+
# pass the value "1000" to #poll.
|
21
|
+
#
|
22
|
+
# Pass "-1" or +:blocking+ for +timeout+ for this call to block
|
23
|
+
# indefinitely.
|
24
|
+
#
|
25
|
+
# This method will return *immediately* when there are no registered
|
26
|
+
# sockets. In that case, the +timeout+ parameter is not honored. To
|
27
|
+
# prevent a CPU busy-loop, the caller of this method should detect
|
28
|
+
# this possible condition (via #size) and throttle the call
|
29
|
+
# frequency.
|
30
|
+
#
|
31
|
+
# Returns 0 when there are no registered sockets that are readable
|
32
|
+
# or writable.
|
33
|
+
#
|
34
|
+
# Return 1 (or greater) to indicate the number of readable or writable
|
35
|
+
# sockets. These sockets should be processed using the #readables and
|
36
|
+
# #writables accessors.
|
37
|
+
#
|
38
|
+
# Returns -1 when there is an error. Use ZMQ::Util.errno to get the related
|
39
|
+
# error number.
|
40
|
+
#
|
41
|
+
def poll timeout = :blocking
|
42
|
+
unless @items.empty?
|
43
|
+
timeout = adjust timeout
|
44
|
+
items_triggered = LibXS.xs_poll @items.address, @items.size, timeout
|
45
|
+
|
46
|
+
if Util.resultcode_ok?(items_triggered)
|
47
|
+
update_selectables
|
48
|
+
end
|
49
|
+
|
50
|
+
items_triggered
|
51
|
+
else
|
52
|
+
0
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# The non-blocking version of #poll. See the #poll description for
|
57
|
+
# potential exceptions.
|
58
|
+
#
|
59
|
+
# May return -1 when an error is encounted. Check ZMQ::Util.errno
|
60
|
+
# to determine the underlying cause.
|
61
|
+
#
|
62
|
+
def poll_nonblock
|
63
|
+
poll 0
|
64
|
+
end
|
65
|
+
|
66
|
+
# Register the +sock+ for +events+. This method is idempotent meaning
|
67
|
+
# it can be called multiple times with the same data and the socket
|
68
|
+
# will only get registered at most once. Calling multiple times with
|
69
|
+
# different values for +events+ will OR the event information together.
|
70
|
+
#
|
71
|
+
def register sock, events = XS::POLLIN | XS::POLLOUT, fd = 0
|
72
|
+
return false if (sock.nil? && fd.zero?) || events.zero?
|
73
|
+
|
74
|
+
item = @items.get(@sockets.index(sock))
|
75
|
+
|
76
|
+
unless item
|
77
|
+
@sockets << sock
|
78
|
+
item = LibXS::PollItem.new
|
79
|
+
|
80
|
+
if sock.kind_of?(XS::Socket) || sock.kind_of?(Socket)
|
81
|
+
item[:socket] = sock.socket
|
82
|
+
item[:fd] = 0
|
83
|
+
else
|
84
|
+
item[:socket] = FFI::MemoryPointer.new(0)
|
85
|
+
item[:fd] = fd
|
86
|
+
end
|
87
|
+
|
88
|
+
@raw_to_socket[item.socket.address] = sock
|
89
|
+
@items << item
|
90
|
+
end
|
91
|
+
|
92
|
+
item[:events] |= events
|
93
|
+
end
|
94
|
+
|
95
|
+
# Deregister the +sock+ for +events+. When there are no events left,
|
96
|
+
# this also deletes the socket from the poll items.
|
97
|
+
#
|
98
|
+
def deregister sock, events, fd = 0
|
99
|
+
return unless sock || !fd.zero?
|
100
|
+
|
101
|
+
item = @items.get(@sockets.index(sock))
|
102
|
+
|
103
|
+
if item && (item[:events] & events) > 0
|
104
|
+
# change the value in place
|
105
|
+
item[:events] ^= events
|
106
|
+
|
107
|
+
delete sock if item[:events].zero?
|
108
|
+
true
|
109
|
+
else
|
110
|
+
false
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
# A helper method to register a +sock+ as readable events only.
|
115
|
+
#
|
116
|
+
def register_readable sock
|
117
|
+
register sock, XS::POLLIN, 0
|
118
|
+
end
|
119
|
+
|
120
|
+
# A helper method to register a +sock+ for writable events only.
|
121
|
+
#
|
122
|
+
def register_writable sock
|
123
|
+
register sock, XS::POLLOUT, 0
|
124
|
+
end
|
125
|
+
|
126
|
+
# A helper method to deregister a +sock+ for readable events.
|
127
|
+
#
|
128
|
+
def deregister_readable sock
|
129
|
+
deregister sock, XS::POLLIN, 0
|
130
|
+
end
|
131
|
+
|
132
|
+
# A helper method to deregister a +sock+ for writable events.
|
133
|
+
#
|
134
|
+
def deregister_writable sock
|
135
|
+
deregister sock, XS::POLLOUT, 0
|
136
|
+
end
|
137
|
+
|
138
|
+
# Deletes the +sock+ for all subscribed events. Called internally
|
139
|
+
# when a socket has been deregistered and has no more events
|
140
|
+
# registered anywhere.
|
141
|
+
#
|
142
|
+
# Can also be called directly to remove the socket from the polling
|
143
|
+
# array.
|
144
|
+
#
|
145
|
+
def delete sock
|
146
|
+
unless (size = @sockets.size).zero?
|
147
|
+
@sockets.delete_if { |socket| socket.socket.address == sock.socket.address }
|
148
|
+
socket_deleted = size != @sockets.size
|
149
|
+
|
150
|
+
item_deleted = @items.delete sock
|
151
|
+
|
152
|
+
raw_deleted = @raw_to_socket.delete(sock.socket.address)
|
153
|
+
|
154
|
+
socket_deleted && item_deleted && raw_deleted
|
155
|
+
|
156
|
+
else
|
157
|
+
false
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
def size(); @items.size; end
|
162
|
+
|
163
|
+
def inspect
|
164
|
+
@items.inspect
|
165
|
+
end
|
166
|
+
|
167
|
+
def to_s(); inspect; end
|
168
|
+
|
169
|
+
|
170
|
+
private
|
171
|
+
|
172
|
+
def items_hash hash
|
173
|
+
@items.each do |poll_item|
|
174
|
+
hash[@raw_to_socket[poll_item.socket.address]] = poll_item
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
def update_selectables
|
179
|
+
@readables.clear
|
180
|
+
@writables.clear
|
181
|
+
|
182
|
+
@items.each do |poll_item|
|
183
|
+
#FIXME: spec for sockets *and* file descriptors
|
184
|
+
if poll_item.readable?
|
185
|
+
@readables << (poll_item.socket.address.zero? ? poll_item.fd : @raw_to_socket[poll_item.socket.address])
|
186
|
+
end
|
187
|
+
|
188
|
+
if poll_item.writable?
|
189
|
+
@writables << (poll_item.socket.address.zero? ? poll_item.fd : @raw_to_socket[poll_item.socket.address])
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
# Convert the timeout value to something usable by
|
195
|
+
# the library.
|
196
|
+
#
|
197
|
+
# -1 or :blocking should be converted to -1.
|
198
|
+
#
|
199
|
+
# Users will pass in values measured as
|
200
|
+
# milliseconds, so we need to convert that value to
|
201
|
+
# microseconds for the library.
|
202
|
+
|
203
|
+
def adjust timeout
|
204
|
+
if :blocking == timeout || -1 == timeout
|
205
|
+
-1
|
206
|
+
else
|
207
|
+
timeout.to_i
|
208
|
+
end
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
end # module XS
|
@@ -0,0 +1,120 @@
|
|
1
|
+
|
2
|
+
module XS
|
3
|
+
class PollItems
|
4
|
+
include Enumerable
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
@element_size = LibXS::PollItem.size
|
8
|
+
@store = nil
|
9
|
+
@items = []
|
10
|
+
end
|
11
|
+
|
12
|
+
def size; @items.size; end
|
13
|
+
|
14
|
+
def empty?; @items.empty?; end
|
15
|
+
|
16
|
+
def address
|
17
|
+
clean
|
18
|
+
@store
|
19
|
+
end
|
20
|
+
|
21
|
+
def get index
|
22
|
+
unless @items.empty? || index.nil?
|
23
|
+
clean
|
24
|
+
|
25
|
+
# pointer arithmetic in ruby! whee!
|
26
|
+
pointer = @store + (@element_size * index)
|
27
|
+
|
28
|
+
# cast the memory to a PollItem
|
29
|
+
LibXS::PollItem.new pointer
|
30
|
+
end
|
31
|
+
end
|
32
|
+
alias :[] :get
|
33
|
+
|
34
|
+
def <<(obj)
|
35
|
+
@dirty = true
|
36
|
+
@items << obj
|
37
|
+
end
|
38
|
+
alias :push :<<
|
39
|
+
|
40
|
+
def delete sock
|
41
|
+
address = sock.socket.address
|
42
|
+
found = false
|
43
|
+
|
44
|
+
each_with_index do |item, index|
|
45
|
+
if address == item[:socket].address
|
46
|
+
@items.delete_at index
|
47
|
+
found = true
|
48
|
+
@dirty = true
|
49
|
+
clean
|
50
|
+
break
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# these semantics are different from the usual Array#delete; returns a
|
55
|
+
# boolean instead of the actual item or nil
|
56
|
+
found
|
57
|
+
end
|
58
|
+
|
59
|
+
def delete_at index
|
60
|
+
value = nil
|
61
|
+
unless @items.empty?
|
62
|
+
value = @items.delete_at index
|
63
|
+
@dirty = true
|
64
|
+
clean
|
65
|
+
end
|
66
|
+
|
67
|
+
value
|
68
|
+
end
|
69
|
+
|
70
|
+
def each &blk
|
71
|
+
clean
|
72
|
+
index = 0
|
73
|
+
until index >= @items.size do
|
74
|
+
struct = get index
|
75
|
+
yield struct
|
76
|
+
index += 1
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def each_with_index &blk
|
81
|
+
clean
|
82
|
+
index = 0
|
83
|
+
until index >= @items.size do
|
84
|
+
struct = get index
|
85
|
+
yield struct, index
|
86
|
+
index += 1
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def inspect
|
91
|
+
clean
|
92
|
+
str = ""
|
93
|
+
each { |item| str << "ptr [#{item[:socket]}], events [#{item[:events]}], revents [#{item[:revents]}], " }
|
94
|
+
str.chop.chop
|
95
|
+
end
|
96
|
+
|
97
|
+
def to_s(); inspect; end
|
98
|
+
|
99
|
+
private
|
100
|
+
|
101
|
+
# Allocate a contiguous chunk of memory and copy over the PollItem structs
|
102
|
+
# to this block. Note that the old +@store+ value goes out of scope so when
|
103
|
+
# it is garbage collected that native memory should be automatically freed.
|
104
|
+
def clean
|
105
|
+
if @dirty
|
106
|
+
@store = FFI::MemoryPointer.new @element_size, @items.size, true
|
107
|
+
|
108
|
+
# copy over
|
109
|
+
offset = 0
|
110
|
+
@items.each do |item|
|
111
|
+
LibC.memcpy(@store + offset, item.pointer, @element_size)
|
112
|
+
offset += @element_size
|
113
|
+
end
|
114
|
+
|
115
|
+
@dirty = false
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
end # class PollItems
|
120
|
+
end # module XS
|
@@ -0,0 +1,120 @@
|
|
1
|
+
|
2
|
+
module ZMQ
|
3
|
+
class PollItems
|
4
|
+
include Enumerable
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
@element_size = LibZMQ::PollItem.size
|
8
|
+
@store = nil
|
9
|
+
@items = []
|
10
|
+
end
|
11
|
+
|
12
|
+
def size; @items.size; end
|
13
|
+
|
14
|
+
def empty?; @items.empty?; end
|
15
|
+
|
16
|
+
def address
|
17
|
+
clean
|
18
|
+
@store
|
19
|
+
end
|
20
|
+
|
21
|
+
def get index
|
22
|
+
unless @items.empty? || index.nil?
|
23
|
+
clean
|
24
|
+
|
25
|
+
# pointer arithmetic in ruby! whee!
|
26
|
+
pointer = @store + (@element_size * index)
|
27
|
+
|
28
|
+
# cast the memory to a PollItem
|
29
|
+
LibZMQ::PollItem.new pointer
|
30
|
+
end
|
31
|
+
end
|
32
|
+
alias :[] :get
|
33
|
+
|
34
|
+
def <<(obj)
|
35
|
+
@dirty = true
|
36
|
+
@items << obj
|
37
|
+
end
|
38
|
+
alias :push :<<
|
39
|
+
|
40
|
+
def delete sock
|
41
|
+
address = sock.socket.address
|
42
|
+
found = false
|
43
|
+
|
44
|
+
each_with_index do |item, index|
|
45
|
+
if address == item[:socket].address
|
46
|
+
@items.delete_at index
|
47
|
+
found = true
|
48
|
+
@dirty = true
|
49
|
+
clean
|
50
|
+
break
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# these semantics are different from the usual Array#delete; returns a
|
55
|
+
# boolean instead of the actual item or nil
|
56
|
+
found
|
57
|
+
end
|
58
|
+
|
59
|
+
def delete_at index
|
60
|
+
value = nil
|
61
|
+
unless @items.empty?
|
62
|
+
value = @items.delete_at index
|
63
|
+
@dirty = true
|
64
|
+
clean
|
65
|
+
end
|
66
|
+
|
67
|
+
value
|
68
|
+
end
|
69
|
+
|
70
|
+
def each &blk
|
71
|
+
clean
|
72
|
+
index = 0
|
73
|
+
until index >= @items.size do
|
74
|
+
struct = get index
|
75
|
+
yield struct
|
76
|
+
index += 1
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def each_with_index &blk
|
81
|
+
clean
|
82
|
+
index = 0
|
83
|
+
until index >= @items.size do
|
84
|
+
struct = get index
|
85
|
+
yield struct, index
|
86
|
+
index += 1
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def inspect
|
91
|
+
clean
|
92
|
+
str = ""
|
93
|
+
each { |item| str << "ptr [#{item[:socket]}], events [#{item[:events]}], revents [#{item[:revents]}], " }
|
94
|
+
str.chop.chop
|
95
|
+
end
|
96
|
+
|
97
|
+
def to_s(); inspect; end
|
98
|
+
|
99
|
+
private
|
100
|
+
|
101
|
+
# Allocate a contiguous chunk of memory and copy over the PollItem structs
|
102
|
+
# to this block. Note that the old +@store+ value goes out of scope so when
|
103
|
+
# it is garbage collected that native memory should be automatically freed.
|
104
|
+
def clean
|
105
|
+
if @dirty
|
106
|
+
@store = FFI::MemoryPointer.new @element_size, @items.size, true
|
107
|
+
|
108
|
+
# copy over
|
109
|
+
offset = 0
|
110
|
+
@items.each do |item|
|
111
|
+
LibC.memcpy(@store + offset, item.pointer, @element_size)
|
112
|
+
offset += @element_size
|
113
|
+
end
|
114
|
+
|
115
|
+
@dirty = false
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
end # class PollItems
|
120
|
+
end # module ZMQ
|