mdp 0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,39 @@
1
+ #ifndef MAJORDOMO_CLIENT_H
2
+ #define MAJORDOMO_CLIENT_H
3
+
4
+ typedef struct {
5
+ mdp_client_t *client;
6
+ VALUE broker;
7
+ VALUE timeout;
8
+ #ifndef HAVE_RB_THREAD_BLOCKING_REGION
9
+ zlist_t *recv_buffer;
10
+ #endif
11
+ } rb_majordomo_client_t;
12
+
13
+ #define MAJORDOMO_CLIENT_TIMEOUT 2500
14
+
15
+ #define GetMajordomoClient(obj) \
16
+ rb_majordomo_client_t *client = NULL; \
17
+ Data_Get_Struct(obj, rb_majordomo_client_t, client); \
18
+ if (!client) rb_raise(rb_eTypeError, "uninitialized Majordomo client!"); \
19
+ if (!client->client) rb_raise(rb_eRuntimeError, "Majordomo client has already been closed!");
20
+
21
+ struct nogvl_md_client_new_args {
22
+ char *broker;
23
+ int verbose;
24
+ };
25
+
26
+ struct nogvl_md_client_send_args {
27
+ mdp_client_t *client;
28
+ char *service;
29
+ zmsg_t *request;
30
+ };
31
+
32
+ struct nogvl_md_client_recv_args {
33
+ rb_majordomo_client_t *client;
34
+ char *service;
35
+ };
36
+
37
+ void _init_majordomo_client();
38
+
39
+ #endif
@@ -0,0 +1,25 @@
1
+ # encoding: utf-8
2
+
3
+ require 'mkmf'
4
+
5
+ RbConfig::MAKEFILE_CONFIG['CC'] = ENV['CC'] if ENV['CC']
6
+
7
+ def fail(msg)
8
+ STDERR.puts msg
9
+ exit(1)
10
+ end
11
+
12
+ dir_config('majordomo')
13
+
14
+ fail("libmdp header file 'mdp.h' not found") unless have_header('mdp.h')
15
+
16
+ fail("zmq not found") unless have_library('zmq')
17
+ fail("czmq not found") unless have_library('czmq')
18
+ fail("libdmp not found") unless have_library('mdp')
19
+
20
+ have_func('rb_thread_blocking_region')
21
+
22
+ $CFLAGS << ' -Wall -funroll-loops'
23
+ $CFLAGS << ' -Wextra -O0 -ggdb3' if ENV['DEBUG']
24
+
25
+ create_makefile('majordomo_ext')
@@ -0,0 +1,9 @@
1
+ #ifndef MAJORDOMO_JRUBY_H
2
+ #define MAJORDOMO_JRUBY_H
3
+
4
+ #define MajordomoEncode(str) str
5
+
6
+ #define TRAP_BEG
7
+ #define TRAP_END
8
+
9
+ #endif
@@ -0,0 +1,19 @@
1
+ #include "majordomo_ext.h"
2
+
3
+ VALUE rb_mMajordomo;
4
+
5
+ #ifdef HAVE_RUBY_ENCODING_H
6
+ rb_encoding *binary_encoding;
7
+ #endif
8
+
9
+ void Init_majordomo_ext()
10
+ {
11
+ rb_mMajordomo = rb_define_module("Majordomo");
12
+
13
+ #ifdef HAVE_RUBY_ENCODING_H
14
+ binary_encoding = rb_enc_find("binary");
15
+ #endif
16
+
17
+ _init_majordomo_client();
18
+ _init_majordomo_worker();
19
+ }
@@ -0,0 +1,26 @@
1
+ #ifndef MAJORDOMO_EXT_H
2
+ #define MAJORDOMO_EXT_H
3
+
4
+ #include <mdp.h>
5
+ #include "ruby.h"
6
+
7
+ /* Compiler specific */
8
+
9
+ #if defined(__GNUC__) && (__GNUC__ >= 3)
10
+ #define MAJORDOMO_UNUSED __attribute__ ((unused))
11
+ #define MAJORDOMO_NOINLINE __attribute__ ((noinline))
12
+ #else
13
+ #define MAJORDOMO_UNUSED
14
+ #define MAJORDOMO_NOINLINE
15
+ #endif
16
+
17
+ #include "prelude.h"
18
+
19
+ extern VALUE rb_mMajordomo;
20
+ extern VALUE rb_cMajordomoClient;
21
+ extern VALUE rb_cMajordomoWorker;
22
+
23
+ #include "client.h"
24
+ #include "worker.h"
25
+
26
+ #endif
@@ -0,0 +1,23 @@
1
+ #ifndef MAJORDOMO_PRELUDE_H
2
+ #define MAJORDOMO_PRELUDE_H
3
+ #include <stdint.h>
4
+
5
+ #ifndef RFLOAT_VALUE
6
+ #define RFLOAT_VALUE(v) (RFLOAT(v)->value)
7
+ #endif
8
+
9
+ #ifdef RUBINIUS
10
+ #include "rubinius.h"
11
+ #else
12
+ #ifdef JRUBY
13
+ #include "jruby.h"
14
+ #else
15
+ #ifdef HAVE_RB_THREAD_BLOCKING_REGION
16
+ #include "ruby19.h"
17
+ #else
18
+ #include "ruby18.h"
19
+ #endif
20
+ #endif
21
+ #endif
22
+
23
+ #endif
@@ -0,0 +1,19 @@
1
+ #ifndef MAJORDOMO_RUBINIUS_H
2
+ #define MAJORDOMO_RUBINIUS_H
3
+
4
+ #define RSTRING_NOT_MODIFIED
5
+
6
+ #ifdef HAVE_RUBY_ENCODING_H
7
+ #include <ruby/encoding.h>
8
+ extern rb_encoding *binary_encoding;
9
+ #define MajordomorEncode(str) rb_enc_associate(str, binary_encoding)
10
+ #else
11
+ #define MajordomoEncode(str) str
12
+ #endif
13
+
14
+ #define TRAP_BEG
15
+ #define TRAP_END
16
+
17
+ #define FIXNUM_FLAG 0x01
18
+
19
+ #endif
@@ -0,0 +1,38 @@
1
+ #ifndef MAJORDOMO_RUBY18_H
2
+ #define MAJORDOMO_RUBY18_H
3
+
4
+ #define MajordomoEncode(str) str
5
+
6
+ #ifndef RSTRING_PTR
7
+ #define RSTRING_PTR(str) RSTRING(str)->ptr
8
+ #endif
9
+ #ifndef RSTRING_LEN
10
+ #define RSTRING_LEN(s) (RSTRING(s)->len)
11
+ #endif
12
+
13
+ #include "rubyio.h"
14
+ #include "rubysig.h"
15
+
16
+ /*
17
+ * partial emulation of the 1.9 rb_thread_blocking_region under 1.8,
18
+ * this is enough for dealing with blocking I/O functions in the
19
+ * presence of threads.
20
+ */
21
+
22
+ #define RUBY_UBF_IO ((rb_unblock_function_t *)-1)
23
+ typedef void rb_unblock_function_t(void *);
24
+ typedef VALUE rb_blocking_function_t(void *);
25
+ static VALUE
26
+ rb_thread_blocking_region(
27
+ rb_blocking_function_t *func, void *data1,
28
+ MAJORDOMO_UNUSED rb_unblock_function_t *ubf,
29
+ MAJORDOMO_UNUSED void *data2)
30
+ {
31
+ VALUE rv;
32
+ TRAP_BEG;
33
+ rv = func(data1);
34
+ TRAP_END;
35
+ return rv;
36
+ }
37
+
38
+ #endif
@@ -0,0 +1,13 @@
1
+ #ifndef MAJORDOMO_RUBY19_H
2
+ #define MAJORDOMO_RUBY19_H
3
+
4
+ #include <ruby/encoding.h>
5
+ #include <ruby/io.h>
6
+
7
+ extern rb_encoding *binary_encoding;
8
+ #define MajordomoEncode(str) rb_enc_associate(str, binary_encoding)
9
+
10
+ #define TRAP_BEG
11
+ #define TRAP_END
12
+
13
+ #endif
@@ -0,0 +1,366 @@
1
+ #include "majordomo_ext.h"
2
+
3
+ VALUE rb_cMajordomoWorker;
4
+
5
+ /*
6
+ * :nodoc:
7
+ * GC mark callback
8
+ *
9
+ */
10
+ static void rb_mark_majordomo_worker(void *ptr)
11
+ {
12
+ rb_majordomo_worker_t *worker = (rb_majordomo_worker_t *)ptr;
13
+ if (worker) {
14
+ rb_gc_mark(worker->broker);
15
+ rb_gc_mark(worker->service);
16
+ rb_gc_mark(worker->heartbeat);
17
+ rb_gc_mark(worker->reconnect);
18
+ }
19
+ }
20
+
21
+ /*
22
+ * :nodoc:
23
+ * Release the GIL when closing a Majordomo worker
24
+ *
25
+ */
26
+ static VALUE rb_nogvl_mdp_worker_close(void *ptr)
27
+ {
28
+ mdp_worker_t *worker = ptr;
29
+ mdp_worker_destroy(&worker);
30
+ return Qnil;
31
+ }
32
+
33
+ /*
34
+ * :nodoc:
35
+ * GC free callback
36
+ *
37
+ */
38
+ static void rb_free_majordomo_worker(void *ptr)
39
+ {
40
+ rb_majordomo_worker_t *worker = (rb_majordomo_worker_t *)ptr;
41
+ if (worker) {
42
+ if (worker->worker) rb_thread_blocking_region(rb_nogvl_mdp_worker_close, (void *)worker->worker, RUBY_UBF_IO, 0);
43
+ #ifndef HAVE_RB_THREAD_BLOCKING_REGION
44
+ zlist_destroy(&(worker->recv_buffer));
45
+ #endif
46
+ xfree(worker);
47
+ worker = NULL;
48
+ }
49
+ }
50
+
51
+ /*
52
+ * :nodoc:
53
+ * Release the GIL when creating a new Majordomo worker
54
+ *
55
+ */
56
+ static VALUE rb_nogvl_mdp_worker_new(void *ptr)
57
+ {
58
+ struct nogvl_md_worker_new_args *args = ptr;
59
+ return (VALUE)mdp_worker_new(args->broker, args->service, args->verbose);
60
+ }
61
+
62
+ /*
63
+ * call-seq:
64
+ * Majordomo::Worker.new("tcp://0.0.0.0:5555", "service") => Majordomo::Worker
65
+ * Majordomo::Worker.new("tcp://0.0.0.0:5555", "service", true) => Majordomo::Worker
66
+ *
67
+ * Creates a new Majordomo::Worker instance. A broker URI and service identifier is required and an
68
+ * optional verbose flag can be passed to the initializer.
69
+ *
70
+ * === Examples
71
+ * wk = Majordomo::Worker.new("tcp://0.0.0.0:5555", "service") => Majordomo::Worker
72
+ * wk.broker => "tcp://0.0.0.0:5555"
73
+ * wk.heartbeat => 2500
74
+ * wk.recv => "request"
75
+ *
76
+ */
77
+ static VALUE rb_majordomo_worker_s_new(int argc, VALUE *argv, VALUE klass)
78
+ {
79
+ rb_majordomo_worker_t *worker = NULL;
80
+ struct nogvl_md_worker_new_args args;
81
+ VALUE obj, broker, service, verbose;
82
+ rb_scan_args(argc, argv, "21", &broker, &service, &verbose);
83
+ if (verbose == Qnil)
84
+ verbose = Qfalse;
85
+ Check_Type(broker, T_STRING);
86
+ Check_Type(service, T_STRING);
87
+ obj = Data_Make_Struct(klass, rb_majordomo_worker_t, rb_mark_majordomo_worker, rb_free_majordomo_worker, worker);
88
+
89
+ args.broker = RSTRING_PTR(broker);
90
+ args.service = RSTRING_PTR(service);
91
+ args.verbose = (verbose == Qtrue ? 1 : 0);
92
+ worker->worker = (mdp_worker_t *)rb_thread_blocking_region(rb_nogvl_mdp_worker_new, (void *)&args, RUBY_UBF_IO, 0);
93
+ worker->broker = rb_str_new4(broker);
94
+ worker->service = rb_str_new4(service);
95
+ worker->heartbeat = INT2NUM(MAJORDOMO_WORKER_HEARTBEAT);
96
+ worker->reconnect = INT2NUM(MAJORDOMO_WORKER_RECONNECT);
97
+ #ifndef HAVE_RB_THREAD_BLOCKING_REGION
98
+ worker->recv_buffer = zlist_new();
99
+ #endif
100
+ rb_obj_call_init(obj, 0, NULL);
101
+ return obj;
102
+ }
103
+
104
+ /*
105
+ * call-seq:
106
+ * wk.broker => String
107
+ *
108
+ * Returns the URI of the broker this worker is connected to.
109
+ *
110
+ * === Examples
111
+ * wk = Majordomo::Worker.new("tcp://0.0.0.0:5555", "service") => Majordomo::Worker
112
+ * wk.broker => "tcp://0.0.0.0:5555"
113
+ *
114
+ */
115
+ static VALUE rb_majordomo_worker_broker(VALUE obj){
116
+ GetMajordomoWorker(obj);
117
+ return worker->broker;
118
+ }
119
+
120
+ /*
121
+ * call-seq:
122
+ * wk.service => String
123
+ *
124
+ * Returns the service identifier this worker implements.
125
+ *
126
+ * === Examples
127
+ * wk = Majordomo::Worker.new("tcp://0.0.0.0:5555", "service") => Majordomo::Worker
128
+ * wk.service => "service"
129
+ *
130
+ */
131
+ static VALUE rb_majordomo_worker_service(VALUE obj){
132
+ GetMajordomoWorker(obj);
133
+ return worker->service;
134
+ }
135
+
136
+ /*
137
+ * call-seq:
138
+ * wk.heartbeat => Fixnum
139
+ *
140
+ * Returns the worker heartbeat delay (in msecs).
141
+ *
142
+ * === Examples
143
+ * wk = Majordomo::Worker.new("tcp://0.0.0.0:5555", "service") => Majordomo::Worker
144
+ * wk.heartbeat => 2500
145
+ *
146
+ */
147
+ static VALUE rb_majordomo_worker_heartbeat(VALUE obj){
148
+ GetMajordomoWorker(obj);
149
+ return worker->heartbeat;
150
+ }
151
+
152
+ /*
153
+ * call-seq:
154
+ * wk.reconnect => Fixnum
155
+ *
156
+ * Returns the worker reconnect delay (in msecs).
157
+ *
158
+ * === Examples
159
+ * wk = Majordomo::Worker.new("tcp://0.0.0.0:5555", "service") => Majordomo::Worker
160
+ * wk.reconnect => 2500
161
+ *
162
+ */
163
+ static VALUE rb_majordomo_worker_reconnect(VALUE obj){
164
+ GetMajordomoWorker(obj);
165
+ return worker->reconnect;
166
+ }
167
+
168
+ /*
169
+ * call-seq:
170
+ * wk.heartbeat = val => nil
171
+ *
172
+ * Sets the worker heartbeat delay (in msecs).
173
+ *
174
+ * === Examples
175
+ * wk = Majordomo::Worker.new("tcp://0.0.0.0:5555", "service") => Majordomo::Worker
176
+ * wk.heartbeat = 100 => nil
177
+ * wk.heartbeat => 100
178
+ *
179
+ */
180
+ static VALUE rb_majordomo_worker_heartbeat_equals(VALUE obj, VALUE heartbeat){
181
+ GetMajordomoWorker(obj);
182
+ Check_Type(heartbeat, T_FIXNUM);
183
+ mdp_worker_set_heartbeat(worker->worker, FIX2INT(heartbeat));
184
+ worker->heartbeat = heartbeat;
185
+ return Qnil;
186
+ }
187
+
188
+ /*
189
+ * call-seq:
190
+ * wk.reconnect = 100 => nil
191
+ *
192
+ * Sets the worker reconnect delay (in msecs).
193
+ *
194
+ * === Examples
195
+ * wk = Majordomo::Worker.new("tcp://0.0.0.0:5555", "service") => Majordomo::Worker
196
+ * wk.reconnect = 100 => nil
197
+ * wk.reconnect => 100
198
+ *
199
+ */
200
+ static VALUE rb_majordomo_worker_reconnect_equals(VALUE obj, VALUE reconnect){
201
+ GetMajordomoWorker(obj);
202
+ Check_Type(reconnect, T_FIXNUM);
203
+ mdp_worker_set_reconnect(worker->worker, FIX2INT(reconnect));
204
+ worker->reconnect = reconnect;
205
+ return Qnil;
206
+ }
207
+
208
+ /*
209
+ * :nodoc:
210
+ * Release the GIL when receiving a worker message
211
+ *
212
+ */
213
+ static VALUE rb_nogvl_mdp_worker_recv(void *ptr)
214
+ {
215
+ struct nogvl_md_worker_recv_args *args = ptr;
216
+ rb_majordomo_worker_t *worker = args->worker;
217
+ #ifdef HAVE_RB_THREAD_BLOCKING_REGION
218
+ return (VALUE)mdp_worker_recv(worker->worker, &args->reply);
219
+ #else
220
+ uint32_t events;
221
+ size_t evopt_len = sizeof (uint32_t);
222
+ int fd;
223
+ size_t fdopt_len = sizeof (int);
224
+ if (zlist_size(worker->recv_buffer) != 0)
225
+ return (VALUE)zlist_pop(worker->recv_buffer);
226
+ try_readable:
227
+ mdp_worker_getsockopt (worker->worker, ZMQ_EVENTS, &events, &evopt_len);
228
+ if ((events & ZMQ_POLLIN) == ZMQ_POLLIN) {
229
+ do {
230
+ zlist_append(worker->recv_buffer, mdp_worker_recv(worker->worker, &args->reply));
231
+ } while (zmq_errno() != EAGAIN && zmq_errno() != EINTR);
232
+ return (VALUE)zlist_pop(worker->recv_buffer);
233
+ } else {
234
+ mdp_worker_getsockopt (worker->worker, ZMQ_FD, &fd, &fdopt_len);
235
+ rb_thread_wait_fd(fd);
236
+ goto try_readable;
237
+ }
238
+ #endif
239
+ }
240
+
241
+ /*
242
+ * call-seq:
243
+ * wk.recv => String or nil
244
+ *
245
+ * Receives a client request form the broker. Valid requests are of type String and NilClass
246
+ *
247
+ * === Examples
248
+ * wk = Majordomo::Worker.new("tcp://0.0.0.0:5555", "service") => Majordomo::Worker
249
+ * wk.recv => ["request", "reply"]
250
+ *
251
+ */
252
+ static VALUE rb_majordomo_worker_recv(VALUE obj){
253
+ VALUE req, reply;
254
+ struct nogvl_md_worker_recv_args args;
255
+ GetMajordomoWorker(obj);
256
+ args.worker = worker;
257
+ args.reply = NULL;
258
+ zmsg_t *request = (zmsg_t *)rb_thread_blocking_region(rb_nogvl_mdp_worker_recv, (void *)&args, RUBY_UBF_IO, 0);
259
+ if (!request)
260
+ return Qnil;
261
+ req = MajordomoEncode(rb_str_new2(zmsg_popstr(request)));
262
+ zmsg_destroy(&request);
263
+ reply = rb_str_new(zframe_data(args.reply), zframe_size(args.reply));
264
+ zframe_destroy(&args.reply);
265
+ return rb_ary_new3(2, req, reply);
266
+ }
267
+
268
+ /*
269
+ * :nodoc:
270
+ * Release the GIL when sending a worker message
271
+ *
272
+ */
273
+ static VALUE rb_nogvl_mdp_worker_send(void *ptr)
274
+ {
275
+ struct nogvl_md_worker_send_args *args = ptr;
276
+ #ifdef HAVE_RB_THREAD_BLOCKING_REGION
277
+ mdp_worker_send(args->worker, &args->progress, args->reply_to);
278
+ #else
279
+ uint32_t events;
280
+ size_t evopt_len = sizeof (uint32_t);
281
+ int fd;
282
+ size_t fdopt_len = sizeof (int);
283
+ if (rb_thread_alone()) {
284
+ mdp_worker_send(args->worker, &args->progress, args->reply_to);
285
+ return Qnil;
286
+ }
287
+ try_writable:
288
+ mdp_worker_getsockopt (args->worker, ZMQ_EVENTS, &events, &evopt_len);
289
+ if ((events & ZMQ_POLLOUT) == ZMQ_POLLOUT) {
290
+ mdp_worker_send(args->worker, &args->progress, args->reply_to);
291
+ } else {
292
+ mdp_worker_getsockopt (args->worker, ZMQ_FD, &fd, &fdopt_len);
293
+ rb_thread_wait_fd(fd);
294
+ goto try_writable;
295
+ }
296
+ #endif
297
+ return Qnil;
298
+ }
299
+
300
+ /*
301
+ * call-seq:
302
+ * wk.send(message, reply_to) => boolean
303
+ *
304
+ * Send a reply to a client request. Returns true if the send was succfessful.
305
+ *
306
+ * === Examples
307
+ * wk = Majordomo::Worker.new("tcp://0.0.0.0:5555", "service") => Majordomo::Worker
308
+ * req, reply_to = wk.recv => ["request", "reply"]
309
+ * wk.send("reply", reply_to) => true
310
+ *
311
+ */
312
+ static VALUE rb_majordomo_worker_send(VALUE obj, VALUE message, VALUE reply_to){
313
+ struct nogvl_md_worker_send_args args;
314
+ GetMajordomoWorker(obj);
315
+ args.worker = worker->worker;
316
+ args.progress = zmsg_new();
317
+ if (!args.progress)
318
+ return Qfalse;
319
+ if (zmsg_pushmem(args.progress, RSTRING_PTR(message), RSTRING_LEN(message)) == -1) {
320
+ zmsg_destroy(&args.progress);
321
+ return Qfalse;
322
+ }
323
+ args.reply_to = zframe_new(RSTRING_PTR(reply_to), RSTRING_LEN(reply_to));
324
+ if (!args.reply_to) {
325
+ zmsg_destroy(&args.progress);
326
+ return Qfalse;
327
+ }
328
+ rb_thread_blocking_region(rb_nogvl_mdp_worker_send, (void *)&args, RUBY_UBF_IO, 0);
329
+ zframe_destroy(&args.reply_to);
330
+ return Qtrue;
331
+ }
332
+
333
+ /*
334
+ * call-seq:
335
+ * wk.close => nil
336
+ *
337
+ * Close the worker connection to the broker.
338
+ *
339
+ * === Examples
340
+ * wk = Majordomo::Worker.new("tcp://0.0.0.0:5555", "service") => Majordomo::Worker
341
+ * wk.close => nil
342
+ *
343
+ */
344
+ static VALUE rb_majordomo_worker_close(VALUE obj){
345
+ VALUE ret;
346
+ GetMajordomoWorker(obj);
347
+ ret = rb_thread_blocking_region(rb_nogvl_mdp_worker_close, (void *)worker->worker, RUBY_UBF_IO, 0);
348
+ worker->worker = NULL;
349
+ return ret;
350
+ }
351
+
352
+ void _init_majordomo_worker()
353
+ {
354
+ rb_cMajordomoWorker = rb_define_class_under(rb_mMajordomo, "Worker", rb_cObject);
355
+
356
+ rb_define_singleton_method(rb_cMajordomoWorker, "new", rb_majordomo_worker_s_new, -1);
357
+ rb_define_method(rb_cMajordomoWorker, "broker", rb_majordomo_worker_broker, 0);
358
+ rb_define_method(rb_cMajordomoWorker, "service", rb_majordomo_worker_service, 0);
359
+ rb_define_method(rb_cMajordomoWorker, "heartbeat", rb_majordomo_worker_heartbeat, 0);
360
+ rb_define_method(rb_cMajordomoWorker, "reconnect", rb_majordomo_worker_reconnect, 0);
361
+ rb_define_method(rb_cMajordomoWorker, "heartbeat=", rb_majordomo_worker_heartbeat_equals, 1);
362
+ rb_define_method(rb_cMajordomoWorker, "reconnect=", rb_majordomo_worker_reconnect_equals, 1);
363
+ rb_define_method(rb_cMajordomoWorker, "recv", rb_majordomo_worker_recv, 0);
364
+ rb_define_method(rb_cMajordomoWorker, "send", rb_majordomo_worker_send, 2);
365
+ rb_define_method(rb_cMajordomoWorker, "close", rb_majordomo_worker_close, 0);
366
+ }