mdp 0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+ }