rbczmq 0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (101) hide show
  1. data/.gitignore +23 -0
  2. data/.travis.yml +19 -0
  3. data/Gemfile +5 -0
  4. data/Gemfile.lock +19 -0
  5. data/MIT-LICENSE +20 -0
  6. data/README.rdoc +247 -0
  7. data/Rakefile +67 -0
  8. data/examples/loop.rb +109 -0
  9. data/examples/poller.rb +37 -0
  10. data/examples/pub_sub.rb +101 -0
  11. data/examples/push_pull.rb +104 -0
  12. data/examples/req_rep.rb +100 -0
  13. data/ext/czmq.tar.gz +0 -0
  14. data/ext/rbczmq/context.c +280 -0
  15. data/ext/rbczmq/context.h +26 -0
  16. data/ext/rbczmq/extconf.rb +138 -0
  17. data/ext/rbczmq/frame.c +401 -0
  18. data/ext/rbczmq/frame.h +24 -0
  19. data/ext/rbczmq/jruby.h +22 -0
  20. data/ext/rbczmq/loop.c +413 -0
  21. data/ext/rbczmq/loop.h +24 -0
  22. data/ext/rbczmq/message.c +620 -0
  23. data/ext/rbczmq/message.h +24 -0
  24. data/ext/rbczmq/poller.c +308 -0
  25. data/ext/rbczmq/poller.h +29 -0
  26. data/ext/rbczmq/pollitem.c +251 -0
  27. data/ext/rbczmq/pollitem.h +25 -0
  28. data/ext/rbczmq/rbczmq_ext.c +198 -0
  29. data/ext/rbczmq/rbczmq_ext.h +94 -0
  30. data/ext/rbczmq/rbczmq_prelude.h +22 -0
  31. data/ext/rbczmq/rubinius.h +24 -0
  32. data/ext/rbczmq/ruby18.h +43 -0
  33. data/ext/rbczmq/ruby19.h +15 -0
  34. data/ext/rbczmq/socket.c +1570 -0
  35. data/ext/rbczmq/socket.h +136 -0
  36. data/ext/rbczmq/timer.c +110 -0
  37. data/ext/rbczmq/timer.h +23 -0
  38. data/ext/zeromq.tar.gz +0 -0
  39. data/lib/rbczmq.rb +3 -0
  40. data/lib/zmq.rb +77 -0
  41. data/lib/zmq/context.rb +50 -0
  42. data/lib/zmq/default_handler.rb +16 -0
  43. data/lib/zmq/frame.rb +11 -0
  44. data/lib/zmq/handler.rb +76 -0
  45. data/lib/zmq/loop.rb +131 -0
  46. data/lib/zmq/message.rb +9 -0
  47. data/lib/zmq/poller.rb +22 -0
  48. data/lib/zmq/pollitem.rb +31 -0
  49. data/lib/zmq/socket.rb +125 -0
  50. data/lib/zmq/socket/dealer.rb +33 -0
  51. data/lib/zmq/socket/pair.rb +39 -0
  52. data/lib/zmq/socket/pub.rb +30 -0
  53. data/lib/zmq/socket/pull.rb +29 -0
  54. data/lib/zmq/socket/push.rb +32 -0
  55. data/lib/zmq/socket/rep.rb +37 -0
  56. data/lib/zmq/socket/req.rb +37 -0
  57. data/lib/zmq/socket/router.rb +38 -0
  58. data/lib/zmq/socket/sub.rb +27 -0
  59. data/lib/zmq/timer.rb +12 -0
  60. data/lib/zmq/version.rb +5 -0
  61. data/perf/pair.rb +7 -0
  62. data/perf/pair/local.rb +22 -0
  63. data/perf/pair/remote.rb +25 -0
  64. data/perf/pub_sub.rb +7 -0
  65. data/perf/pub_sub/local.rb +22 -0
  66. data/perf/pub_sub/remote.rb +25 -0
  67. data/perf/push_pull.rb +7 -0
  68. data/perf/push_pull/local.rb +21 -0
  69. data/perf/push_pull/remote.rb +25 -0
  70. data/perf/req_rep.rb +7 -0
  71. data/perf/req_rep/local.rb +35 -0
  72. data/perf/req_rep/remote.rb +28 -0
  73. data/perf/runner.rb +142 -0
  74. data/rbczmq.gemspec +22 -0
  75. data/test/helper.rb +21 -0
  76. data/test/socket/test_dealer_socket.rb +14 -0
  77. data/test/socket/test_pair_socket.rb +24 -0
  78. data/test/socket/test_pair_sockets.rb +74 -0
  79. data/test/socket/test_pub_socket.rb +17 -0
  80. data/test/socket/test_pub_sub_sockets.rb +87 -0
  81. data/test/socket/test_pull_socket.rb +17 -0
  82. data/test/socket/test_push_pull_sockets.rb +81 -0
  83. data/test/socket/test_push_socket.rb +17 -0
  84. data/test/socket/test_rep_socket.rb +25 -0
  85. data/test/socket/test_req_rep_sockets.rb +42 -0
  86. data/test/socket/test_req_socket.rb +27 -0
  87. data/test/socket/test_router_socket.rb +14 -0
  88. data/test/socket/test_routing.rb +66 -0
  89. data/test/socket/test_sub_socket.rb +17 -0
  90. data/test/test_context.rb +86 -0
  91. data/test/test_frame.rb +78 -0
  92. data/test/test_handler.rb +28 -0
  93. data/test/test_loop.rb +252 -0
  94. data/test/test_message.rb +201 -0
  95. data/test/test_poller.rb +154 -0
  96. data/test/test_pollitem.rb +78 -0
  97. data/test/test_socket.rb +403 -0
  98. data/test/test_threading.rb +34 -0
  99. data/test/test_timer.rb +37 -0
  100. data/test/test_zmq.rb +62 -0
  101. metadata +208 -0
@@ -0,0 +1,37 @@
1
+ # encoding: utf-8
2
+
3
+ $:.unshift('.')
4
+ $:.unshift(File.expand_path(File.dirname(__FILE__)) + '/../lib')
5
+ require 'zmq'
6
+
7
+ ctx = ZMQ::Context.new
8
+ pub = ctx.bind(:PUB, 'inproc://example.poller')
9
+
10
+ subscribers = []
11
+
12
+ poller = ZMQ::Poller.new
13
+
14
+ 5.times do
15
+ sub = ctx.connect(:SUB, 'inproc://example.poller')
16
+ sub.subscribe("")
17
+ subscribers << sub
18
+ poller.register_readable(sub)
19
+ end
20
+
21
+ puts "[#{subscribers.size}] subscribers registered with the poller"
22
+ p subscribers
23
+
24
+ puts "publisher sends 'test'"
25
+ pub.send("test")
26
+
27
+ puts "poll, timeout 1s"
28
+ poller.poll(1)
29
+ puts "readable sockets ..."
30
+ p poller.readables
31
+ puts "writable sockets ..."
32
+ p poller.writables
33
+
34
+ puts "Subscriber sockets can receive without blocking ..."
35
+ p subscribers.map{|s| s.recv_nonblock }
36
+
37
+ ctx.destroy
@@ -0,0 +1,101 @@
1
+ # encoding: utf-8
2
+
3
+ $:.unshift('.')
4
+ $:.unshift(File.expand_path(File.dirname(__FILE__)) + '/../lib')
5
+ require 'zmq'
6
+ require 'pp'
7
+
8
+ # PUB / SUB topology
9
+
10
+ Thread.abort_on_exception = true
11
+
12
+ class Consumer
13
+ attr_reader :thread
14
+ def initialize(ctx, endpoint, topic = "")
15
+ @thread = nil
16
+ @connect = Proc.new do
17
+ @socket = ctx.socket(:SUB)
18
+ @socket.subscribe("")
19
+ # verbose output
20
+ @socket.verbose = true
21
+ @socket.subscribe(topic)
22
+ @socket.connect(endpoint)
23
+ @socket.linger = 1
24
+ end
25
+ @jobs, @working = 0, 0.0
26
+ end
27
+
28
+ def start
29
+ @thread = Thread.new do
30
+ @connect.call
31
+ loop do
32
+ break if Thread.current[:interrupted]
33
+ perform(@socket.recv)
34
+ end
35
+ end
36
+ self
37
+ end
38
+
39
+ def stop
40
+ return unless @thread
41
+ @thread[:interrupted] = true
42
+ @thread.join(0.1)
43
+ stats
44
+ end
45
+
46
+ def perform(work)
47
+ # Random hot loop to simulate CPU intensive work
48
+ start = Time.now
49
+ work.to_i.times{}
50
+ @jobs += 1
51
+ @working += (Time.now - start).to_f
52
+ end
53
+
54
+ private
55
+ def stats
56
+ puts "Processed #{@jobs} jobs in %.4f seconds" % @working
57
+ $stdout.flush
58
+ end
59
+ end
60
+
61
+ class Producer
62
+ def initialize(ctx, endpoint, topic = "")
63
+ @ctx, @endpoint, @topic, @consumers = ctx, endpoint, topic, []
64
+ @socket = ctx.socket(:PUB)
65
+ # verbose output
66
+ @socket.verbose = true
67
+ @socket.bind(endpoint)
68
+ @socket.linger = 1
69
+ @interrupted = false
70
+ end
71
+
72
+ def spawn_consumers(count = 10)
73
+ count.times do
74
+ @consumers << Consumer.new(@ctx, @endpoint, @topic).start
75
+ sleep 0.01 # give each thread time to spin up
76
+ end
77
+ end
78
+
79
+ def start(messages = 100)
80
+ messages.to_i.times do
81
+ # Tasks are hot loops with random 0 to 100k iterations
82
+ work = "#{@topic}#{rand(100_000).to_s}"
83
+ @socket.send(work)
84
+ break if @interrupted
85
+ end
86
+ @consumers.each{|c| c.stop }
87
+ @ctx.destroy
88
+ end
89
+
90
+ def stop
91
+ @interrupted = true
92
+ end
93
+ end
94
+
95
+ ctx = ZMQ::Context.new
96
+ producer = Producer.new(ctx, 'inproc://example.pub_sub')
97
+ producer.spawn_consumers
98
+ trap(:INT) do
99
+ producer.stop
100
+ end
101
+ producer.start(ENV['MESSAGES'] || 1000)
@@ -0,0 +1,104 @@
1
+ # encoding: utf-8
2
+
3
+ $:.unshift('.')
4
+ $:.unshift(File.expand_path(File.dirname(__FILE__)) + '/../lib')
5
+ require 'zmq'
6
+ require 'pp'
7
+
8
+ # PUSH / PULL topology that demonstrates work being distributed in round robin fashion to a pool of worker threads.
9
+ # This pattern is a good start where little operational complexity is crucial and can scale out to multiple processes
10
+ # and boxes by just swapping out the transport.
11
+
12
+ Thread.abort_on_exception = true
13
+
14
+ class Worker
15
+ attr_reader :thread
16
+ def initialize(ctx, endpoint, watermark = 1000)
17
+ @thread = nil
18
+ @connect = Proc.new do
19
+ @socket = ctx.socket(:PULL)
20
+ # verbose output
21
+ @socket.verbose = true
22
+ @socket.connect(endpoint)
23
+ # Limit the amount of work queued for this worker. When the high water mark ceiling hits, a particular worker
24
+ # is ignored during round robin distribution
25
+ @socket.hwm = watermark
26
+ @socket.linger = 0
27
+ end
28
+ @jobs, @working = 0, 0.0
29
+ end
30
+
31
+ def start
32
+ @thread = Thread.new do
33
+ @connect.call
34
+ loop do
35
+ break if Thread.current[:interrupted]
36
+ perform(@socket.recv)
37
+ end
38
+ end
39
+ self
40
+ end
41
+
42
+ def stop
43
+ return unless @thread
44
+ @thread[:interrupted] = true
45
+ @thread.join(0.1)
46
+ stats
47
+ end
48
+
49
+ def perform(work)
50
+ # Random hot loop to simulate CPU intensive work
51
+ start = Time.now
52
+ work.to_i.times{}
53
+ @jobs += 1
54
+ @working += (Time.now - start).to_f
55
+ end
56
+
57
+ private
58
+ def stats
59
+ puts "Processed #{@jobs} jobs in %.4f seconds" % @working
60
+ $stdout.flush
61
+ end
62
+ end
63
+
64
+ class Master
65
+ def initialize(ctx, endpoint)
66
+ @ctx, @endpoint, @workers = ctx, endpoint, []
67
+ @socket = ctx.socket(:PUSH)
68
+ # verbose output
69
+ @socket.verbose = true
70
+ @socket.bind(endpoint)
71
+ @socket.linger = 0
72
+ @interrupted = false
73
+ end
74
+
75
+ def spawn_workers(count = 10)
76
+ count.times do
77
+ @workers << Worker.new(@ctx, @endpoint).start
78
+ sleep 0.01 # give each thread time to spin up
79
+ end
80
+ end
81
+
82
+ def start(messages = 100)
83
+ messages.to_i.times do
84
+ # Tasks are hot loops with random 0 to 100k iterations
85
+ work = rand(100_000).to_s
86
+ @socket.send(work)
87
+ break if @interrupted
88
+ end
89
+ @workers.each{|w| w.stop }
90
+ @ctx.destroy
91
+ end
92
+
93
+ def stop
94
+ @interrupted = true
95
+ end
96
+ end
97
+
98
+ ctx = ZMQ::Context.new
99
+ master = Master.new(ctx, 'inproc://example.push_pull')
100
+ master.spawn_workers
101
+ trap(:INT) do
102
+ master.stop
103
+ end
104
+ master.start(ENV['MESSAGES'] || 1000)
@@ -0,0 +1,100 @@
1
+ # encoding: utf-8
2
+
3
+ $:.unshift('.')
4
+ $:.unshift(File.expand_path(File.dirname(__FILE__)) + '/../lib')
5
+ require 'zmq'
6
+ require 'pp'
7
+
8
+ # REQ / REP topology
9
+
10
+ Thread.abort_on_exception = true
11
+
12
+ class Server
13
+ def initialize(ctx, endpoint)
14
+ @thread = nil
15
+ @connect = Proc.new do
16
+ @socket = ctx.socket(:REP)
17
+ # verbose output
18
+ @socket.verbose = true
19
+ @socket.bind(endpoint)
20
+ @socket.linger = 1
21
+ end
22
+ @jobs, @working = 0, 0.0
23
+ end
24
+
25
+ def start
26
+ @thread = Thread.new do
27
+ @connect.call
28
+ loop do
29
+ perform(@socket.recv)
30
+ break if Thread.current[:interrupted]
31
+ end
32
+ end
33
+ self
34
+ end
35
+
36
+ def stop
37
+ return unless @thread
38
+ @thread[:interrupted] = true
39
+ @thread.join(0.1) unless @thread.stop?
40
+ stats
41
+ end
42
+
43
+ def perform(work)
44
+ # Random hot loop to simulate CPU intensive work
45
+ start = Time.now
46
+ work.to_i.times{}
47
+ @jobs += 1
48
+ @working += (Time.now - start).to_f
49
+ @socket.send "done"
50
+ end
51
+
52
+ private
53
+ def stats
54
+ puts "Processed #{@jobs} jobs in %.4f seconds" % @working
55
+ $stdout.flush
56
+ end
57
+ end
58
+
59
+ class Client
60
+ def initialize(ctx, endpoint)
61
+ @ctx, @endpoint, @server, @interrupted = ctx, endpoint, nil, false
62
+ @socket = ctx.socket(:REQ)
63
+ # verbose output
64
+ @socket.verbose = true
65
+ end
66
+
67
+ def spawn_server
68
+ @server = Server.new(@ctx, @endpoint).start
69
+ sleep 0.01 # give each thread time to spin up
70
+ connect
71
+ end
72
+
73
+ def start(messages = 100)
74
+ messages.to_i.times do
75
+ request = "#{@topic}#{rand(100_000).to_s}"
76
+ @socket.send(request)
77
+ response = @socket.recv
78
+ break if @interrupted
79
+ end
80
+ @server.stop
81
+ @ctx.destroy
82
+ end
83
+
84
+ def stop
85
+ @interrupted = true
86
+ end
87
+ private
88
+ def connect
89
+ @socket.connect(@endpoint)
90
+ @socket.linger = 1
91
+ end
92
+ end
93
+
94
+ ctx = ZMQ::Context.new
95
+ client = Client.new(ctx, 'inproc://example.req_rep')
96
+ client.spawn_server
97
+ trap(:INT) do
98
+ client.stop
99
+ end
100
+ client.start(ENV['MESSAGES'] || 1000)
data/ext/czmq.tar.gz ADDED
Binary file
@@ -0,0 +1,280 @@
1
+ #include <rbczmq_ext.h>
2
+
3
+ static VALUE intern_zctx_process;
4
+
5
+ static VALUE rb_czmq_ctx_set_iothreads(VALUE context, VALUE io_threads);
6
+
7
+ static VALUE get_pid()
8
+ {
9
+ rb_secure(2);
10
+ return INT2NUM(getpid());
11
+ }
12
+
13
+ /*
14
+ * :nodoc:
15
+ * Destroy the context while the GIL is released - zctx_destroy also closes it's list of sockets and thus may block
16
+ * depending on socket linger values.
17
+ *
18
+ */
19
+ static VALUE rb_czmq_nogvl_zctx_destroy(void *ptr)
20
+ {
21
+ errno = 0;
22
+ zmq_ctx_wrapper *ctx = ptr;
23
+ zctx_destroy(&ctx->ctx);
24
+ ctx->flags |= ZMQ_CONTEXT_DESTROYED;
25
+ return Qnil;
26
+ }
27
+
28
+ /*
29
+ * :nodoc:
30
+ * Free all resources for a context - invoked by the lower level ZMQ::Context#destroy as well as the GC callback
31
+ *
32
+ */
33
+ static void rb_czmq_free_ctx(zmq_ctx_wrapper *ctx)
34
+ {
35
+ VALUE ctx_map;
36
+ ctx_map = rb_ivar_get(rb_mZmq, intern_zctx_process);
37
+ rb_thread_blocking_region(rb_czmq_nogvl_zctx_destroy, ctx, RUBY_UBF_IO, 0);
38
+ ctx->ctx = NULL;
39
+ rb_hash_aset(ctx_map, get_pid(), Qnil);
40
+ }
41
+
42
+ /*
43
+ * :nodoc:
44
+ * GC free callback
45
+ *
46
+ */
47
+ static void rb_czmq_free_ctx_gc(void *ptr)
48
+ {
49
+ zmq_ctx_wrapper *ctx = (zmq_ctx_wrapper *)ptr;
50
+ if (ctx) {
51
+ if (ctx->ctx != NULL && !(ctx->flags & ZMQ_CONTEXT_DESTROYED)) rb_czmq_free_ctx(ctx);
52
+ xfree(ctx);
53
+ }
54
+ }
55
+
56
+ /*
57
+ * :nodoc:
58
+ * Creates a new context while the GIL is released.
59
+ *
60
+ */
61
+ static VALUE rb_czmq_nogvl_zctx_new(ZMQ_UNUSED void *ptr)
62
+ {
63
+ errno = 0;
64
+ zctx_t *ctx = NULL;
65
+ ctx = zctx_new();
66
+ zctx_set_linger(ctx, 1);
67
+ return (VALUE)ctx;
68
+ }
69
+
70
+ /*
71
+ * call-seq:
72
+ * ZMQ::Context.new => ZMQ::Context
73
+ * ZMQ::Context.new(1) => ZMQ::Context
74
+ *
75
+ * Returns a handle to a new ZMQ context. A single context per process is supported in order to guarantee stability across
76
+ * all Ruby implementations. A context should be passed as an argument to any Ruby threads. Optionally a context can be
77
+ * initialized with an I/O threads value (default: 1) - there should be no need to fiddle with this.
78
+ *
79
+ * === Examples
80
+ * ZMQ::Context.new => ZMQ::Context
81
+ * ZMQ::Context.new(1) => ZMQ::Context
82
+ *
83
+ */
84
+
85
+ static VALUE rb_czmq_ctx_s_new(int argc, VALUE *argv, VALUE context)
86
+ {
87
+ VALUE ctx_map;
88
+ VALUE io_threads;
89
+ zmq_ctx_wrapper *ctx = NULL;
90
+ rb_scan_args(argc, argv, "01", &io_threads);
91
+ ctx_map = rb_ivar_get(rb_mZmq, intern_zctx_process);
92
+ if (!NIL_P(rb_hash_aref(ctx_map, get_pid()))) rb_raise(rb_eZmqError, "single ZMQ context per process allowed");
93
+ context = Data_Make_Struct(rb_cZmqContext, zmq_ctx_wrapper, 0, rb_czmq_free_ctx_gc, ctx);
94
+ ctx->ctx = (zctx_t*)rb_thread_blocking_region(rb_czmq_nogvl_zctx_new, NULL, RUBY_UBF_IO, 0);
95
+ ZmqAssertObjOnAlloc(ctx->ctx, ctx);
96
+ ctx->flags = 0;
97
+ rb_obj_call_init(context, 0, NULL);
98
+ rb_hash_aset(ctx_map, get_pid(), context);
99
+ if (!NIL_P(io_threads)) rb_czmq_ctx_set_iothreads(context, io_threads);
100
+ return context;
101
+ }
102
+
103
+ /*
104
+ * call-seq:
105
+ * ctx.destroy => nil
106
+ *
107
+ * Destroy a ZMQ context and all sockets in it. Useful for manual memory management, otherwise the GC
108
+ * will take the same action if a context object is not reachable anymore on the next GC cycle. This is
109
+ * a lower level API.
110
+ *
111
+ * === Examples
112
+ * ctx = ZMQ::Context.new
113
+ * ctx.destroy => nil
114
+ *
115
+ */
116
+
117
+ static VALUE rb_czmq_ctx_destroy(VALUE obj)
118
+ {
119
+ ZmqGetContext(obj);
120
+ rb_czmq_free_ctx(ctx);
121
+ return Qnil;
122
+ }
123
+
124
+ /*
125
+ * call-seq:
126
+ * ctx.iothreads = 2 => nil
127
+ *
128
+ * Raises default I/O threads from 1 - there should be no need to fiddle with this.
129
+ *
130
+ * === Examples
131
+ * ctx = ZMQ::Context.new
132
+ * ctx.iothreads = 2 => nil
133
+ *
134
+ */
135
+
136
+ static VALUE rb_czmq_ctx_set_iothreads(VALUE obj, VALUE threads)
137
+ {
138
+ int iothreads;
139
+ errno = 0;
140
+ ZmqGetContext(obj);
141
+ Check_Type(threads, T_FIXNUM);
142
+ iothreads = FIX2INT(threads);
143
+ if (iothreads > 1) rb_warn("You probably don't want to spawn more than 1 I/O thread per ZMQ context.");
144
+ if (iothreads < 0) rb_raise(rb_eZmqError, "negative I/O threads count is not supported.");
145
+ zctx_set_iothreads(ctx->ctx, iothreads);
146
+ if (zmq_errno() == EINVAL) ZmqRaiseSysError();
147
+ return Qnil;
148
+ }
149
+
150
+ /*
151
+ * call-seq:
152
+ * ctx.linger = 100 => nil
153
+ *
154
+ * Set msecs to flush sockets when closing them. A high value may block / pause the application on socket close. This
155
+ * binding defaults to a linger value of 1 msec set for all sockets, which is important for the reactor implementation
156
+ * in ZMQ::Loop to avoid stalling the event loop.
157
+ *
158
+ * === Examples
159
+ * ctx = ZMQ::Context.new
160
+ * ctx.linger = 100 => nil
161
+ *
162
+ */
163
+
164
+ static VALUE rb_czmq_ctx_set_linger(VALUE obj, VALUE linger)
165
+ {
166
+ errno = 0;
167
+ int msecs;
168
+ ZmqGetContext(obj);
169
+ Check_Type(linger, T_FIXNUM);
170
+ msecs = FIX2INT(linger);
171
+ if (msecs < 0) rb_raise(rb_eZmqError, "negative linger / timeout values is not supported.");
172
+ zctx_set_linger(ctx->ctx, msecs);
173
+ return Qnil;
174
+ }
175
+
176
+ /*
177
+ * :nodoc:
178
+ * Creates a new socket while the GIL is released.
179
+ *
180
+ */
181
+ VALUE rb_czmq_nogvl_socket_new(void *ptr)
182
+ {
183
+ errno = 0;
184
+ struct nogvl_socket_args *args = ptr;
185
+ return (VALUE)zsocket_new(args->ctx, args->type);
186
+ }
187
+
188
+ /*
189
+ * :nodoc:
190
+ * Maps a Ruby class to a ZMQ socket type.
191
+ *
192
+ */
193
+ static inline VALUE rb_czmq_ctx_socket_klass(int socket_type)
194
+ {
195
+ switch (socket_type) {
196
+ case ZMQ_PUB: return rb_cZmqPubSocket;
197
+ break;
198
+ case ZMQ_SUB: return rb_cZmqSubSocket;
199
+ break;
200
+ case ZMQ_PUSH: return rb_cZmqPushSocket;
201
+ break;
202
+ case ZMQ_PULL: return rb_cZmqPullSocket;
203
+ break;
204
+ case ZMQ_PAIR: return rb_cZmqPairSocket;
205
+ break;
206
+ case ZMQ_REQ: return rb_cZmqReqSocket;
207
+ break;
208
+ case ZMQ_REP: return rb_cZmqRepSocket;
209
+ break;
210
+ case ZMQ_ROUTER: return rb_cZmqRouterSocket;
211
+ break;
212
+ case ZMQ_DEALER: return rb_cZmqDealerSocket;
213
+ break;
214
+ default: rb_raise(rb_eZmqError, "ZMQ socket type %d not supported!", socket_type);
215
+ break;
216
+ }
217
+ }
218
+
219
+ /*
220
+ * call-seq:
221
+ * ctx.socket(:PUSH) => ZMQ::Socket
222
+ * ctx.socket(ZMQ::PUSH) => ZMQ::Socket
223
+ *
224
+ * Creates a socket within this ZMQ context. This is the only API exposed for creating sockets - they're always spawned off
225
+ * a context. Sockets also track state of the current Ruby thread they're created in to ensure they always only ever do work
226
+ * on the thread they were spawned on.
227
+ *
228
+ * === Examples
229
+ * ctx = ZMQ::Context.new
230
+ * ctx.socket(:PUSH) => ZMQ::Socket
231
+ * ctx.socket(ZMQ::PUSH) => ZMQ::Socket
232
+ *
233
+ */
234
+
235
+ static VALUE rb_czmq_ctx_socket(VALUE obj, VALUE type)
236
+ {
237
+ VALUE socket;
238
+ int socket_type;
239
+ struct nogvl_socket_args args;
240
+ zmq_sock_wrapper *sock = NULL;
241
+ errno = 0;
242
+ ZmqGetContext(obj);
243
+ if (TYPE(type) != T_FIXNUM && TYPE(type) != T_SYMBOL) rb_raise(rb_eTypeError, "wrong socket type %s (expected Fixnum or Symbol)", RSTRING_PTR(rb_obj_as_string(type)));
244
+ socket_type = FIX2INT((SYMBOL_P(type)) ? rb_const_get_at(rb_mZmq, rb_to_id(type)) : type);
245
+
246
+ socket = Data_Make_Struct(rb_czmq_ctx_socket_klass(socket_type), zmq_sock_wrapper, rb_czmq_mark_sock, rb_czmq_free_sock_gc, sock);
247
+ args.ctx = ctx->ctx;
248
+ args.type = socket_type;
249
+ sock->socket = (void*)rb_thread_blocking_region(rb_czmq_nogvl_socket_new, (void *)&args, RUBY_UBF_IO, 0);
250
+ ZmqAssertObjOnAlloc(sock->socket, sock);
251
+ #ifndef HAVE_RB_THREAD_BLOCKING_REGION
252
+ sock->str_buffer = zlist_new();
253
+ sock->frame_buffer = zlist_new();
254
+ sock->msg_buffer = zlist_new();
255
+ #endif
256
+ sock->flags = 0;
257
+ sock->ctx = ctx->ctx;
258
+ sock->verbose = FALSE;
259
+ sock->state = ZMQ_SOCKET_PENDING;
260
+ sock->endpoint = Qnil;
261
+ sock->thread = rb_thread_current();
262
+ sock->recv_timeout = ZMQ_SOCKET_DEFAULT_TIMEOUT;
263
+ sock->send_timeout = ZMQ_SOCKET_DEFAULT_TIMEOUT;
264
+ rb_obj_call_init(socket, 0, NULL);
265
+ return socket;
266
+ }
267
+
268
+ void _init_rb_czmq_context()
269
+ {
270
+ intern_zctx_process = rb_intern("@__zmq_ctx_process");
271
+ rb_ivar_set(rb_mZmq, intern_zctx_process, rb_hash_new());
272
+
273
+ rb_cZmqContext = rb_define_class_under(rb_mZmq, "Context", rb_cObject);
274
+
275
+ rb_define_singleton_method(rb_cZmqContext, "new", rb_czmq_ctx_s_new, -1);
276
+ rb_define_method(rb_cZmqContext, "destroy", rb_czmq_ctx_destroy, 0);
277
+ rb_define_method(rb_cZmqContext, "iothreads=", rb_czmq_ctx_set_iothreads, 1);
278
+ rb_define_method(rb_cZmqContext, "linger=", rb_czmq_ctx_set_linger, 1);
279
+ rb_define_method(rb_cZmqContext, "socket", rb_czmq_ctx_socket, 1);
280
+ }