cztop 0.10.0 → 0.11.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
[![Build Status on Travis CI](https://travis-ci.org/paddor/cztop.svg?branch=master)](https://travis-ci.org/paddor/cztop?branch=master)
|
10
14
|
[![Code Climate](https://codeclimate.com/repos/56677a7849f50a141c001784/badges/48f3cca3c62df9e4b17b/gpa.svg)](https://codeclimate.com/repos/56677a7849f50a141c001784/feed)
|
11
15
|
[![Inline docs](http://inch-ci.org/github/paddor/cztop.svg?branch=master&style=shields)](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
|