posix_mq 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,813 @@
1
+ #define _XOPEN_SOURCE 600
2
+ #include <ruby.h>
3
+
4
+ #include <time.h>
5
+ #include <mqueue.h>
6
+ #include <fcntl.h>
7
+ #include <sys/stat.h>
8
+ #include <errno.h>
9
+ #include <assert.h>
10
+ #include <unistd.h>
11
+
12
+ #if defined(__linux__)
13
+ # define MQD_IS_FD 1
14
+ # define MQ_IO_MARK(mq) rb_gc_mark((mq)->io)
15
+ # define MQ_IO_SET(mq,val) do { (mq)->io = (val); } while (0)
16
+ #else
17
+ # warning mqd_t is not select()-able on your OS
18
+ # define MQD_IS_FD 0
19
+ # define MQ_IO_MARK(mq) ((void)(0))
20
+ # define MQ_IO_SET(mq,val) ((void)(0))
21
+ #endif /* non-Linux */
22
+
23
+ struct posix_mq {
24
+ mqd_t des;
25
+ long msgsize;
26
+ VALUE name;
27
+ #if MQD_IS_FD
28
+ VALUE io;
29
+ #endif
30
+ };
31
+
32
+ static VALUE cPOSIX_MQ, cAttr;
33
+ static ID id_new;
34
+ static ID sym_r, sym_w, sym_rw;
35
+ static const mqd_t MQD_INVALID = (mqd_t)-1;
36
+
37
+ /* Ruby 1.8.6+ macros (for compatibility with Ruby 1.9) */
38
+ #ifndef RSTRING_PTR
39
+ # define RSTRING_PTR(s) (RSTRING(s)->ptr)
40
+ #endif
41
+ #ifndef RSTRING_LEN
42
+ # define RSTRING_LEN(s) (RSTRING(s)->len)
43
+ #endif
44
+ #ifndef RSTRUCT_PTR
45
+ # define RSTRUCT_PTR(s) (RSTRUCT(s)->ptr)
46
+ #endif
47
+ #ifndef RSTRUCT_LEN
48
+ # define RSTRUCT_LEN(s) (RSTRUCT(s)->len)
49
+ #endif
50
+
51
+ #ifndef HAVE_RB_STR_SET_LEN
52
+ # ifdef RUBINIUS
53
+ # define rb_str_set_len(str,len) rb_str_resize(str,len)
54
+ # else /* 1.8.6 optimized version */
55
+ /* this is taken from Ruby 1.8.7, 1.8.6 may not have it */
56
+ static void rb_18_str_set_len(VALUE str, long len)
57
+ {
58
+ RSTRING(str)->len = len;
59
+ RSTRING(str)->ptr[len] = '\0';
60
+ rb_str_flush(str);
61
+ }
62
+ # define rb_str_set_len(str,len) rb_18_str_set_len(str,len)
63
+ # endif /* ! RUBINIUS */
64
+ #endif /* !defined(HAVE_RB_STR_SET_LEN) */
65
+
66
+ #ifndef HAVE_RB_STRUCT_ALLOC_NOINIT
67
+ static VALUE rb_struct_alloc_noinit(VALUE class)
68
+ {
69
+ return rb_funcall(class, id_new, 0, 0);
70
+ }
71
+ #endif /* !defined(HAVE_RB_STRUCT_ALLOC_NOINIT) */
72
+
73
+ /* partial emulation of the 1.9 rb_thread_blocking_region under 1.8 */
74
+ #ifndef HAVE_RB_THREAD_BLOCKING_REGION
75
+ # include <rubysig.h>
76
+ # define RUBY_UBF_IO ((rb_unblock_function_t *)-1)
77
+ typedef void rb_unblock_function_t(void *);
78
+ typedef VALUE rb_blocking_function_t(void *);
79
+ static VALUE
80
+ rb_thread_blocking_region(
81
+ rb_blocking_function_t *func, void *data1,
82
+ rb_unblock_function_t *ubf, void *data2)
83
+ {
84
+ VALUE rv;
85
+
86
+ assert(RUBY_UBF_IO == ubf && "RUBY_UBF_IO required for emulation");
87
+
88
+ TRAP_BEG;
89
+ rv = func(data1);
90
+ TRAP_END;
91
+
92
+ return rv;
93
+ }
94
+ #endif /* ! HAVE_RB_THREAD_BLOCKING_REGION */
95
+
96
+ /* used to pass arguments to mq_open inside blocking region */
97
+ struct open_args {
98
+ int argc;
99
+ const char *name;
100
+ int oflags;
101
+ mode_t mode;
102
+ struct mq_attr attr;
103
+ };
104
+
105
+ /* used to pass arguments to mq_send/mq_receive inside blocking region */
106
+ struct rw_args {
107
+ mqd_t des;
108
+ char *msg_ptr;
109
+ size_t msg_len;
110
+ unsigned msg_prio;
111
+ struct timespec *timeout;
112
+ };
113
+
114
+ /* hope it's there..., TODO: a better version that works in rbx */
115
+ struct timeval rb_time_interval(VALUE);
116
+
117
+ static struct timespec *convert_timeout(struct timespec *dest, VALUE time)
118
+ {
119
+ struct timeval tv, now;
120
+
121
+ if (NIL_P(time))
122
+ return NULL;
123
+
124
+ tv = rb_time_interval(time); /* aggregate return :( */
125
+ gettimeofday(&now, NULL);
126
+ dest->tv_sec = now.tv_sec + tv.tv_sec;
127
+ dest->tv_nsec = (now.tv_usec + tv.tv_usec) * 1000;
128
+
129
+ if (dest->tv_nsec > 1000000000) {
130
+ dest->tv_nsec -= 1000000000;
131
+ dest->tv_sec++;
132
+ }
133
+
134
+ return dest;
135
+ }
136
+
137
+ /* runs without GVL */
138
+ static VALUE xopen(void *ptr)
139
+ {
140
+ struct open_args *x = ptr;
141
+ mqd_t rv;
142
+
143
+ switch (x->argc) {
144
+ case 2: rv = mq_open(x->name, x->oflags); break;
145
+ case 3: rv = mq_open(x->name, x->oflags, x->mode, NULL); break;
146
+ case 4: rv = mq_open(x->name, x->oflags, x->mode, &x->attr); break;
147
+ default: rv = MQD_INVALID;
148
+ }
149
+
150
+ return (VALUE)rv;
151
+ }
152
+
153
+ /* runs without GVL */
154
+ static VALUE xsend(void *ptr)
155
+ {
156
+ struct rw_args *x = ptr;
157
+
158
+ if (x->timeout)
159
+ return (VALUE)mq_timedsend(x->des, x->msg_ptr, x->msg_len,
160
+ x->msg_prio, x->timeout);
161
+
162
+ return (VALUE)mq_send(x->des, x->msg_ptr, x->msg_len, x->msg_prio);
163
+ }
164
+
165
+ /* runs without GVL */
166
+ static VALUE xrecv(void *ptr)
167
+ {
168
+ struct rw_args *x = ptr;
169
+
170
+ if (x->timeout)
171
+ return (VALUE)mq_timedreceive(x->des, x->msg_ptr, x->msg_len,
172
+ &x->msg_prio, x->timeout);
173
+
174
+ return (VALUE)mq_receive(x->des, x->msg_ptr, x->msg_len, &x->msg_prio);
175
+ }
176
+
177
+ /* runs without GVL, path resolution may be slow */
178
+ static VALUE xunlink(void *ptr)
179
+ {
180
+ VALUE name = (VALUE)ptr;
181
+
182
+ return (VALUE)mq_unlink(RSTRING_PTR(name));
183
+ }
184
+
185
+ /* called by GC */
186
+ static void mark(void *ptr)
187
+ {
188
+ struct posix_mq *mq = ptr;
189
+
190
+ rb_gc_mark(mq->name);
191
+ MQ_IO_MARK(mq);
192
+ }
193
+
194
+ /* called by GC */
195
+ static void _free(void *ptr)
196
+ {
197
+ struct posix_mq *mq = ptr;
198
+
199
+ if (mq->des != MQD_INVALID) {
200
+ /* we ignore errors when gc-ing */
201
+ int saved_errno = errno;
202
+
203
+ mq_close(mq->des);
204
+ errno = saved_errno;
205
+ mq->des = MQD_INVALID;
206
+ }
207
+ }
208
+
209
+ /* automatically called at creation (before initialize) */
210
+ static VALUE alloc(VALUE klass)
211
+ {
212
+ struct posix_mq *mq;
213
+ VALUE rv = Data_Make_Struct(klass, struct posix_mq, mark, _free, mq);
214
+
215
+ mq->des = MQD_INVALID;
216
+ mq->msgsize = -1;
217
+ mq->name = Qnil;
218
+ MQ_IO_SET(mq, Qnil);
219
+
220
+ return rv;
221
+ }
222
+
223
+ /* unwraps the posix_mq struct from self */
224
+ static struct posix_mq *get(VALUE self, int need_valid)
225
+ {
226
+ struct posix_mq *mq;
227
+
228
+ Data_Get_Struct(self, struct posix_mq, mq);
229
+
230
+ if (need_valid && mq->des == MQD_INVALID)
231
+ rb_raise(rb_eIOError, "closed queue descriptor");
232
+
233
+ return mq;
234
+ }
235
+
236
+ /* converts the POSIX_MQ::Attr astruct into a struct mq_attr attr */
237
+ static void attr_from_struct(struct mq_attr *attr, VALUE astruct, int all)
238
+ {
239
+ VALUE *ptr;
240
+
241
+ if (CLASS_OF(astruct) != cAttr)
242
+ rb_raise(rb_eArgError, "not a POSIX_MQ::Attr: %s",
243
+ RSTRING_PTR(rb_inspect(astruct)));
244
+
245
+ ptr = RSTRUCT_PTR(astruct);
246
+
247
+ attr->mq_flags = NUM2LONG(ptr[0]);
248
+
249
+ if (all || !NIL_P(ptr[1]))
250
+ attr->mq_maxmsg = NUM2LONG(ptr[1]);
251
+ if (all || !NIL_P(ptr[2]))
252
+ attr->mq_msgsize = NUM2LONG(ptr[2]);
253
+ if (!NIL_P(ptr[3]))
254
+ attr->mq_curmsgs = NUM2LONG(ptr[3]);
255
+ }
256
+
257
+ /*
258
+ * call-seq:
259
+ * POSIX_MQ.new(name [, flags [, mode [, mq_attr]]) => mq
260
+ *
261
+ * Opens a POSIX message queue given by +name+. +name+ should start
262
+ * with a slash ("/") for portable applications.
263
+ *
264
+ * If a Symbol is given in place of integer +flags+, then:
265
+ *
266
+ * * +:r+ is equivalent to IO::RDONLY
267
+ * * +:w+ is equivalent to IO::CREAT|IO::WRONLY
268
+ * * +:rw+ is equivalent to IO::CREAT|IO::RDWR
269
+ *
270
+ * +mode+ is an integer and only used when IO::CREAT is used.
271
+ * +mq_attr+ is a POSIX_MQ::Attr and only used if IO::CREAT is used.
272
+ * If +mq_attr+ is not specified when creating a queue, then the
273
+ * system defaults will be used.
274
+ *
275
+ * See the manpage for mq_open(3) for more details on this function.
276
+ */
277
+ static VALUE init(int argc, VALUE *argv, VALUE self)
278
+ {
279
+ struct posix_mq *mq = get(self, 0);
280
+ struct open_args x;
281
+ VALUE name, oflags, mode, attr;
282
+
283
+ rb_scan_args(argc, argv, "13", &name, &oflags, &mode, &attr);
284
+
285
+ if (TYPE(name) != T_STRING)
286
+ rb_raise(rb_eArgError, "name must be a string");
287
+
288
+ switch (TYPE(oflags)) {
289
+ case T_NIL:
290
+ x.oflags = O_RDONLY;
291
+ break;
292
+ case T_SYMBOL:
293
+ if (oflags == sym_r)
294
+ x.oflags = O_RDONLY;
295
+ else if (oflags == sym_w)
296
+ x.oflags = O_CREAT|O_WRONLY;
297
+ else if (oflags == sym_rw)
298
+ x.oflags = O_CREAT|O_RDWR;
299
+ else
300
+ rb_raise(rb_eArgError,
301
+ "symbol must be :r, :w, or :rw: %s",
302
+ RSTRING_PTR(rb_inspect(oflags)));
303
+ break;
304
+ case T_BIGNUM:
305
+ case T_FIXNUM:
306
+ x.oflags = NUM2INT(oflags);
307
+ break;
308
+ default:
309
+ rb_raise(rb_eArgError, "flags must be an int, :r, :w, or :wr");
310
+ }
311
+
312
+ x.name = RSTRING_PTR(name);
313
+ x.argc = 2;
314
+
315
+ switch (TYPE(mode)) {
316
+ case T_FIXNUM:
317
+ x.argc = 3;
318
+ x.mode = NUM2INT(mode);
319
+ break;
320
+ case T_NIL:
321
+ if (x.oflags & O_CREAT) {
322
+ x.argc = 3;
323
+ x.mode = 0666;
324
+ }
325
+ break;
326
+ default:
327
+ rb_raise(rb_eArgError, "mode not an integer");
328
+ }
329
+
330
+ switch (TYPE(attr)) {
331
+ case T_STRUCT:
332
+ x.argc = 4;
333
+ attr_from_struct(&x.attr, attr, 1);
334
+
335
+ /* principle of least surprise */
336
+ if (x.attr.mq_flags & O_NONBLOCK)
337
+ x.oflags |= O_NONBLOCK;
338
+ break;
339
+ case T_NIL:
340
+ break;
341
+ default:
342
+ rb_raise(rb_eArgError, "attr must be a POSIX_MQ::Attr: %s",
343
+ RSTRING_PTR(rb_inspect(attr)));
344
+ }
345
+
346
+ mq->des = (mqd_t)rb_thread_blocking_region(xopen, &x, RUBY_UBF_IO, 0);
347
+ if (mq->des == MQD_INVALID)
348
+ rb_sys_fail("mq_open");
349
+
350
+ mq->name = rb_str_dup(name);
351
+
352
+ return self;
353
+ }
354
+
355
+ /*
356
+ * call-seq:
357
+ * POSIX_MQ.unlink(name) => 1
358
+ *
359
+ * Unlinks the message queue given by +name+. The queue will be destroyed
360
+ * when the last process with the queue open closes its queue descriptors.
361
+ */
362
+ static VALUE s_unlink(VALUE self, VALUE name)
363
+ {
364
+ mqd_t rv;
365
+ void *ptr = (void *)name;
366
+
367
+ if (TYPE(name) != T_STRING)
368
+ rb_raise(rb_eArgError, "argument must be a string");
369
+
370
+ rv = (mqd_t)rb_thread_blocking_region(xunlink, ptr, RUBY_UBF_IO, 0);
371
+ if (rv == MQD_INVALID)
372
+ rb_sys_fail("mq_unlink");
373
+
374
+ return INT2NUM(1);
375
+ }
376
+
377
+ /*
378
+ * call-seq:
379
+ * mq.unlink => mq
380
+ *
381
+ * Unlinks the message queue to prevent other processes from accessing it.
382
+ * All existing queue descriptors to this queue including those opened by
383
+ * other processes are unaffected. The queue will only be destroyed
384
+ * when the last process with open descriptors to this queue closes
385
+ * the descriptors.
386
+ */
387
+ static VALUE _unlink(VALUE self)
388
+ {
389
+ struct posix_mq *mq = get(self, 0);
390
+ mqd_t rv;
391
+ void *ptr = (void *)mq->name;
392
+
393
+ assert(TYPE(mq->name) == T_STRING && "mq->name is not a string");
394
+
395
+ rv = (mqd_t)rb_thread_blocking_region(xunlink, ptr, RUBY_UBF_IO, 0);
396
+ if (rv == MQD_INVALID)
397
+ rb_sys_fail("mq_unlink");
398
+
399
+ return self;
400
+ }
401
+
402
+ static void setup_send_buffer(struct rw_args *x, VALUE buffer)
403
+ {
404
+ buffer = rb_obj_as_string(buffer);
405
+ x->msg_ptr = RSTRING_PTR(buffer);
406
+ x->msg_len = (size_t)RSTRING_LEN(buffer);
407
+ }
408
+
409
+ /*
410
+ * call-seq:
411
+ * mq.send(string [,priority[, timeout]]) => nil
412
+ *
413
+ * Inserts the given +string+ into the message queue with an optional,
414
+ * unsigned integer +priority+. If the optional +timeout+ is specified,
415
+ * then Errno::ETIMEDOUT will be raised if the operation cannot complete
416
+ * before +timeout+ seconds has elapsed. Without +timeout+, this method
417
+ * may block until the queue is writable.
418
+ */
419
+ static VALUE _send(int argc, VALUE *argv, VALUE self)
420
+ {
421
+ struct posix_mq *mq = get(self, 1);
422
+ struct rw_args x;
423
+ VALUE buffer, prio, timeout;
424
+ mqd_t rv;
425
+ struct timespec expire;
426
+
427
+ rb_scan_args(argc, argv, "12", &buffer, &prio, &timeout);
428
+
429
+ setup_send_buffer(&x, buffer);
430
+ x.des = mq->des;
431
+ x.timeout = convert_timeout(&expire, timeout);
432
+ x.msg_prio = NIL_P(prio) ? 0 : NUM2UINT(prio);
433
+
434
+ rv = (mqd_t)rb_thread_blocking_region(xsend, &x, RUBY_UBF_IO, 0);
435
+ if (rv == MQD_INVALID)
436
+ rb_sys_fail("mq_send");
437
+
438
+ return Qnil;
439
+ }
440
+
441
+ /*
442
+ * call-seq:
443
+ * mq << string => mq
444
+ *
445
+ * Inserts the given +string+ into the message queue with a
446
+ * default priority of 0 and no timeout.
447
+ */
448
+ static VALUE send0(VALUE self, VALUE buffer)
449
+ {
450
+ struct posix_mq *mq = get(self, 1);
451
+ struct rw_args x;
452
+ mqd_t rv;
453
+
454
+ setup_send_buffer(&x, buffer);
455
+ x.des = mq->des;
456
+ x.timeout = NULL;
457
+ x.msg_prio = 0;
458
+
459
+ rv = (mqd_t)rb_thread_blocking_region(xsend, &x, RUBY_UBF_IO, 0);
460
+ if (rv == MQD_INVALID)
461
+ rb_sys_fail("mq_send");
462
+
463
+ return self;
464
+ }
465
+
466
+ #if MQD_IS_FD
467
+ /*
468
+ * call-seq:
469
+ * mq.to_io => IO
470
+ *
471
+ * Returns an IO.select-able +IO+ object. This method is only available
472
+ * under Linux and is not intended to be portable.
473
+ */
474
+ static VALUE to_io(VALUE self)
475
+ {
476
+ struct posix_mq *mq = get(self, 1);
477
+
478
+ if (NIL_P(mq->io))
479
+ mq->io = rb_funcall(rb_cIO, id_new, 1, INT2NUM(mq->des));
480
+
481
+ return mq->io;
482
+ }
483
+ #endif
484
+
485
+ static void get_msgsize(struct posix_mq *mq)
486
+ {
487
+ struct mq_attr attr;
488
+
489
+ if (mq_getattr(mq->des, &attr) == MQD_INVALID)
490
+ rb_sys_fail("mq_getattr");
491
+
492
+ mq->msgsize = attr.mq_msgsize;
493
+ }
494
+
495
+ /*
496
+ * call-seq:
497
+ * mq.receive([buffer, [timeout]]) => [ message, priority ]
498
+ *
499
+ * Takes the highest priority message off the queue and returns
500
+ * an array containing the message as a String and the Integer
501
+ * priority of the message.
502
+ *
503
+ * If the optional +buffer+ is present, then it must be a String
504
+ * which will receive the data.
505
+ *
506
+ * If the optional +timeout+ is present, then it may be a Float
507
+ * or Integer specifying the timeout in seconds. Errno::ETIMEDOUT
508
+ * will be raised if +timeout+ has elapsed and there are no messages
509
+ * in the queue.
510
+ */
511
+ static VALUE receive(int argc, VALUE *argv, VALUE self)
512
+ {
513
+ struct posix_mq *mq = get(self, 1);
514
+ struct rw_args x;
515
+ VALUE buffer, timeout;
516
+ ssize_t r;
517
+ struct timespec expire;
518
+
519
+ if (mq->msgsize < 0)
520
+ get_msgsize(mq);
521
+
522
+ rb_scan_args(argc, argv, "02", &buffer, &timeout);
523
+ x.timeout = convert_timeout(&expire, timeout);
524
+
525
+ if (NIL_P(buffer)) {
526
+ buffer = rb_str_new(0, mq->msgsize);
527
+ } else {
528
+ StringValue(buffer);
529
+ rb_str_modify(buffer);
530
+ rb_str_resize(buffer, mq->msgsize);
531
+ }
532
+ OBJ_TAINT(buffer);
533
+ x.msg_ptr = RSTRING_PTR(buffer);
534
+ x.msg_len = (size_t)mq->msgsize;
535
+ x.des = mq->des;
536
+
537
+ r = (ssize_t)rb_thread_blocking_region(xrecv, &x, RUBY_UBF_IO, 0);
538
+ if (r < 0)
539
+ rb_sys_fail("mq_receive");
540
+
541
+ rb_str_set_len(buffer, r);
542
+
543
+ return rb_ary_new3(2, buffer, UINT2NUM(x.msg_prio));
544
+ }
545
+
546
+ /*
547
+ * call-seq:
548
+ * mq.attr => mq_attr
549
+ *
550
+ * Returns a POSIX_MQ::Attr struct containing the attributes
551
+ * of the message queue. See the mq_getattr(3) manpage for
552
+ * more details.
553
+ */
554
+ static VALUE getattr(VALUE self)
555
+ {
556
+ struct posix_mq *mq = get(self, 1);
557
+ struct mq_attr attr;
558
+ VALUE astruct;
559
+ VALUE *ptr;
560
+
561
+ if (mq_getattr(mq->des, &attr) == MQD_INVALID)
562
+ rb_sys_fail("mq_getattr");
563
+
564
+ astruct = rb_struct_alloc_noinit(cAttr);
565
+ ptr = RSTRUCT_PTR(astruct);
566
+ ptr[0] = LONG2NUM(attr.mq_flags);
567
+ ptr[1] = LONG2NUM(attr.mq_maxmsg);
568
+ ptr[2] = LONG2NUM(attr.mq_msgsize);
569
+ ptr[3] = LONG2NUM(attr.mq_curmsgs);
570
+
571
+ return astruct;
572
+ }
573
+
574
+ /*
575
+ * call-seq:
576
+ * mq.attr = POSIX_MQ::Attr(IO::NONBLOCK) => mq_attr
577
+ *
578
+ * Only the IO::NONBLOCK flag may be set or unset (zero) in this manner.
579
+ * See the mq_setattr(3) manpage for more details.
580
+ *
581
+ * Consider using the POSIX_MQ#nonblock= method as it is easier and
582
+ * more natural to use.
583
+ */
584
+ static VALUE setattr(VALUE self, VALUE astruct)
585
+ {
586
+ struct posix_mq *mq = get(self, 1);
587
+ struct mq_attr newattr;
588
+
589
+ attr_from_struct(&newattr, astruct, 0);
590
+
591
+ if (mq_setattr(mq->des, &newattr, NULL) == MQD_INVALID)
592
+ rb_sys_fail("mq_setattr");
593
+
594
+ return astruct;
595
+ }
596
+
597
+ /*
598
+ * call-seq:
599
+ * mq.close => nil
600
+ *
601
+ * Closes the underlying message queue descriptor.
602
+ * If this descriptor had a registered notification request, the request
603
+ * will be removed so another descriptor or process may register a
604
+ * notification request. Message queue descriptors are automatically
605
+ * closed by garbage collection.
606
+ */
607
+ static VALUE _close(VALUE self)
608
+ {
609
+ struct posix_mq *mq = get(self, 1);
610
+
611
+ if (mq_close(mq->des) == MQD_INVALID)
612
+ rb_sys_fail("mq_close");
613
+
614
+ mq->des = MQD_INVALID;
615
+ MQ_IO_SET(mq, Qnil);
616
+
617
+ return Qnil;
618
+ }
619
+
620
+ /*
621
+ * call-seq:
622
+ * mq.closed? => true or false
623
+ *
624
+ * Returns +true+ if the message queue descriptor is closed and therefore
625
+ * unusable, otherwise +false+
626
+ */
627
+ static VALUE closed(VALUE self)
628
+ {
629
+ struct posix_mq *mq = get(self, 0);
630
+
631
+ return mq->des == MQD_INVALID ? Qtrue : Qfalse;
632
+ }
633
+
634
+ /*
635
+ * call-seq:
636
+ * mq.name => string
637
+ *
638
+ * Returns the string name of message queue associated with +mq+
639
+ */
640
+ static VALUE name(VALUE self)
641
+ {
642
+ struct posix_mq *mq = get(self, 0);
643
+
644
+ return mq->name;
645
+ }
646
+
647
+ static int lookup_sig(VALUE sig)
648
+ {
649
+ static VALUE list;
650
+ const char *ptr;
651
+ long len;
652
+
653
+ sig = rb_obj_as_string(sig);
654
+ len = RSTRING_LEN(sig);
655
+ ptr = RSTRING_PTR(sig);
656
+
657
+ if (len > 3 && !memcmp("SIG", ptr, 3))
658
+ sig = rb_str_new(ptr + 3, len - 3);
659
+
660
+ if (!list) {
661
+ VALUE mSignal = rb_define_module("Signal"""); /* avoid RDoc */
662
+
663
+ list = rb_funcall(mSignal, rb_intern("list"), 0, 0);
664
+ rb_global_variable(&list);
665
+ }
666
+
667
+ sig = rb_hash_aref(list, sig);
668
+ if (NIL_P(sig))
669
+ rb_raise(rb_eArgError, "invalid signal: %s\n",
670
+ RSTRING_PTR(rb_inspect(sig)));
671
+
672
+ return NUM2INT(sig);
673
+ }
674
+
675
+ /*
676
+ * call-seq:
677
+ * mq.notify = signal => signal
678
+ *
679
+ * Registers the notification request to deliver a given +signal+
680
+ * to the current process when message is received.
681
+ * If +signal+ is +nil+, it will unregister and disable the notification
682
+ * request to allow other processes to register a request.
683
+ * Only one process may have a notification request for a queue
684
+ * at a time, Errno::EBUSY will be raised if there is already
685
+ * a notification request registration for the queue.
686
+ */
687
+ static VALUE setnotify(VALUE self, VALUE arg)
688
+ {
689
+ struct posix_mq *mq = get(self, 1);
690
+ struct sigevent not;
691
+ VALUE rv = arg;
692
+
693
+ not.sigev_notify = SIGEV_SIGNAL;
694
+
695
+ switch (TYPE(arg)) {
696
+ case T_FIXNUM:
697
+ not.sigev_signo = NUM2INT(arg);
698
+ break;
699
+ case T_SYMBOL:
700
+ case T_STRING:
701
+ not.sigev_signo = lookup_sig(arg);
702
+ rv = INT2NUM(not.sigev_signo);
703
+ break;
704
+ case T_NIL:
705
+ not.sigev_notify = SIGEV_NONE;
706
+ break;
707
+ default:
708
+ /* maybe support Proc+thread via sigev_notify_function.. */
709
+ rb_raise(rb_eArgError, "must be a signal or nil");
710
+ }
711
+
712
+ if (mq_notify(mq->des, &not) == MQD_INVALID)
713
+ rb_sys_fail("mq_notify");
714
+
715
+ return rv;
716
+ }
717
+
718
+ /*
719
+ * call-seq:
720
+ * mq.nonblock? => true or false
721
+ *
722
+ * Returns the current non-blocking state of the message queue descriptor.
723
+ */
724
+ static VALUE getnonblock(VALUE self)
725
+ {
726
+ struct mq_attr attr;
727
+ struct posix_mq *mq = get(self, 1);
728
+
729
+ if (mq_getattr(mq->des, &attr) == MQD_INVALID)
730
+ rb_sys_fail("mq_getattr");
731
+
732
+ mq->msgsize = attr.mq_msgsize; /* optimization */
733
+
734
+ return attr.mq_flags & O_NONBLOCK ? Qtrue : Qfalse;
735
+ }
736
+
737
+ /*
738
+ * call-seq:
739
+ * mq.nonblock = boolean => boolean
740
+ *
741
+ * Enables or disables non-blocking operation for the message queue
742
+ * descriptor. Errno::EAGAIN will be raised in situations where
743
+ * the queue would block. This is not compatible with +timeout+
744
+ * arguments to POSIX_MQ#send and POSIX_MQ#receive.
745
+ */
746
+ static VALUE setnonblock(VALUE self, VALUE nb)
747
+ {
748
+ struct mq_attr newattr, oldattr;
749
+ struct posix_mq *mq = get(self, 1);
750
+
751
+ if (nb == Qtrue)
752
+ newattr.mq_flags = O_NONBLOCK;
753
+ else if (nb == Qfalse)
754
+ newattr.mq_flags = 0;
755
+ else
756
+ rb_raise(rb_eArgError, "must be true or false");
757
+
758
+ if (mq_setattr(mq->des, &newattr, &oldattr) == MQD_INVALID)
759
+ rb_sys_fail("mq_setattr");
760
+
761
+ mq->msgsize = oldattr.mq_msgsize; /* optimization */
762
+
763
+ return nb;
764
+ }
765
+
766
+ void Init_posix_mq_ext(void)
767
+ {
768
+ cPOSIX_MQ = rb_define_class("POSIX_MQ", rb_cObject);
769
+ rb_define_alloc_func(cPOSIX_MQ, alloc);
770
+ cAttr = rb_const_get(cPOSIX_MQ, rb_intern("Attr"));
771
+
772
+ /*
773
+ * The maximum number of open message descriptors supported
774
+ * by the system. This may be -1, in which case it is dynamically
775
+ * set at runtime. Consult your operating system documentation
776
+ * for system-specific information about this.
777
+ */
778
+ rb_define_const(cPOSIX_MQ, "OPEN_MAX",
779
+ LONG2NUM(sysconf(_SC_MQ_OPEN_MAX)));
780
+
781
+ /*
782
+ * The maximum priority that may be specified for POSIX_MQ#send
783
+ * On POSIX-compliant systems, this is at least 31, but some
784
+ * systems allow higher limits.
785
+ * The minimum priority is always zero.
786
+ */
787
+ rb_define_const(cPOSIX_MQ, "PRIO_MAX",
788
+ LONG2NUM(sysconf(_SC_MQ_PRIO_MAX)));
789
+
790
+ rb_define_singleton_method(cPOSIX_MQ, "unlink", s_unlink, 1);
791
+
792
+ rb_define_method(cPOSIX_MQ, "initialize", init, -1);
793
+ rb_define_method(cPOSIX_MQ, "send", _send, -1);
794
+ rb_define_method(cPOSIX_MQ, "<<", send0, 1);
795
+ rb_define_method(cPOSIX_MQ, "receive", receive, -1);
796
+ rb_define_method(cPOSIX_MQ, "attr", getattr, 0);
797
+ rb_define_method(cPOSIX_MQ, "attr=", setattr, 1);
798
+ rb_define_method(cPOSIX_MQ, "close", _close, 0);
799
+ rb_define_method(cPOSIX_MQ, "closed?", closed, 0);
800
+ rb_define_method(cPOSIX_MQ, "unlink", _unlink, 0);
801
+ rb_define_method(cPOSIX_MQ, "name", name, 0);
802
+ rb_define_method(cPOSIX_MQ, "notify=", setnotify, 1);
803
+ rb_define_method(cPOSIX_MQ, "nonblock=", setnonblock, 1);
804
+ rb_define_method(cPOSIX_MQ, "nonblock?", getnonblock, 0);
805
+ #if MQD_IS_FD
806
+ rb_define_method(cPOSIX_MQ, "to_io", to_io, 0);
807
+ #endif
808
+
809
+ id_new = rb_intern("new");
810
+ sym_r = ID2SYM(rb_intern("r"));
811
+ sym_w = ID2SYM(rb_intern("w"));
812
+ sym_rw = ID2SYM(rb_intern("rw"));
813
+ }