ffi-rzmq 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,210 @@
1
+
2
+ module ZMQ
3
+
4
+ ZMQ_MSG_INIT_SIZE_STR = 'zmq_msg_init_size'.freeze
5
+ ZMQ_MSG_INIT_DATA_STR = 'zmq_msg_init_data'.freeze
6
+ ZMQ_MSG_INIT_STR = 'zmq_msg_init'.freeze
7
+ ZMQ_MSG_CLOSE_STR = 'zmq_msg_close'.freeze
8
+ ZMQ_MSG_COPY_STR = 'zmq_msg_copy'.freeze
9
+ ZMQ_MSG_MOVE_STR = 'zmq_msg_move'.freeze
10
+ ZMQ_MSG_SIZE_STR = 'zmq_msg_size'.freeze
11
+
12
+ # The constructor optionally takes a string as an argument. It will
13
+ # copy this string to native memory in preparation for transmission.
14
+ # So, don't pass a string unless you intend to send it. Internally it
15
+ # calls #copy_in_string.
16
+ #
17
+ # Call #close to release buffers when you have *not* passed this on
18
+ # to Socket#send or Socket#recv. Those methods call #close on your
19
+ # behalf.
20
+ #
21
+ # (This class is not really zero-copy. Ruby makes this near impossible.)
22
+ #
23
+ # Message represents ruby equivalent of the +zmq_msg_t+ C struct.
24
+ # Access the underlying memory buffer and the buffer size using the
25
+ # #data and #size methods respectively.
26
+ #
27
+ # It is recommended that this class be composed inside another class for
28
+ # access to the underlying buffer. The outer wrapper class can provide
29
+ # nice accessors for the information in the data buffer; a clever
30
+ # implementation can probably lazily encode/decode the data buffer
31
+ # on demand. Lots of protocols send more information than is strictly
32
+ # necessary, so only decode (copy from the 0mq buffer to Ruby) that
33
+ # which is necessary.
34
+ #
35
+ # When you are done using a *received* message object, just let it go out of
36
+ # scope to release the memory. During the next garbage collection run
37
+ # it will call the equivalent of #LibZMQ.zmq_msg_close to release
38
+ # all buffers. Obviously, this automatic collection of message objects
39
+ # comes at the price of a larger memory footprint (for the
40
+ # finalizer proc object) and lower performance. If you wanted blistering
41
+ # performance, Ruby isn't there just yet.
42
+ #
43
+ # As noted above, for sent objects the underlying library will call close
44
+ # for you.
45
+ #
46
+ # class MyMessage
47
+ # def initialize msg_struct = nil
48
+ # @msg_t = msg_struct ? msg_struct : ZMQ::Message.new
49
+ # end
50
+ #
51
+ # def size() @size = @msg_t.size; end
52
+ #
53
+ # def decode
54
+ # @decoded_data = JSON.parse(@msg_t.copy_out_string)
55
+ # end
56
+ #
57
+ # def field1
58
+ # @field1 ||= decode[:field1]
59
+ # end
60
+ #
61
+ # def field2
62
+ # @field2 ||= decode[:field2]
63
+ # end
64
+ # ---
65
+ #
66
+ # message = Message.new
67
+ # successful_read = socket.recv message
68
+ # message = MyMessage.new message if successful_read
69
+ # puts "field1 is #{message.field1}"
70
+ #
71
+ class Message
72
+ include ZMQ::Util
73
+
74
+ def initialize message = nil
75
+ @state = :uninitialized
76
+
77
+ # allocate our own pointer so that we can tell it to *not* zero out
78
+ # the memory; it's pointless work since the library is going to
79
+ # overwrite it anyway.
80
+ @pointer = FFI::MemoryPointer.new LibZMQ::Msg.size, 1, false
81
+ @struct = LibZMQ::Msg.new @pointer
82
+
83
+ if message
84
+ copy_in_string message
85
+ else
86
+ # initialize an empty message structure to receive a message
87
+ result_code = LibZMQ.zmq_msg_init @struct
88
+ error_check ZMQ_MSG_INIT_STR, result_code
89
+ @state = :initialized
90
+ end
91
+ end
92
+
93
+ # Makes a copy of the ruby +string+ into a native memory buffer so
94
+ # that libzmq can send it. The underlying library will handle
95
+ # deallocation of the native memory buffer.
96
+ #
97
+ def copy_in_string string
98
+ copy_in_bytes string, string.size
99
+ end
100
+
101
+ # Makes a copy of +len+ bytes from the ruby string +bytes+. Library
102
+ # handles deallocation of the native memory buffer.
103
+ #
104
+ def copy_in_bytes bytes, len
105
+ # release any associated buffers if this Message object is being
106
+ # reused
107
+ close unless uninitialized?
108
+
109
+ data_buffer = LibC.malloc len
110
+ # writes the exact number of bytes, no null byte to terminate string
111
+ data_buffer.write_string bytes, len
112
+
113
+ # make sure we have a way to deallocate this memory if the object goes
114
+ # out of scope
115
+ define_finalizer
116
+
117
+ unless RBX
118
+ result_code = LibZMQ.zmq_msg_init_data @struct.pointer, data_buffer, len, LibZMQ::MessageDeallocator, nil
119
+ else
120
+ # no callback for freeing up memory; memory leak!
121
+ result_code = LibZMQ.zmq_msg_init_data @struct.pointer, data_buffer, len, nil, nil
122
+ end
123
+ error_check ZMQ_MSG_INIT_DATA_STR, result_code
124
+ @state = :initialized
125
+ end
126
+
127
+ # Provides the memory address of the +zmq_msg_t+ struct. Used mostly for
128
+ # passing to other methods accessing the underlying library that
129
+ # require a real data address.
130
+ #
131
+ def address
132
+ @struct.pointer
133
+ end
134
+ alias :pointer :address
135
+
136
+ def copy source
137
+ result_code = LibZMQ.zmq_msg_copy @struct.pointer, source.address
138
+ error_check ZMQ_MSG_COPY_STR, result_code
139
+ @state = :initialized
140
+ end
141
+
142
+ def move source
143
+ result_code = LibZMQ.zmq_msg_copy @struct.pointer, source.address
144
+ error_check ZMQ_MSG_MOVE_STR, result_code
145
+ @state = :initialized
146
+ end
147
+
148
+ # Provides the size of the data buffer for this +zmq_msg_t+ C struct.
149
+ #
150
+ def size
151
+ LibZMQ.zmq_msg_size @struct.pointer
152
+ end
153
+
154
+ # Returns a pointer to the data buffer.
155
+ # This pointer should *never* be freed. It will automatically be freed
156
+ # when the +message+ object goes out of scope and gets garbage
157
+ # collected.
158
+ #
159
+ def data
160
+ LibZMQ.zmq_msg_data @struct.pointer
161
+ end
162
+
163
+ # Returns the data buffer as a string.
164
+ #
165
+ # Note: If this is binary data, it won't print very prettily.
166
+ #
167
+ def copy_out_string
168
+ data.read_string(size)
169
+ end
170
+
171
+ # Manually release the message struct and its associated data
172
+ # buffer.
173
+ #
174
+ # The Message object is still valid after this call and can be used
175
+ # again for sending or receiving.
176
+ #
177
+ def close
178
+ LibZMQ.zmq_msg_close @struct.pointer
179
+ remove_finalizer
180
+ @state = :uninitialized
181
+ end
182
+
183
+
184
+ private
185
+
186
+ def uninitialized?(); :uninitialized == @state; end
187
+
188
+ def define_finalizer
189
+ ObjectSpace.define_finalizer(self, self.class.close(@struct))
190
+ end
191
+
192
+ def remove_finalizer
193
+ ObjectSpace.undefine_finalizer self
194
+ end
195
+
196
+ # Message finalizer
197
+ # Note that there is no error checking for the call to #zmq_msg_close.
198
+ # This is intentional. Since this code runs as a finalizer, there is no
199
+ # way to catch a raised exception anywhere near where the error actually
200
+ # occurred in the code, so we just ignore deallocation failures here.
201
+ def self.close struct
202
+ Proc.new do
203
+ # release the data buffer
204
+ LibZMQ.zmq_msg_close struct.pointer
205
+ end
206
+ end
207
+
208
+ end # class Message
209
+
210
+ end # module ZMQ
@@ -0,0 +1,186 @@
1
+
2
+ module ZMQ
3
+
4
+ ZMQ_POLL_STR = 'zmq_poll'.freeze
5
+
6
+ class Poller
7
+ include ZMQ::Util
8
+
9
+ attr_reader :readables, :writables
10
+
11
+ def initialize
12
+ @items = ZMQ::PollItems.new
13
+ @raw_to_socket = {}
14
+ @sockets = []
15
+ @readables = []
16
+ @writables = []
17
+ end
18
+
19
+ # Checks each poll item for selectability based on the poll items'
20
+ # registered +events+. Will block for up to +timeout+ milliseconds
21
+ # A millisecond is 1/1000 of a second, so to block for 1 second
22
+ # pass the value "1000" to #poll.
23
+ #
24
+ # Pass "-1" or +:blocking+ for +timeout+ for this call to block
25
+ # indefinitely.
26
+ #
27
+ # May raise a ZMQ::PollError exception. This occurs when one of the
28
+ # registered sockets belongs to an application thread in another
29
+ # Context.
30
+ #
31
+ def poll timeout = :blocking
32
+ unless @items.empty?
33
+ timeout = adjust timeout
34
+ items_triggered = LibZMQ.zmq_poll @items.address, @items.size, timeout
35
+ error_check ZMQ_POLL_STR, items_triggered >= 0 ? 0 : items_triggered
36
+ update_selectables
37
+ items_hash
38
+ else
39
+ {}
40
+ end
41
+ end
42
+
43
+ # The non-blocking version of #poll. See the #poll description for
44
+ # potential exceptions.
45
+ #
46
+ # May raise a ZMQ::PollError exception. This occurs when one of the
47
+ # registered sockets belongs to an application thread in another
48
+ # Context.
49
+ #
50
+ def poll_nonblock
51
+ poll 0
52
+ end
53
+
54
+ # Register the +sock+ for +events+. This method is idempotent meaning
55
+ # it can be called multiple times with the same data and the socket
56
+ # will only get registered at most once. Calling multiple times with
57
+ # different values for +events+ will OR the event information together.
58
+ #
59
+ # Does not raise any exceptions.
60
+ #
61
+ def register sock, events = ZMQ::POLLIN | ZMQ::POLLOUT, fd = 0
62
+ return unless sock || !fd.zero? || !events.zero?
63
+
64
+ @poll_items_dirty = true
65
+ item = @items.get(@sockets.index(sock))
66
+
67
+ unless item
68
+ @sockets << sock
69
+ item = LibZMQ::PollItem.new
70
+ @raw_to_socket[item.socket] = sock
71
+
72
+ case sock
73
+ when ZMQ::Socket, Socket
74
+ item[:socket] = sock.socket
75
+ item[:fd] = 0
76
+ else
77
+ item[:socket] = 0
78
+ item[:fd] = fd
79
+ end
80
+ end
81
+
82
+ item[:events] |= events
83
+
84
+ @items << item
85
+ end
86
+
87
+ # Deregister the +sock+ for +events+.
88
+ #
89
+ # Does not raise any exceptions.
90
+ #
91
+ def deregister sock, events, fd = 0
92
+ return unless sock || !fd.zero?
93
+
94
+ item = @items.get(@sockets.index(sock))
95
+
96
+ if item
97
+ # change the value in place
98
+ item[:events] ^= events
99
+
100
+ delete sock if items[:events].zero?
101
+ end
102
+ end
103
+
104
+ # A helper method to register a +sock+ as readable events only.
105
+ #
106
+ def register_readable sock
107
+ register sock, ZMQ::POLLIN, 0
108
+ end
109
+
110
+ # A helper method to register a +sock+ for writable events only.
111
+ #
112
+ def register_writable sock
113
+ register sock, ZMQ::POLLOUT, 0
114
+ end
115
+
116
+ # A helper method to deregister a +sock+ for readable events.
117
+ #
118
+ def deregister_readable sock
119
+ deregister sock, ZMQ::POLLIN, 0
120
+ end
121
+
122
+ # A helper method to deregister a +sock+ for writable events.
123
+ #
124
+ def deregister_writable sock
125
+ deregister sock, ZMQ::POLLOUT, 0
126
+ end
127
+
128
+ # Deletes the +sock+ for all subscribed events.
129
+ #
130
+ def delete sock
131
+ if index = @sockets.index(sock)
132
+ @items.delete_at index
133
+ @sockets.delete sock
134
+ @raw_to_socket.delete sock.socket
135
+ end
136
+ end
137
+
138
+ def size(); @items.size; end
139
+
140
+ def inspect
141
+ @items.inspect
142
+ end
143
+
144
+ def to_s(); inspect; end
145
+
146
+
147
+ private
148
+
149
+ def items_hash
150
+ hsh = {}
151
+
152
+ @items.each do |poll_item|
153
+ hsh[@raw_to_socket[poll_item.socket]] = poll_item
154
+ end
155
+
156
+ hsh
157
+ end
158
+
159
+ def update_selectables
160
+ @readables.clear
161
+ @writables.clear
162
+
163
+ @items.each do |poll_item|
164
+ @readables << @raw_to_socket[poll_item.socket] if poll_item.readable?
165
+ @writables << @raw_to_socket[poll_item.socket] if poll_item.writable?
166
+ end
167
+ end
168
+
169
+ # Convert the timeout value to something usable by
170
+ # the library.
171
+ #
172
+ # -1 or :blocking should be converted to -1.
173
+ #
174
+ # Users will pass in values measured as
175
+ # milliseconds, so we need to convert that value to
176
+ # microseconds for the library.
177
+ def adjust timeout
178
+ if :blocking == timeout || -1 == timeout
179
+ -1
180
+ else
181
+ timeout *= 1000
182
+ end
183
+ end
184
+ end
185
+
186
+ end # module ZMQ
@@ -0,0 +1,98 @@
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_at index
41
+ unless @items.empty?
42
+ @items.delete_at index
43
+ @dirty = true
44
+ clean
45
+ end
46
+ end
47
+
48
+ def each &blk
49
+ clean
50
+ index = 0
51
+ until index >= @items.size do
52
+ struct = get index
53
+ yield struct
54
+ index += 1
55
+ end
56
+ end
57
+
58
+ def each_with_index &blk
59
+ clean
60
+ index = 0
61
+ until index >= @items.size do
62
+ struct = get index
63
+ yield struct, index
64
+ index += 1
65
+ end
66
+ end
67
+
68
+ def inspect
69
+ clean
70
+ str = ""
71
+ each { |item| str << "ptr [#{item[:socket]}], events [#{item[:events]}], revents [#{item[:revents]}], " }
72
+ str.chop.chop
73
+ end
74
+
75
+ def to_s(); inspect; end
76
+
77
+ private
78
+
79
+ # Allocate a contiguous chunk of memory and copy over the PollItem structs
80
+ # to this block. Note that the old +@store+ value goes out of scope so when
81
+ # it is garbage collected that native memory should be automatically freed.
82
+ def clean
83
+ if @dirty
84
+ @store = FFI::MemoryPointer.new @element_size, @items.size, false
85
+
86
+ # copy over
87
+ offset = 0
88
+ @items.each do |item|
89
+ LibC.memcpy(@store + offset, item, @element_size)
90
+ offset += @element_size
91
+ end
92
+
93
+ @dirty = false
94
+ end
95
+ end
96
+
97
+ end # class PollItems
98
+ end # module ZMQ