ffi-rzmq 0.5.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.
@@ -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