cztop 0.10.0 → 0.11.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.
- checksums.yaml +4 -4
- data/.travis.yml +1 -0
- data/CHANGES.md +7 -0
- data/README.md +19 -9
- data/ci-scripts/install-deps +0 -1
- data/cztop.gemspec +1 -1
- data/lib/cztop/frame.rb +25 -3
- data/lib/cztop/has_ffi_delegate.rb +6 -1
- data/lib/cztop/metadata.rb +89 -0
- data/lib/cztop/poller/aggregated.rb +126 -0
- data/lib/cztop/poller/zmq.rb +78 -0
- data/lib/cztop/poller/zpoller.rb +113 -0
- data/lib/cztop/poller.rb +0 -299
- data/lib/cztop/socket/types.rb +57 -0
- data/lib/cztop/version.rb +1 -1
- data/lib/cztop/zsock_options.rb +30 -0
- data/lib/cztop.rb +4 -0
- metadata +8 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c4906ff9fd7c2ebd3fb3b33eadcab389799649ff
|
4
|
+
data.tar.gz: 0332b4b9a1fed07870e459298ca43fb82223acd8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8727db3d61b640eb5a2e0611086d180f6515418d7fd4ecebd6ff6fd76e15f82fb10116e2420121c4dc8279bf11b426604228971d03d646f244d8886735475db7
|
7
|
+
data.tar.gz: 838b219829c9df9f8fc04081d3e262360bda1cf02f5c5d7e8881c2593e4aa7921a1ecaea54fd0e344a091d0cc5e1338267375ab6758ab1eb60b652fc4db2bfd5
|
data/.travis.yml
CHANGED
data/CHANGES.md
CHANGED
@@ -1,3 +1,10 @@
|
|
1
|
+
0.11.0 (11/06/2016)
|
2
|
+
-----
|
3
|
+
* upgrade to czmq-ffi-gen's 0.12.x line (which includes the CZMQ v4.0.0 release)
|
4
|
+
* add support for RADIO/DISH and SCATTER/GATHER sockets
|
5
|
+
* add CZTop::Frame#group and #group= methods
|
6
|
+
* add CZTop::Metadata to encode/decode ZMTP metadata
|
7
|
+
|
1
8
|
0.10.0 (10/24/2016)
|
2
9
|
-----
|
3
10
|
* add Socket#readable? and #writable?
|
data/README.md
CHANGED
@@ -6,6 +6,10 @@ FFI binding of [CZMQ](https://github.com/zeromq/czmq) and has a focus on being
|
|
6
6
|
easy to use for Rubyists (POLS) and providing first class support for security
|
7
7
|
mechanisms (like CURVE).
|
8
8
|
|
9
|
+
You might wanna check out
|
10
|
+
[cztop-patterns](https://github.com/paddor/cztop-patterns). It's still very
|
11
|
+
new, but will contain some reusable patterns described in the Zguide.
|
12
|
+
|
9
13
|
[](https://travis-ci.org/paddor/cztop?branch=master)
|
10
14
|
[](https://codeclimate.com/repos/56677a7849f50a141c001784/feed)
|
11
15
|
[](http://inch-ci.org/github/paddor/cztop)
|
@@ -44,9 +48,11 @@ Here's an overview of the core classes:
|
|
44
48
|
* [Frame](http://www.rubydoc.info/gems/cztop/CZTop/Frame)
|
45
49
|
* [Message](http://www.rubydoc.info/gems/cztop/CZTop/Message)
|
46
50
|
* [Monitor](http://www.rubydoc.info/gems/cztop/CZTop/Monitor) < Actor
|
51
|
+
* [Metadata](http://www.rubydoc.info/gems/cztop/CZTop/Metadata)
|
47
52
|
* [Proxy](http://www.rubydoc.info/gems/cztop/CZTop/Proxy) < Actor
|
48
|
-
* [Poller](http://www.rubydoc.info/gems/cztop/CZTop/Poller)
|
53
|
+
* [Poller](http://www.rubydoc.info/gems/cztop/CZTop/Poller) (based on `zmq_poller_*()` functions)
|
49
54
|
* [Aggregated](http://www.rubydoc.info/gems/cztop/CZTop/Poller/Aggregated)
|
55
|
+
* [ZPoller](http://www.rubydoc.info/gems/cztop/CZTop/Poller/ZPoller)
|
50
56
|
* [Socket](http://www.rubydoc.info/gems/cztop/CZTop/Socket)
|
51
57
|
* [REQ](http://www.rubydoc.info/gems/cztop/CZTop/Socket/REQ) < Socket
|
52
58
|
* [REP](http://www.rubydoc.info/gems/cztop/CZTop/Socket/REP) < Socket
|
@@ -62,6 +68,10 @@ Here's an overview of the core classes:
|
|
62
68
|
* [STREAM](http://www.rubydoc.info/gems/cztop/CZTop/Socket/STREAM) < Socket
|
63
69
|
* [CLIENT](http://www.rubydoc.info/gems/cztop/CZTop/Socket/CLIENT) < Socket
|
64
70
|
* [SERVER](http://www.rubydoc.info/gems/cztop/CZTop/Socket/SERVER) < Socket
|
71
|
+
* [RADIO](http://www.rubydoc.info/gems/cztop/CZTop/Socket/RADIO) < Socket
|
72
|
+
* [DISH](http://www.rubydoc.info/gems/cztop/CZTop/Socket/DISH) < Socket
|
73
|
+
* [SCATTER](http://www.rubydoc.info/gems/cztop/CZTop/Socket/SCATTER) < Socket
|
74
|
+
* [GATHER](http://www.rubydoc.info/gems/cztop/CZTop/Socket/GATHER) < Socket
|
65
75
|
* [Z85](http://www.rubydoc.info/gems/cztop/CZTop/Z85)
|
66
76
|
* [Padded](http://www.rubydoc.info/gems/cztop/CZTop/Z85/Padded) < Z85
|
67
77
|
* [Pipe](http://www.rubydoc.info/gems/cztop/CZTop/Z85/Pipe)
|
@@ -88,11 +98,12 @@ More information in the [API documentation](http://www.rubydoc.info/github/paddo
|
|
88
98
|
* no need to manually pass some constant
|
89
99
|
* but you can: `CZTop::Socket.new_by_type(:REP)`
|
90
100
|
* e.g. `#subscribe` only exists on CZTop::Socket::SUB
|
91
|
-
*
|
92
|
-
*
|
93
|
-
* there
|
101
|
+
* DRAFT API ready
|
102
|
+
* CLIENT/SERVER/RADIO/DISH/SCATTER/GATHER and other DRAFT methods are supported if the libraries (ZMQ/CZMQ) have been compiled with DRAFT APIs enabled (`--enable-drafts`)
|
103
|
+
* there is `#routing_id` and `#routing_id=` on the following classes:
|
94
104
|
* CZTop::Message
|
95
105
|
* CZTop::Frame
|
106
|
+
* there is `#group` and `#group=` on CZTop::Frame
|
96
107
|
* ZMTP 3.1 heartbeat ready
|
97
108
|
* `socket.options.heartbeat_ivl = 2000`
|
98
109
|
* `socket.options.heartbeat_timeout = 8000`
|
@@ -101,12 +112,11 @@ More information in the [API documentation](http://www.rubydoc.info/github/paddo
|
|
101
112
|
|
102
113
|
You'll need:
|
103
114
|
|
104
|
-
* CZMQ
|
105
|
-
* ZMQ >= 4.2
|
106
|
-
|
107
|
-
For security mechanisms like CURVE, you'll need:
|
115
|
+
* CZMQ >= 4.0.0
|
116
|
+
* ZMQ >= 4.2.0
|
108
117
|
|
109
|
-
|
118
|
+
For security mechanisms like CURVE, it's recommended to use Libsodium. However,
|
119
|
+
ZMQ can be compiled with tweetnacl enabled.
|
110
120
|
|
111
121
|
To install on OSX using homebrew, run:
|
112
122
|
|
data/ci-scripts/install-deps
CHANGED
data/cztop.gemspec
CHANGED
@@ -18,7 +18,7 @@ Gem::Specification.new do |spec|
|
|
18
18
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
19
19
|
spec.require_paths = ["lib"]
|
20
20
|
|
21
|
-
spec.add_runtime_dependency "czmq-ffi-gen", "~> 0.
|
21
|
+
spec.add_runtime_dependency "czmq-ffi-gen", "~> 0.12.0"
|
22
22
|
|
23
23
|
spec.add_development_dependency "bundler", "~> 1.10"
|
24
24
|
spec.add_development_dependency "rake", "~> 10.0"
|
data/lib/cztop/frame.rb
CHANGED
@@ -137,13 +137,13 @@ module CZTop
|
|
137
137
|
ffi_delegate :size
|
138
138
|
|
139
139
|
# Gets the routing ID.
|
140
|
-
# @note This only set when the frame
|
141
|
-
# socket.
|
140
|
+
# @note This only set when the frame has been read from
|
141
|
+
# a {CZTop::Socket::SERVER} socket.
|
142
142
|
# @return [Integer] the routing ID, or 0 if unset
|
143
143
|
ffi_delegate :routing_id
|
144
144
|
|
145
145
|
# Sets a new routing ID.
|
146
|
-
# @note This is used when the frame is sent
|
146
|
+
# @note This is used when the frame is sent via a {CZTop::Socket::CLIENT}
|
147
147
|
# socket.
|
148
148
|
# @param new_routing_id [Integer] new routing ID
|
149
149
|
# @raise [RangeError] if new routing ID is out of +uint32_t+ range
|
@@ -154,5 +154,27 @@ module CZTop
|
|
154
154
|
raise RangeError if new_routing_id < 0
|
155
155
|
ffi_delegate.set_routing_id(new_routing_id)
|
156
156
|
end
|
157
|
+
|
158
|
+
# Gets the group (radio/dish pattern).
|
159
|
+
# @note This only set when the frame has been read from
|
160
|
+
# a {CZTop::Socket::DISH} socket.
|
161
|
+
# @return [String] the group
|
162
|
+
# @return [nil] when no group has been set
|
163
|
+
def group
|
164
|
+
group = ffi_delegate.group
|
165
|
+
return nil if group.empty?
|
166
|
+
group
|
167
|
+
end
|
168
|
+
|
169
|
+
# Sets a new group (radio/dish pattern).
|
170
|
+
# @note This is used when the frame is sent via a {CZTop::Socket::RADIO}
|
171
|
+
# socket.
|
172
|
+
# @param new_group [String] new group
|
173
|
+
# @raise [ArgumentError] if new group name is too long
|
174
|
+
# @return [new_group]
|
175
|
+
def group=(new_group)
|
176
|
+
rc = ffi_delegate.set_group(new_group)
|
177
|
+
raise_zmq_err("unable to set group to %p" % group) if rc == -1
|
178
|
+
end
|
157
179
|
end
|
158
180
|
end
|
@@ -15,8 +15,13 @@ module CZTop::HasFFIDelegate
|
|
15
15
|
# Attaches an FFI delegate to the current (probably new) {CZTop} object.
|
16
16
|
# @param ffi_delegate an instance of the corresponding class in the
|
17
17
|
# CZMQ::FFI namespace
|
18
|
-
# @raise [SystemCallError] if the FFI delegate's
|
18
|
+
# @raise [SystemCallError, ArgumentError, ...] if the FFI delegate's
|
19
|
+
# internal pointer is NULL
|
19
20
|
# @return [void]
|
21
|
+
# @note This only raises the correct exception when the creation of the new
|
22
|
+
# CZMQ object was the most recent thing done with the CZMQ library and
|
23
|
+
# thus CZMQ::FFI::Errors.errno is still reports the correct error number.
|
24
|
+
# @see raise_zmq_err
|
20
25
|
def attach_ffi_delegate(ffi_delegate)
|
21
26
|
raise_zmq_err(CZMQ::FFI::Errors.strerror) if ffi_delegate.null?
|
22
27
|
@ffi_delegate = ffi_delegate
|
@@ -0,0 +1,89 @@
|
|
1
|
+
module CZTop
|
2
|
+
# Useful to encode and decode metadata as defined by ZMTP.
|
3
|
+
#
|
4
|
+
# ABNF:
|
5
|
+
#
|
6
|
+
# metadata = *property
|
7
|
+
# property = name value
|
8
|
+
# name = OCTET 1*255name-char
|
9
|
+
# name-char = ALPHA | DIGIT | "-" | "_" | "." | "+"
|
10
|
+
# value = 4OCTET *OCTET ; Size in network byte order
|
11
|
+
#
|
12
|
+
# @see https://rfc.zeromq.org/spec:23/ZMTP
|
13
|
+
class Metadata
|
14
|
+
VALUE_MAXLEN = 2**31-1
|
15
|
+
|
16
|
+
# Raised when decoding malformed metadata.
|
17
|
+
class InvalidData < StandardError
|
18
|
+
end
|
19
|
+
|
20
|
+
# regular expression used to validate property names
|
21
|
+
NAME_REGEX = /\A[[:alnum:]_.+-]{1,255}\Z/.freeze
|
22
|
+
|
23
|
+
|
24
|
+
# @param metadata [Hash<Symbol, #to_s>]
|
25
|
+
# @return [String]
|
26
|
+
def self.dump(metadata)
|
27
|
+
ic_names = Set.new
|
28
|
+
metadata.map do |k, v|
|
29
|
+
ic_name = k.to_sym.downcase
|
30
|
+
if ic_names.include?(ic_name)
|
31
|
+
raise ArgumentError, "property #{k.inspect}: duplicate name"
|
32
|
+
else
|
33
|
+
ic_names << ic_name
|
34
|
+
end
|
35
|
+
name = k.to_s
|
36
|
+
if NAME_REGEX !~ name
|
37
|
+
raise ArgumentError, "property #{k.inspect}: invalid name"
|
38
|
+
end
|
39
|
+
value = v.to_s
|
40
|
+
if value.bytesize > VALUE_MAXLEN
|
41
|
+
raise ArgumentError, "property #{k.inspect}: value too long"
|
42
|
+
end
|
43
|
+
[name.size, name, value.bytesize, value].pack("CA*NA*")
|
44
|
+
end.join
|
45
|
+
end
|
46
|
+
|
47
|
+
# @param string [String, Frame, #to_s]
|
48
|
+
# @return [Hash]
|
49
|
+
def self.load(string)
|
50
|
+
properties = {}
|
51
|
+
io = StringIO.new(string)
|
52
|
+
until io.eof?
|
53
|
+
# check for zero length names
|
54
|
+
name_length = io.read(1).unpack("C").first
|
55
|
+
raise InvalidData, "zero-length property name" if name_length.zero?
|
56
|
+
name = io.read(name_length)
|
57
|
+
name_sym = name.to_sym.downcase
|
58
|
+
if properties.has_key?(name_sym)
|
59
|
+
raise InvalidData, "property #{name.inspect}: duplicate name"
|
60
|
+
end
|
61
|
+
|
62
|
+
value_length = io.read(4).unpack("N").first
|
63
|
+
value = io.read(value_length)
|
64
|
+
|
65
|
+
properties[name_sym] = value
|
66
|
+
end
|
67
|
+
new(properties)
|
68
|
+
end
|
69
|
+
|
70
|
+
# @param properties [Hash<Symbol, String>] the properties as loaded by
|
71
|
+
# {load}
|
72
|
+
def initialize(properties)
|
73
|
+
@properties = properties
|
74
|
+
end
|
75
|
+
|
76
|
+
# Gets the value corresponding to a property name. The case of the name
|
77
|
+
# is insignificant.
|
78
|
+
# @param name [Symbol, String] the property name
|
79
|
+
# @return [String] the value
|
80
|
+
def [](name)
|
81
|
+
@properties[name.to_sym.downcase]
|
82
|
+
end
|
83
|
+
|
84
|
+
# @return [Hash<Symbol, String] all properties
|
85
|
+
def to_h
|
86
|
+
@properties
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,126 @@
|
|
1
|
+
module CZTop
|
2
|
+
# This is a poller which is able to provide a list of readable and a list
|
3
|
+
# of writable sockets. This is useful for when you need to process socket
|
4
|
+
# events in batch, rather than one per event loop iteration.
|
5
|
+
#
|
6
|
+
# In particular, this is needed in Celluloid::ZMQ, where in a call to
|
7
|
+
# Celluloid::ZMQ::Reactor#run_once all readable/writable sockets need to
|
8
|
+
# be processed.
|
9
|
+
#
|
10
|
+
# = Implementation
|
11
|
+
#
|
12
|
+
# It wraps a {CZTop::Poller} and just does the following to support
|
13
|
+
# getting an array of readable/writable sockets:
|
14
|
+
#
|
15
|
+
# * in {#wait}, poll with given timeout
|
16
|
+
# * in case there was an event:
|
17
|
+
# * deregister the corresponding event(s) on the registered socket
|
18
|
+
# * poll again with zero timeout until no more sockets
|
19
|
+
# * repeat and accumulate results into two lists
|
20
|
+
#
|
21
|
+
# = Forwarded Methods
|
22
|
+
#
|
23
|
+
# The following methods are defined on this class too, and calls are
|
24
|
+
# forwarded directly to the actual {CZTop::Poller} instance:
|
25
|
+
#
|
26
|
+
# * {CZTop::Poller#add}
|
27
|
+
# * {CZTop::Poller#add_reader}
|
28
|
+
# * {CZTop::Poller#add_writer}
|
29
|
+
# * {CZTop::Poller#modify}
|
30
|
+
# * {CZTop::Poller#remove}
|
31
|
+
# * {CZTop::Poller#remove_reader}
|
32
|
+
# * {CZTop::Poller#remove_writer}
|
33
|
+
# * {CZTop::Poller#sockets}
|
34
|
+
#
|
35
|
+
class Poller::Aggregated
|
36
|
+
|
37
|
+
# @return [CZTop::Poller.new] the associated (regular) poller
|
38
|
+
attr_reader :poller
|
39
|
+
|
40
|
+
# @return [Array<CZTop::Socket>] readable sockets
|
41
|
+
attr_reader :readables
|
42
|
+
|
43
|
+
# @return [Array<CZTop::Socket>] writable sockets
|
44
|
+
attr_reader :writables
|
45
|
+
|
46
|
+
extend Forwardable
|
47
|
+
def_delegators :@poller,
|
48
|
+
:add,
|
49
|
+
:add_reader,
|
50
|
+
:add_writer,
|
51
|
+
:modify,
|
52
|
+
:remove,
|
53
|
+
:remove_reader,
|
54
|
+
:remove_writer,
|
55
|
+
:sockets
|
56
|
+
|
57
|
+
# Initializes the aggregated poller.
|
58
|
+
# @param poller [CZTop::Poller] the wrapped poller
|
59
|
+
def initialize(poller = CZTop::Poller.new)
|
60
|
+
@readables = []
|
61
|
+
@writables = []
|
62
|
+
@poller = poller
|
63
|
+
end
|
64
|
+
|
65
|
+
# Forgets all previous event information (which sockets are
|
66
|
+
# readable/writable) and waits for events anew. After getting the first
|
67
|
+
# event, {CZTop::Poller#wait} is called again with a zero-timeout to get
|
68
|
+
# all pending events to extract them into the aggregated lists of
|
69
|
+
# readable and writable sockets.
|
70
|
+
#
|
71
|
+
# For every event, the corresponding event mask flag is disabled for the
|
72
|
+
# associated socket, so it won't turn up again. Finally, all event masks
|
73
|
+
# are restored to what they were before the call to this method.
|
74
|
+
#
|
75
|
+
# @param timeout [Integer] how long to wait in ms, or 0 to avoid blocking,
|
76
|
+
# or -1 to wait indefinitely
|
77
|
+
# @return [Boolean] whether there have been any events
|
78
|
+
def wait(timeout = -1)
|
79
|
+
@readables = []
|
80
|
+
@writables = []
|
81
|
+
@event_masks = {}
|
82
|
+
|
83
|
+
if event = @poller.wait(timeout)
|
84
|
+
extract(event)
|
85
|
+
|
86
|
+
# get all other pending events, if any, but no more blocking
|
87
|
+
while event = @poller.wait(0)
|
88
|
+
extract(event)
|
89
|
+
end
|
90
|
+
|
91
|
+
restore_event_masks
|
92
|
+
return true
|
93
|
+
end
|
94
|
+
return false
|
95
|
+
end
|
96
|
+
|
97
|
+
private
|
98
|
+
|
99
|
+
# Extracts the event information, adds the socket to the correct list(s)
|
100
|
+
# and modifies the socket's event mask for the socket to not turn up
|
101
|
+
# again during the next call(s) to {CZTop::Poller#wait} within {#wait}.
|
102
|
+
#
|
103
|
+
# @param event [CZTop::Poller::Event]
|
104
|
+
# @return [void]
|
105
|
+
def extract(event)
|
106
|
+
event_mask = poller.event_mask_for_socket(event.socket)
|
107
|
+
@event_masks[event.socket] = event_mask
|
108
|
+
if event.readable?
|
109
|
+
@readables << event.socket
|
110
|
+
event_mask &= 0xFFFF ^ CZTop::Poller::ZMQ::POLLIN
|
111
|
+
end
|
112
|
+
if event.writable?
|
113
|
+
@writables << event.socket
|
114
|
+
event_mask &= 0xFFFF ^ CZTop::Poller::ZMQ::POLLOUT
|
115
|
+
end
|
116
|
+
poller.modify(event.socket, event_mask)
|
117
|
+
end
|
118
|
+
|
119
|
+
# Restores the event mask for all registered sockets to the state they
|
120
|
+
# were before the call to {#wait}.
|
121
|
+
# @return [void]
|
122
|
+
def restore_event_masks
|
123
|
+
@event_masks.each { |socket, mask| poller.modify(socket, mask) }
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
module CZTop
|
2
|
+
|
3
|
+
# CZTop's interface to the low-level +zmq_poller_*()+ functions.
|
4
|
+
module Poller::ZMQ
|
5
|
+
|
6
|
+
POLLIN = 1
|
7
|
+
POLLOUT = 2
|
8
|
+
POLLERR = 4
|
9
|
+
|
10
|
+
extend ::FFI::Library
|
11
|
+
lib_name = 'libzmq'
|
12
|
+
lib_paths = ['/usr/local/lib', '/opt/local/lib', '/usr/lib64']
|
13
|
+
.map { |path| "#{path}/#{lib_name}.#{::FFI::Platform::LIBSUFFIX}" }
|
14
|
+
ffi_lib lib_paths + [lib_name]
|
15
|
+
|
16
|
+
# This represents a +zmq_poller_event_t+ as in:
|
17
|
+
#
|
18
|
+
# typedef struct zmq_poller_event_t
|
19
|
+
# {
|
20
|
+
# void *socket;
|
21
|
+
# int fd;
|
22
|
+
# void *user_data;
|
23
|
+
# short events;
|
24
|
+
# } zmq_poller_event_t;
|
25
|
+
class PollerEvent < FFI::Struct
|
26
|
+
layout :socket, :pointer,
|
27
|
+
:fd, :int,
|
28
|
+
:user_data, :pointer,
|
29
|
+
:events, :short
|
30
|
+
|
31
|
+
# @return [Boolean] whether the socket is readable
|
32
|
+
def readable?
|
33
|
+
(self[:events] & POLLIN) > 0
|
34
|
+
end
|
35
|
+
|
36
|
+
# @return [Boolean] whether the socket is writable
|
37
|
+
def writable?
|
38
|
+
(self[:events] & POLLOUT) > 0
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
#ZMQ_EXPORT void *zmq_poller_new (void);
|
43
|
+
#ZMQ_EXPORT int zmq_poller_destroy (void **poller_p);
|
44
|
+
#ZMQ_EXPORT int zmq_poller_add (void *poller, void *socket, void *user_data, short events);
|
45
|
+
#ZMQ_EXPORT int zmq_poller_modify (void *poller, void *socket, short events);
|
46
|
+
#ZMQ_EXPORT int zmq_poller_remove (void *poller, void *socket);
|
47
|
+
#ZMQ_EXPORT int zmq_poller_wait (void *poller, zmq_poller_event_t *event, long timeout);
|
48
|
+
|
49
|
+
# Gracefully attaches a function. If it's not available, this creates
|
50
|
+
# a placeholder class method which, when called, simply raises
|
51
|
+
# NotImplementedError with a helpful message.
|
52
|
+
def self.attach_function(function_nickname, function_name, *args)
|
53
|
+
super
|
54
|
+
rescue ::FFI::NotFoundError
|
55
|
+
if $VERBOSE || $DEBUG
|
56
|
+
warn "CZTop: The ZMQ function #{function_name}() is not available. Don't use CZTop::Poller."
|
57
|
+
end
|
58
|
+
define_singleton_method(function_nickname) do |*|
|
59
|
+
raise NotImplementedError, "compile ZMQ with --enable-drafts"
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
opts = {
|
64
|
+
blocking: true # only necessary on MRI to deal with the GIL.
|
65
|
+
}
|
66
|
+
attach_function :poller_new, :zmq_poller_new, [], :pointer, **opts
|
67
|
+
attach_function :poller_destroy, :zmq_poller_destroy,
|
68
|
+
[:pointer], :int, **opts
|
69
|
+
attach_function :poller_add, :zmq_poller_add,
|
70
|
+
[:pointer, :pointer, :pointer, :short], :int, **opts
|
71
|
+
attach_function :poller_modify, :zmq_poller_modify,
|
72
|
+
[:pointer, :pointer, :short], :int, **opts
|
73
|
+
attach_function :poller_remove, :zmq_poller_remove,
|
74
|
+
[:pointer, :pointer], :int, **opts
|
75
|
+
attach_function :poller_wait, :zmq_poller_wait,
|
76
|
+
[:pointer, :pointer, :long], :int, **opts
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
module CZTop
|
2
|
+
# This is the trivial poller based on zpoller. It only supports polling
|
3
|
+
# for readability, but it also supports doing that on CLIENT/SERVER sockets,
|
4
|
+
# which is useful for {CZTop::Poller}.
|
5
|
+
#
|
6
|
+
# @see http://api.zeromq.org/czmq3-0:zpoller
|
7
|
+
class Poller::ZPoller
|
8
|
+
include HasFFIDelegate
|
9
|
+
extend CZTop::HasFFIDelegate::ClassMethods
|
10
|
+
include ::CZMQ::FFI
|
11
|
+
|
12
|
+
# Initializes the Poller. At least one reader has to be given.
|
13
|
+
# @param reader [Socket, Actor] socket to poll for input
|
14
|
+
# @param readers [Socket, Actor] any additional sockets to poll for input
|
15
|
+
def initialize(reader, *readers)
|
16
|
+
@sockets = {} # to keep references and return same instances
|
17
|
+
ptr = Zpoller.new(reader,
|
18
|
+
*readers.flat_map {|r| [ :pointer, r ] },
|
19
|
+
:pointer, nil)
|
20
|
+
attach_ffi_delegate(ptr)
|
21
|
+
remember_socket(reader)
|
22
|
+
readers.each { |r| remember_socket(r) }
|
23
|
+
end
|
24
|
+
|
25
|
+
# Adds another reader socket to the poller.
|
26
|
+
# @param reader [Socket, Actor] socket to poll for input
|
27
|
+
# @return [void]
|
28
|
+
# @raise [SystemCallError] if this fails
|
29
|
+
def add(reader)
|
30
|
+
rc = ffi_delegate.add(reader)
|
31
|
+
raise_zmq_err("unable to add socket %p" % reader) if rc == -1
|
32
|
+
remember_socket(reader)
|
33
|
+
end
|
34
|
+
|
35
|
+
# Removes a reader socket from the poller.
|
36
|
+
# @param reader [Socket, Actor] socket to remove
|
37
|
+
# @return [void]
|
38
|
+
# @raise [ArgumentError] if socket was invalid, e.g. it wasn't registered
|
39
|
+
# in this poller
|
40
|
+
# @raise [SystemCallError] if this fails for another reason
|
41
|
+
def remove(reader)
|
42
|
+
rc = ffi_delegate.remove(reader)
|
43
|
+
raise_zmq_err("unable to remove socket %p" % reader) if rc == -1
|
44
|
+
forget_socket(reader)
|
45
|
+
end
|
46
|
+
|
47
|
+
# Waits and returns the first socket that becomes readable.
|
48
|
+
# @param timeout [Integer] how long to wait in ms, or 0 to avoid
|
49
|
+
# blocking, or -1 to wait indefinitely
|
50
|
+
# @return [Socket, Actor] first socket of interest
|
51
|
+
# @return [nil] if the timeout expired or
|
52
|
+
# @raise [Interrupt] if the timeout expired or
|
53
|
+
def wait(timeout = -1)
|
54
|
+
ptr = ffi_delegate.wait(timeout)
|
55
|
+
if ptr.null?
|
56
|
+
raise Interrupt if ffi_delegate.terminated
|
57
|
+
return nil
|
58
|
+
end
|
59
|
+
return socket_by_ptr(ptr)
|
60
|
+
end
|
61
|
+
|
62
|
+
# Tells the zpoller to ignore interrupts. By default, {#wait} will return
|
63
|
+
# immediately if it detects an interrupt (when +zsys_interrupted+ is set
|
64
|
+
# to something other than zero). Calling this method will supress this
|
65
|
+
# behavior.
|
66
|
+
# @return [void]
|
67
|
+
def ignore_interrupts
|
68
|
+
ffi_delegate.ignore_interrupts
|
69
|
+
end
|
70
|
+
|
71
|
+
# By default the poller stops if the process receives a SIGINT or SIGTERM
|
72
|
+
# signal. This makes it impossible to shut-down message based architectures
|
73
|
+
# like zactors. This method lets you switch off break handling. The default
|
74
|
+
# nonstop setting is off (false).
|
75
|
+
#
|
76
|
+
# Setting this will cause {#wait} to never raise.
|
77
|
+
#
|
78
|
+
# @param flag [Boolean] whether the poller should run nonstop
|
79
|
+
def nonstop=(flag)
|
80
|
+
ffi_delegate.set_nonstop(flag)
|
81
|
+
end
|
82
|
+
|
83
|
+
private
|
84
|
+
|
85
|
+
# Remembers the socket so a call to {#wait} can return with the exact same
|
86
|
+
# instance of {Socket}, and it also makes sure the socket won't get
|
87
|
+
# GC'd.
|
88
|
+
# @param socket [Socket, Actor] the socket instance to remember
|
89
|
+
# @return [void]
|
90
|
+
def remember_socket(socket)
|
91
|
+
@sockets[socket.to_ptr.to_i] = socket
|
92
|
+
end
|
93
|
+
|
94
|
+
# Forgets the socket because it has been removed from the poller.
|
95
|
+
# @param socket [Socket, Actor] the socket instance to forget
|
96
|
+
# @return [void]
|
97
|
+
def forget_socket(socket)
|
98
|
+
@sockets.delete(socket.to_ptr.to_i)
|
99
|
+
end
|
100
|
+
|
101
|
+
# Gets the previously remembered socket associated to the given pointer.
|
102
|
+
# @param ptr [FFI::Pointer] the pointer to a socket
|
103
|
+
# @return [Socket, Actor] the socket associated to the given pointer
|
104
|
+
# @raise [SystemCallError] if no socket is registered under given pointer
|
105
|
+
def socket_by_ptr(ptr)
|
106
|
+
@sockets[ptr.to_i] or
|
107
|
+
# NOTE: This should never happen, since #wait will return nil if
|
108
|
+
# +zpoller_wait+ returned NULL. But it's better to fail early in case
|
109
|
+
# it ever returns a wrong pointer.
|
110
|
+
raise_zmq_err("no socket known for pointer #{ptr.inspect}")
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
data/lib/cztop/poller.rb
CHANGED
@@ -213,303 +213,4 @@ module CZTop
|
|
213
213
|
end
|
214
214
|
end
|
215
215
|
end
|
216
|
-
|
217
|
-
# This is a poller which is able to provide a list of readable and a list
|
218
|
-
# of writable sockets. This is useful for when you need to process socket
|
219
|
-
# events in batch, rather than one per event loop iteration.
|
220
|
-
#
|
221
|
-
# In particular, this is needed in Celluloid::ZMQ, where in a call to
|
222
|
-
# Celluloid::ZMQ::Reactor#run_once all readable/writable sockets need to
|
223
|
-
# be processed.
|
224
|
-
#
|
225
|
-
# = Implementation
|
226
|
-
#
|
227
|
-
# It wraps a {CZTop::Poller} and just does the following to support
|
228
|
-
# getting an array of readable/writable sockets:
|
229
|
-
#
|
230
|
-
# * in {#wait}, poll with given timeout
|
231
|
-
# * in case there was an event:
|
232
|
-
# * deregister the corresponding event(s) on the registered socket
|
233
|
-
# * poll again with zero timeout until no more sockets
|
234
|
-
# * repeat and accumulate results into two lists
|
235
|
-
#
|
236
|
-
# = Forwarded Methods
|
237
|
-
#
|
238
|
-
# The following methods are defined on this class too, and calls are
|
239
|
-
# forwarded directly to the actual {CZTop::Poller} instance:
|
240
|
-
#
|
241
|
-
# * {CZTop::Poller#add}
|
242
|
-
# * {CZTop::Poller#add_reader}
|
243
|
-
# * {CZTop::Poller#add_writer}
|
244
|
-
# * {CZTop::Poller#modify}
|
245
|
-
# * {CZTop::Poller#remove}
|
246
|
-
# * {CZTop::Poller#remove_reader}
|
247
|
-
# * {CZTop::Poller#remove_writer}
|
248
|
-
# * {CZTop::Poller#sockets}
|
249
|
-
#
|
250
|
-
class Poller::Aggregated
|
251
|
-
|
252
|
-
# @return [CZTop::Poller.new] the associated (regular) poller
|
253
|
-
attr_reader :poller
|
254
|
-
|
255
|
-
# @return [Array<CZTop::Socket>] readable sockets
|
256
|
-
attr_reader :readables
|
257
|
-
|
258
|
-
# @return [Array<CZTop::Socket>] writable sockets
|
259
|
-
attr_reader :writables
|
260
|
-
|
261
|
-
extend Forwardable
|
262
|
-
def_delegators :@poller,
|
263
|
-
:add,
|
264
|
-
:add_reader,
|
265
|
-
:add_writer,
|
266
|
-
:modify,
|
267
|
-
:remove,
|
268
|
-
:remove_reader,
|
269
|
-
:remove_writer,
|
270
|
-
:sockets
|
271
|
-
|
272
|
-
# Initializes the aggregated poller.
|
273
|
-
# @param poller [CZTop::Poller] the wrapped poller
|
274
|
-
def initialize(poller = CZTop::Poller.new)
|
275
|
-
@readables = []
|
276
|
-
@writables = []
|
277
|
-
@poller = poller
|
278
|
-
end
|
279
|
-
|
280
|
-
# Forgets all previous event information (which sockets are
|
281
|
-
# readable/writable) and waits for events anew. After getting the first
|
282
|
-
# event, {CZTop::Poller#wait} is called again with a zero-timeout to get
|
283
|
-
# all pending events to extract them into the aggregated lists of
|
284
|
-
# readable and writable sockets.
|
285
|
-
#
|
286
|
-
# For every event, the corresponding event mask flag is disabled for the
|
287
|
-
# associated socket, so it won't turn up again. Finally, all event masks
|
288
|
-
# are restored to what they were before the call to this method.
|
289
|
-
#
|
290
|
-
# @param timeout [Integer] how long to wait in ms, or 0 to avoid blocking,
|
291
|
-
# or -1 to wait indefinitely
|
292
|
-
# @return [Boolean] whether there have been any events
|
293
|
-
def wait(timeout = -1)
|
294
|
-
@readables = []
|
295
|
-
@writables = []
|
296
|
-
@event_masks = {}
|
297
|
-
|
298
|
-
if event = @poller.wait(timeout)
|
299
|
-
extract(event)
|
300
|
-
|
301
|
-
# get all other pending events, if any, but no more blocking
|
302
|
-
while event = @poller.wait(0)
|
303
|
-
extract(event)
|
304
|
-
end
|
305
|
-
|
306
|
-
restore_event_masks
|
307
|
-
return true
|
308
|
-
end
|
309
|
-
return false
|
310
|
-
end
|
311
|
-
|
312
|
-
private
|
313
|
-
|
314
|
-
# Extracts the event information, adds the socket to the correct list(s)
|
315
|
-
# and modifies the socket's event mask for the socket to not turn up
|
316
|
-
# again during the next call(s) to {CZTop::Poller#wait} within {#wait}.
|
317
|
-
#
|
318
|
-
# @param event [CZTop::Poller::Event]
|
319
|
-
# @return [void]
|
320
|
-
def extract(event)
|
321
|
-
event_mask = poller.event_mask_for_socket(event.socket)
|
322
|
-
@event_masks[event.socket] = event_mask
|
323
|
-
if event.readable?
|
324
|
-
@readables << event.socket
|
325
|
-
event_mask &= 0xFFFF ^ CZTop::Poller::ZMQ::POLLIN
|
326
|
-
end
|
327
|
-
if event.writable?
|
328
|
-
@writables << event.socket
|
329
|
-
event_mask &= 0xFFFF ^ CZTop::Poller::ZMQ::POLLOUT
|
330
|
-
end
|
331
|
-
poller.modify(event.socket, event_mask)
|
332
|
-
end
|
333
|
-
|
334
|
-
# Restores the event mask for all registered sockets to the state they
|
335
|
-
# were before the call to {#wait}.
|
336
|
-
# @return [void]
|
337
|
-
def restore_event_masks
|
338
|
-
@event_masks.each { |socket, mask| poller.modify(socket, mask) }
|
339
|
-
end
|
340
|
-
end
|
341
|
-
|
342
|
-
# CZTop's interface to the low-level +zmq_poll()+ function.
|
343
|
-
module Poller::ZMQ
|
344
|
-
|
345
|
-
POLLIN = 1
|
346
|
-
POLLOUT = 2
|
347
|
-
POLLERR = 4
|
348
|
-
|
349
|
-
extend ::FFI::Library
|
350
|
-
lib_name = 'libzmq'
|
351
|
-
lib_paths = ['/usr/local/lib', '/opt/local/lib', '/usr/lib64']
|
352
|
-
.map { |path| "#{path}/#{lib_name}.#{::FFI::Platform::LIBSUFFIX}" }
|
353
|
-
ffi_lib lib_paths + [lib_name]
|
354
|
-
|
355
|
-
# This represents a +zmq_poller_event_t+ as in:
|
356
|
-
#
|
357
|
-
# typedef struct zmq_poller_event_t
|
358
|
-
# {
|
359
|
-
# void *socket;
|
360
|
-
# int fd;
|
361
|
-
# void *user_data;
|
362
|
-
# short events;
|
363
|
-
# } zmq_poller_event_t;
|
364
|
-
class PollerEvent < FFI::Struct
|
365
|
-
layout :socket, :pointer,
|
366
|
-
:fd, :int,
|
367
|
-
:user_data, :pointer,
|
368
|
-
:events, :short
|
369
|
-
|
370
|
-
# @return [Boolean] whether the socket is readable
|
371
|
-
def readable?
|
372
|
-
(self[:events] & POLLIN) > 0
|
373
|
-
end
|
374
|
-
|
375
|
-
# @return [Boolean] whether the socket is writable
|
376
|
-
def writable?
|
377
|
-
(self[:events] & POLLOUT) > 0
|
378
|
-
end
|
379
|
-
end
|
380
|
-
|
381
|
-
#ZMQ_EXPORT void *zmq_poller_new (void);
|
382
|
-
#ZMQ_EXPORT int zmq_poller_destroy (void **poller_p);
|
383
|
-
#ZMQ_EXPORT int zmq_poller_add (void *poller, void *socket, void *user_data, short events);
|
384
|
-
#ZMQ_EXPORT int zmq_poller_modify (void *poller, void *socket, short events);
|
385
|
-
#ZMQ_EXPORT int zmq_poller_remove (void *poller, void *socket);
|
386
|
-
#ZMQ_EXPORT int zmq_poller_wait (void *poller, zmq_poller_event_t *event, long timeout);
|
387
|
-
|
388
|
-
opts = {
|
389
|
-
blocking: true # only necessary on MRI to deal with the GIL.
|
390
|
-
}
|
391
|
-
attach_function :poller_new, :zmq_poller_new, [], :pointer, **opts
|
392
|
-
attach_function :poller_destroy, :zmq_poller_destroy,
|
393
|
-
[:pointer], :int, **opts
|
394
|
-
attach_function :poller_add, :zmq_poller_add,
|
395
|
-
[:pointer, :pointer, :pointer, :short], :int, **opts
|
396
|
-
attach_function :poller_modify, :zmq_poller_modify,
|
397
|
-
[:pointer, :pointer, :short], :int, **opts
|
398
|
-
attach_function :poller_remove, :zmq_poller_remove,
|
399
|
-
[:pointer, :pointer], :int, **opts
|
400
|
-
attach_function :poller_wait, :zmq_poller_wait,
|
401
|
-
[:pointer, :pointer, :long], :int, **opts
|
402
|
-
end
|
403
|
-
|
404
|
-
# This is the trivial poller based on zpoller. It only supports polling
|
405
|
-
# for readability, but it also supports doing that on CLIENT/SERVER sockets,
|
406
|
-
# which is useful for {CZTop::Poller}.
|
407
|
-
#
|
408
|
-
# @see http://api.zeromq.org/czmq3-0:zpoller
|
409
|
-
class Poller::ZPoller
|
410
|
-
include HasFFIDelegate
|
411
|
-
extend CZTop::HasFFIDelegate::ClassMethods
|
412
|
-
include ::CZMQ::FFI
|
413
|
-
|
414
|
-
# Initializes the Poller. At least one reader has to be given.
|
415
|
-
# @param reader [Socket, Actor] socket to poll for input
|
416
|
-
# @param readers [Socket, Actor] any additional sockets to poll for input
|
417
|
-
def initialize(reader, *readers)
|
418
|
-
@sockets = {} # to keep references and return same instances
|
419
|
-
ptr = Zpoller.new(reader,
|
420
|
-
*readers.flat_map {|r| [ :pointer, r ] },
|
421
|
-
:pointer, nil)
|
422
|
-
attach_ffi_delegate(ptr)
|
423
|
-
remember_socket(reader)
|
424
|
-
readers.each { |r| remember_socket(r) }
|
425
|
-
end
|
426
|
-
|
427
|
-
# Adds another reader socket to the poller.
|
428
|
-
# @param reader [Socket, Actor] socket to poll for input
|
429
|
-
# @return [void]
|
430
|
-
# @raise [SystemCallError] if this fails
|
431
|
-
def add(reader)
|
432
|
-
rc = ffi_delegate.add(reader)
|
433
|
-
raise_zmq_err("unable to add socket %p" % reader) if rc == -1
|
434
|
-
remember_socket(reader)
|
435
|
-
end
|
436
|
-
|
437
|
-
# Removes a reader socket from the poller.
|
438
|
-
# @param reader [Socket, Actor] socket to remove
|
439
|
-
# @return [void]
|
440
|
-
# @raise [ArgumentError] if socket was invalid, e.g. it wasn't registered
|
441
|
-
# in this poller
|
442
|
-
# @raise [SystemCallError] if this fails for another reason
|
443
|
-
def remove(reader)
|
444
|
-
rc = ffi_delegate.remove(reader)
|
445
|
-
raise_zmq_err("unable to remove socket %p" % reader) if rc == -1
|
446
|
-
forget_socket(reader)
|
447
|
-
end
|
448
|
-
|
449
|
-
# Waits and returns the first socket that becomes readable.
|
450
|
-
# @param timeout [Integer] how long to wait in ms, or 0 to avoid
|
451
|
-
# blocking, or -1 to wait indefinitely
|
452
|
-
# @return [Socket, Actor] first socket of interest
|
453
|
-
# @return [nil] if the timeout expired or
|
454
|
-
# @raise [Interrupt] if the timeout expired or
|
455
|
-
def wait(timeout = -1)
|
456
|
-
ptr = ffi_delegate.wait(timeout)
|
457
|
-
if ptr.null?
|
458
|
-
raise Interrupt if ffi_delegate.terminated
|
459
|
-
return nil
|
460
|
-
end
|
461
|
-
return socket_by_ptr(ptr)
|
462
|
-
end
|
463
|
-
|
464
|
-
# Tells the zpoller to ignore interrupts. By default, {#wait} will return
|
465
|
-
# immediately if it detects an interrupt (when +zsys_interrupted+ is set
|
466
|
-
# to something other than zero). Calling this method will supress this
|
467
|
-
# behavior.
|
468
|
-
# @return [void]
|
469
|
-
def ignore_interrupts
|
470
|
-
ffi_delegate.ignore_interrupts
|
471
|
-
end
|
472
|
-
|
473
|
-
# By default the poller stops if the process receives a SIGINT or SIGTERM
|
474
|
-
# signal. This makes it impossible to shut-down message based architectures
|
475
|
-
# like zactors. This method lets you switch off break handling. The default
|
476
|
-
# nonstop setting is off (false).
|
477
|
-
#
|
478
|
-
# Setting this will cause {#wait} to never raise.
|
479
|
-
#
|
480
|
-
# @param flag [Boolean] whether the poller should run nonstop
|
481
|
-
def nonstop=(flag)
|
482
|
-
ffi_delegate.set_nonstop(flag)
|
483
|
-
end
|
484
|
-
|
485
|
-
private
|
486
|
-
|
487
|
-
# Remembers the socket so a call to {#wait} can return with the exact same
|
488
|
-
# instance of {Socket}, and it also makes sure the socket won't get
|
489
|
-
# GC'd.
|
490
|
-
# @param socket [Socket, Actor] the socket instance to remember
|
491
|
-
# @return [void]
|
492
|
-
def remember_socket(socket)
|
493
|
-
@sockets[socket.to_ptr.to_i] = socket
|
494
|
-
end
|
495
|
-
|
496
|
-
# Forgets the socket because it has been removed from the poller.
|
497
|
-
# @param socket [Socket, Actor] the socket instance to forget
|
498
|
-
# @return [void]
|
499
|
-
def forget_socket(socket)
|
500
|
-
@sockets.delete(socket.to_ptr.to_i)
|
501
|
-
end
|
502
|
-
|
503
|
-
# Gets the previously remembered socket associated to the given pointer.
|
504
|
-
# @param ptr [FFI::Pointer] the pointer to a socket
|
505
|
-
# @return [Socket, Actor] the socket associated to the given pointer
|
506
|
-
# @raise [SystemCallError] if no socket is registered under given pointer
|
507
|
-
def socket_by_ptr(ptr)
|
508
|
-
@sockets[ptr.to_i] or
|
509
|
-
# NOTE: This should never happen, since #wait will return nil if
|
510
|
-
# +zpoller_wait+ returned NULL. But it's better to fail early in case
|
511
|
-
# it ever returns a wrong pointer.
|
512
|
-
raise_zmq_err("no socket known for pointer #{ptr.inspect}")
|
513
|
-
end
|
514
|
-
end
|
515
216
|
end
|
data/lib/cztop/socket/types.rb
CHANGED
@@ -206,5 +206,62 @@ module CZTop
|
|
206
206
|
attach_ffi_delegate(Zsock.new_stream(endpoints))
|
207
207
|
end
|
208
208
|
end
|
209
|
+
|
210
|
+
# Group-based pub/sub (vs topic-based). This is the publisher socket.
|
211
|
+
# @see https://github.com/zeromq/libzmq/pull/1727
|
212
|
+
class RADIO < Socket
|
213
|
+
# @param endpoints [String] endpoints to connect to
|
214
|
+
def initialize(endpoints = nil)
|
215
|
+
attach_ffi_delegate(Zsock.new_radio(endpoints))
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
# Group-based pub/sub (vs topic-based). This is the subscriber socket.
|
220
|
+
# @see https://github.com/zeromq/libzmq/pull/1727
|
221
|
+
class DISH < Socket
|
222
|
+
# @param endpoints [String] endpoints to connect to
|
223
|
+
def initialize(endpoints = nil)
|
224
|
+
attach_ffi_delegate(Zsock.new_dish(endpoints))
|
225
|
+
end
|
226
|
+
|
227
|
+
# Joins the given group.
|
228
|
+
# @param group [String] group to join, up to 15 characters
|
229
|
+
# @return [void]
|
230
|
+
# @raise [ArgumentError] when group name is invalid or group has already
|
231
|
+
# been joined before
|
232
|
+
# @raise [SystemCallError] in case of failure
|
233
|
+
def join(group)
|
234
|
+
rc = ffi_delegate.join(group)
|
235
|
+
raise_zmq_err("unable to join group %p" % group) if rc == -1
|
236
|
+
end
|
237
|
+
|
238
|
+
# Leaves the given group.
|
239
|
+
# @param group [String] group to leave
|
240
|
+
# @return [void]
|
241
|
+
# @raise [ArgumentError] when group wasn't joined before
|
242
|
+
# @raise [SystemCallError] in case of another failure
|
243
|
+
def leave(group)
|
244
|
+
rc = ffi_delegate.leave(group)
|
245
|
+
raise_zmq_err("unable to leave group %p" % group) if rc == -1
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
# Scatter/gather pattern.
|
250
|
+
# @see https://github.com/zeromq/libzmq/pull/1909
|
251
|
+
class SCATTER < Socket
|
252
|
+
# @param endpoints [String] endpoints to connect to
|
253
|
+
def initialize(endpoints = nil)
|
254
|
+
attach_ffi_delegate(Zsock.new_scatter(endpoints))
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
258
|
+
# Scatter/gather pattern.
|
259
|
+
# @see https://github.com/zeromq/libzmq/pull/1909
|
260
|
+
class GATHER < Socket
|
261
|
+
# @param endpoints [String] endpoints to connect to
|
262
|
+
def initialize(endpoints = nil)
|
263
|
+
attach_ffi_delegate(Zsock.new_gather(endpoints))
|
264
|
+
end
|
265
|
+
end
|
209
266
|
end
|
210
267
|
end
|
data/lib/cztop/version.rb
CHANGED
data/lib/cztop/zsock_options.rb
CHANGED
@@ -330,6 +330,18 @@ module CZTop
|
|
330
330
|
#int zsock_tcp_keepalive_intvl (void *self);
|
331
331
|
#int zsock_rcvmore (void *self);
|
332
332
|
#char * zsock_last_endpoint (void *self);
|
333
|
+
#attach_function :zsock_connect_timeout, [:pointer], :int, **opts
|
334
|
+
#attach_function :zsock_handshake_ivl, [:pointer], :int, **opts
|
335
|
+
#attach_function :zsock_invert_matching, [:pointer], :int, **opts
|
336
|
+
#attach_function :zsock_multicast_maxtpdu, [:pointer], :int, **opts
|
337
|
+
#attach_function :zsock_socks_proxy, [:pointer], :pointer, **opts
|
338
|
+
#attach_function :zsock_tcp_maxrt, [:pointer], :int, **opts
|
339
|
+
#attach_function :zsock_thread_safe, [:pointer], :int, **opts
|
340
|
+
#attach_function :zsock_use_fd, [:pointer], :int, **opts
|
341
|
+
#attach_function :zsock_vmci_buffer_max_size, [:pointer], :int, **opts
|
342
|
+
#attach_function :zsock_vmci_buffer_min_size, [:pointer], :int, **opts
|
343
|
+
#attach_function :zsock_vmci_buffer_size, [:pointer], :int, **opts
|
344
|
+
#attach_function :zsock_vmci_connect_timeout, [:pointer], :int, **opts
|
333
345
|
#
|
334
346
|
#// Set socket options
|
335
347
|
#void zsock_set_router_handover (void *self, int router_handover);
|
@@ -358,6 +370,24 @@ module CZTop
|
|
358
370
|
#void zsock_set_tcp_keepalive_idle (void *self, int tcp_keepalive_idle);
|
359
371
|
#void zsock_set_tcp_keepalive_cnt (void *self, int tcp_keepalive_cnt);
|
360
372
|
#void zsock_set_tcp_keepalive_intvl (void *self, int tcp_keepalive_intvl);
|
373
|
+
#attach_function :zsock_set_connect_rid, [:pointer, :string], :void, **opts
|
374
|
+
#attach_function :zsock_set_connect_rid_bin, [:pointer, :pointer], :void, **opts
|
375
|
+
#attach_function :zsock_set_connect_timeout, [:pointer, :int], :void, **opts
|
376
|
+
#attach_function :zsock_set_handshake_ivl, [:pointer, :int], :void, **opts
|
377
|
+
#attach_function :zsock_set_invert_matching, [:pointer, :int], :void, **opts
|
378
|
+
#attach_function :zsock_set_multicast_maxtpdu, [:pointer, :int], :void, **opts
|
379
|
+
#attach_function :zsock_set_socks_proxy, [:pointer, :string], :void, **opts
|
380
|
+
#attach_function :zsock_set_stream_notify, [:pointer, :int], :void, **opts
|
381
|
+
#attach_function :zsock_set_tcp_maxrt, [:pointer, :int], :void, **opts
|
382
|
+
#attach_function :zsock_set_use_fd, [:pointer, :int], :void, **opts
|
383
|
+
#attach_function :zsock_set_vmci_buffer_max_size, [:pointer, :int], :void, **opts
|
384
|
+
#attach_function :zsock_set_vmci_buffer_min_size, [:pointer, :int], :void, **opts
|
385
|
+
#attach_function :zsock_set_vmci_buffer_size, [:pointer, :int], :void, **opts
|
386
|
+
#attach_function :zsock_set_vmci_connect_timeout, [:pointer, :int], :void, **opts
|
387
|
+
#attach_function :zsock_set_xpub_manual, [:pointer, :int], :void, **opts
|
388
|
+
#attach_function :zsock_set_xpub_nodrop, [:pointer, :int], :void, **opts
|
389
|
+
#attach_function :zsock_set_xpub_verboser, [:pointer, :int], :void, **opts
|
390
|
+
#attach_function :zsock_set_xpub_welcome_msg, [:pointer, :string], :void, **opts
|
361
391
|
end
|
362
392
|
end
|
363
393
|
end
|
data/lib/cztop.rb
CHANGED
@@ -31,6 +31,10 @@ require_relative 'cztop/config/comments'
|
|
31
31
|
require_relative 'cztop/config/traversing'
|
32
32
|
require_relative 'cztop/config/serialization'
|
33
33
|
require_relative 'cztop/message/frames'
|
34
|
+
require_relative 'cztop/metadata'
|
35
|
+
require_relative 'cztop/poller/aggregated'
|
36
|
+
require_relative 'cztop/poller/zmq'
|
37
|
+
require_relative 'cztop/poller/zpoller'
|
34
38
|
require_relative 'cztop/socket/types'
|
35
39
|
require_relative 'cztop/z85/padded'
|
36
40
|
require_relative 'cztop/z85/pipe'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cztop
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.11.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Patrik Wenger
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-11-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: czmq-ffi-gen
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 0.
|
19
|
+
version: 0.12.0
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: 0.
|
26
|
+
version: 0.12.0
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: bundler
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -261,8 +261,12 @@ files:
|
|
261
261
|
- lib/cztop/has_ffi_delegate.rb
|
262
262
|
- lib/cztop/message.rb
|
263
263
|
- lib/cztop/message/frames.rb
|
264
|
+
- lib/cztop/metadata.rb
|
264
265
|
- lib/cztop/monitor.rb
|
265
266
|
- lib/cztop/poller.rb
|
267
|
+
- lib/cztop/poller/aggregated.rb
|
268
|
+
- lib/cztop/poller/zmq.rb
|
269
|
+
- lib/cztop/poller/zpoller.rb
|
266
270
|
- lib/cztop/polymorphic_zsock_methods.rb
|
267
271
|
- lib/cztop/proxy.rb
|
268
272
|
- lib/cztop/send_receive_methods.rb
|