ffi-rxs 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|