posix_mq 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/ChangeLog +114 -0
- data/GIT-VERSION-FILE +1 -1
- data/GIT-VERSION-GEN +1 -1
- data/NEWS +22 -0
- data/README +7 -2
- data/ext/posix_mq/extconf.rb +2 -0
- data/ext/posix_mq/posix_mq.c +104 -11
- data/lib/posix_mq.rb +40 -3
- data/test/test_posix_mq.rb +31 -0
- metadata +2 -2
data/ChangeLog
CHANGED
@@ -1,5 +1,119 @@
|
|
1
1
|
ChangeLog from git://git.bogomips.org/ruby_posix_mq.git ()
|
2
2
|
|
3
|
+
commit 2e420820d3b3fb228c810937539f95a618a2c271
|
4
|
+
Author: Eric Wong <normalperson@yhbt.net>
|
5
|
+
Date: Sat Jan 9 22:52:27 2010 +0000
|
6
|
+
|
7
|
+
posix_mq 0.3.0
|
8
|
+
|
9
|
+
This release adds a few new API methods, fixes MRI 1.8.6
|
10
|
+
support. We should now have full feature parity with
|
11
|
+
underlying POSIX message queue C API.
|
12
|
+
|
13
|
+
* POSIX_MQ#notify(&block)
|
14
|
+
RDoc: http://bogomips.org/ruby_posix_mq/POSIX_MQ.html#M000001
|
15
|
+
This is only supported on platforms that implement
|
16
|
+
SIGEV_THREAD with mq_notify(3) (tested with glibc + Linux).
|
17
|
+
Other platforms will have to continue to rely on signal
|
18
|
+
notifications via POSIX#notify=signal, or IO notifications
|
19
|
+
in FreeBSD (and Linux).
|
20
|
+
|
21
|
+
* POSIX_MQ#shift([buffer [,timeout]])
|
22
|
+
Shorthand for the common "POSIX_MQ#receive.first"
|
23
|
+
when you do not care for priority of the received message.
|
24
|
+
|
25
|
+
Rev, EventMachine and Reactor support are planned for
|
26
|
+
Linux, FreeBSD and possibly any other platforms where POSIX
|
27
|
+
message queues are implemented with a file descriptor.
|
28
|
+
|
29
|
+
commit 2c71257b2b95e737088726ffc963b4e72f1b5455
|
30
|
+
Author: Eric Wong <normalperson@yhbt.net>
|
31
|
+
Date: Sat Jan 9 22:49:49 2010 +0000
|
32
|
+
|
33
|
+
MRI 1.8 does not have rb_str_flush
|
34
|
+
|
35
|
+
It's Rubinius-specific and we use rb_str_resize
|
36
|
+
there anyways...
|
37
|
+
|
38
|
+
commit 531106e51e519458d37bed3721da4eff2f163206
|
39
|
+
Author: Eric Wong <normalperson@yhbt.net>
|
40
|
+
Date: Fri Jan 8 11:30:49 2010 -0800
|
41
|
+
|
42
|
+
no point in non-blocking for fd notifications
|
43
|
+
|
44
|
+
It's not needed since the native thread will retry in the
|
45
|
+
unlikely case of EINTR/EAGAIN. And writing one byte to a pipe
|
46
|
+
that's guaranteed by POSIX to be at least 512 bytes is highly
|
47
|
+
unlikely.
|
48
|
+
|
49
|
+
It's also bad because F_SETFL takes the big kernel lock under
|
50
|
+
Linux (and possibly other systems), and doing it unnecessarily
|
51
|
+
is a waste of system cycles.
|
52
|
+
|
53
|
+
commit d03c76ae11ca6294e05262df747e4d43822ada73
|
54
|
+
Author: Eric Wong <normalperson@yhbt.net>
|
55
|
+
Date: Fri Jan 8 11:25:06 2010 -0800
|
56
|
+
|
57
|
+
mode_t is usually unsigned
|
58
|
+
|
59
|
+
Most used open modes are well under INT_MAX, however
|
60
|
+
|
61
|
+
commit b3c31cf444e2ca3dae0f6d2370944bfbf3382d88
|
62
|
+
Author: Eric Wong <normalperson@yhbt.net>
|
63
|
+
Date: Thu Jan 7 01:45:25 2010 -0800
|
64
|
+
|
65
|
+
add POSIX_MQ#shift helper method
|
66
|
+
|
67
|
+
This acts like POSIX_MQ#receive but only returns the message
|
68
|
+
without the priority.
|
69
|
+
|
70
|
+
commit 3700db51399e4949ed314ad0545d037b7762064e
|
71
|
+
Author: Eric Wong <normalperson@yhbt.net>
|
72
|
+
Date: Thu Jan 7 09:28:57 2010 +0000
|
73
|
+
|
74
|
+
POSIX_MQ#notify only works on GNU/Linux for now
|
75
|
+
|
76
|
+
SIGEV_THREAD is not easy to implement, so many platforms
|
77
|
+
do not implement it.
|
78
|
+
|
79
|
+
commit 40d61f55ac53e3cd2f229d0b032da03032e3d53d
|
80
|
+
Author: Eric Wong <normalperson@yhbt.net>
|
81
|
+
Date: Thu Jan 7 00:37:57 2010 -0800
|
82
|
+
|
83
|
+
POSIX_MQ#notify block execution on message received
|
84
|
+
|
85
|
+
This is implementation uses both a short-lived POSIX thread and
|
86
|
+
a pre-spawned Ruby Thread in a manner that works properly under
|
87
|
+
both Ruby 1.8 (green threads) and 1.9 (where Ruby Threads are
|
88
|
+
POSIX threads).
|
89
|
+
|
90
|
+
The short-lived POSIX thread will write a single "\0" byte to
|
91
|
+
a pipe the Ruby Thread waits on. This operation is atomic
|
92
|
+
on all platforms. Once the Ruby Thread is woken up from the
|
93
|
+
pipe, it will execute th block given to it.
|
94
|
+
|
95
|
+
This dual-thread implementation is inspired by the way glibc
|
96
|
+
implements mq_notify(3) + SIGEV_THREAD under Linux where the
|
97
|
+
kernel itself cannot directly spawn POSIX threads.
|
98
|
+
|
99
|
+
commit d8c8fb4155c1feea454abc3ed3f0a4b26e90be68
|
100
|
+
Author: Eric Wong <normalperson@yhbt.net>
|
101
|
+
Date: Sun Jan 3 05:26:00 2010 +0000
|
102
|
+
|
103
|
+
fix warnings on platforms where mqd_t != int
|
104
|
+
|
105
|
+
The POSIX manpages specify the return values of all
|
106
|
+
mq_* functions besides mq_open(3) to be "int", not "mqd_t".
|
107
|
+
|
108
|
+
commit dbe5ed46e07b853e79e44141924a0166016e3e44
|
109
|
+
Author: Eric Wong <normalperson@yhbt.net>
|
110
|
+
Date: Sat Jan 2 23:19:34 2010 -0800
|
111
|
+
|
112
|
+
bump GIT-VERSION-GEN
|
113
|
+
|
114
|
+
Shouldn't affect most people since they should just
|
115
|
+
take code from git...
|
116
|
+
|
3
117
|
commit fd2fcdeee6b44f7854255cb7e01c81db3cd2d99c
|
4
118
|
Author: Eric Wong <normalperson@yhbt.net>
|
5
119
|
Date: Sun Jan 3 05:46:45 2010 +0000
|
data/GIT-VERSION-FILE
CHANGED
@@ -1 +1 @@
|
|
1
|
-
GIT_VERSION = 0.
|
1
|
+
GIT_VERSION = 0.3.0
|
data/GIT-VERSION-GEN
CHANGED
data/NEWS
CHANGED
@@ -1,3 +1,25 @@
|
|
1
|
+
=== 0.3.0 / 2010-01-09 23:11 UTC
|
2
|
+
|
3
|
+
This release adds a few new API methods, fixes MRI 1.8.6
|
4
|
+
support. We should now have full feature parity with
|
5
|
+
underlying POSIX message queue C API.
|
6
|
+
|
7
|
+
* POSIX_MQ#notify(&block)
|
8
|
+
RDoc: http://bogomips.org/ruby_posix_mq/POSIX_MQ.html#M000001
|
9
|
+
This is only supported on platforms that implement
|
10
|
+
SIGEV_THREAD with mq_notify(3) (tested with glibc + Linux).
|
11
|
+
Other platforms will have to continue to rely on signal
|
12
|
+
notifications via POSIX#notify=signal, or IO notifications
|
13
|
+
in FreeBSD (and Linux).
|
14
|
+
|
15
|
+
* POSIX_MQ#shift([buffer [,timeout]])
|
16
|
+
Shorthand for the common "POSIX_MQ#receive.first"
|
17
|
+
when you do not care for priority of the received message.
|
18
|
+
|
19
|
+
Rev, EventMachine and Reactor support are planned for
|
20
|
+
Linux, FreeBSD and possibly any other platforms where POSIX
|
21
|
+
message queues are implemented with a file descriptor.
|
22
|
+
|
1
23
|
=== 0.2.0 / 2010-01-03 05:52 UTC
|
2
24
|
|
3
25
|
This release fixes notification (un)registration and should be
|
data/README
CHANGED
@@ -11,15 +11,20 @@ network-aware message queue implementations.
|
|
11
11
|
|
12
12
|
== Features
|
13
13
|
|
14
|
-
* Supports message notifications via signals
|
14
|
+
* Supports message notifications via signals on all platforms
|
15
15
|
|
16
16
|
* Supports portable non-blocking operation. Under Linux 2.6.6+ and
|
17
17
|
FreeBSD 7.2+, POSIX_MQ objects may even be used with event
|
18
18
|
notification mechanisms such as IO.select.
|
19
19
|
|
20
|
+
* Supports notifications via block execution in a separate thread
|
21
|
+
on platforms that implement SIGEV_THREAD for mq_notify(3),
|
22
|
+
currently only GNU/Linux.
|
23
|
+
|
20
24
|
* Optional timeouts may be applied to send and receive operations.
|
21
25
|
|
22
|
-
* Thread-safe under Ruby 1.9, releases GVL
|
26
|
+
* Thread-safe blocking operations under Ruby 1.9, releases GVL
|
27
|
+
before blocking operations.
|
23
28
|
|
24
29
|
* Documented library API
|
25
30
|
|
data/ext/posix_mq/extconf.rb
CHANGED
@@ -4,9 +4,11 @@ have_header("sys/select.h")
|
|
4
4
|
have_header("signal.h")
|
5
5
|
have_header("mqueue.h") or abort "mqueue.h header missing"
|
6
6
|
have_func("__mq_oshandle")
|
7
|
+
have_header("pthread.h")
|
7
8
|
have_func("rb_str_set_len")
|
8
9
|
have_func("rb_struct_alloc_noinit")
|
9
10
|
have_func('rb_thread_blocking_region')
|
10
11
|
have_library("rt")
|
12
|
+
have_library("pthread")
|
11
13
|
dir_config("posix_mq")
|
12
14
|
create_makefile("posix_mq_ext")
|
data/ext/posix_mq/posix_mq.c
CHANGED
@@ -5,6 +5,9 @@
|
|
5
5
|
#ifdef HAVE_SIGNAL_H
|
6
6
|
# include <signal.h>
|
7
7
|
#endif
|
8
|
+
#ifdef HAVE_PTHREAD_H
|
9
|
+
# include <pthread.h>
|
10
|
+
#endif
|
8
11
|
#include <ruby.h>
|
9
12
|
|
10
13
|
#include <time.h>
|
@@ -34,13 +37,14 @@ struct posix_mq {
|
|
34
37
|
mqd_t des;
|
35
38
|
long msgsize;
|
36
39
|
VALUE name;
|
40
|
+
VALUE thread;
|
37
41
|
#ifdef MQD_TO_FD
|
38
42
|
VALUE io;
|
39
43
|
#endif
|
40
44
|
};
|
41
45
|
|
42
46
|
static VALUE cPOSIX_MQ, cAttr;
|
43
|
-
static ID id_new;
|
47
|
+
static ID id_new, id_kill, id_fileno;
|
44
48
|
static ID sym_r, sym_w, sym_rw;
|
45
49
|
static const mqd_t MQD_INVALID = (mqd_t)-1;
|
46
50
|
|
@@ -67,7 +71,6 @@ static void rb_18_str_set_len(VALUE str, long len)
|
|
67
71
|
{
|
68
72
|
RSTRING(str)->len = len;
|
69
73
|
RSTRING(str)->ptr[len] = '\0';
|
70
|
-
rb_str_flush(str);
|
71
74
|
}
|
72
75
|
# define rb_str_set_len(str,len) rb_18_str_set_len(str,len)
|
73
76
|
# endif /* ! RUBINIUS */
|
@@ -198,6 +201,7 @@ static void mark(void *ptr)
|
|
198
201
|
struct posix_mq *mq = ptr;
|
199
202
|
|
200
203
|
rb_gc_mark(mq->name);
|
204
|
+
rb_gc_mark(mq->thread);
|
201
205
|
MQ_IO_MARK(mq);
|
202
206
|
}
|
203
207
|
|
@@ -225,6 +229,7 @@ static VALUE alloc(VALUE klass)
|
|
225
229
|
mq->des = MQD_INVALID;
|
226
230
|
mq->msgsize = -1;
|
227
231
|
mq->name = Qnil;
|
232
|
+
mq->thread = Qnil;
|
228
233
|
MQ_IO_SET(mq, Qnil);
|
229
234
|
|
230
235
|
return rv;
|
@@ -325,7 +330,7 @@ static VALUE init(int argc, VALUE *argv, VALUE self)
|
|
325
330
|
switch (TYPE(mode)) {
|
326
331
|
case T_FIXNUM:
|
327
332
|
x.argc = 3;
|
328
|
-
x.mode =
|
333
|
+
x.mode = NUM2UINT(mode);
|
329
334
|
break;
|
330
335
|
case T_NIL:
|
331
336
|
if (x.oflags & O_CREAT) {
|
@@ -497,12 +502,14 @@ static void get_msgsize(struct posix_mq *mq)
|
|
497
502
|
{
|
498
503
|
struct mq_attr attr;
|
499
504
|
|
500
|
-
if (mq_getattr(mq->des, &attr)
|
505
|
+
if (mq_getattr(mq->des, &attr) < 0)
|
501
506
|
rb_sys_fail("mq_getattr");
|
502
507
|
|
503
508
|
mq->msgsize = attr.mq_msgsize;
|
504
509
|
}
|
505
510
|
|
511
|
+
static VALUE _receive(int wantarray, int argc, VALUE *argv, VALUE self);
|
512
|
+
|
506
513
|
/*
|
507
514
|
* call-seq:
|
508
515
|
* mq.receive([buffer, [timeout]]) => [ message, priority ]
|
@@ -520,6 +527,31 @@ static void get_msgsize(struct posix_mq *mq)
|
|
520
527
|
* in the queue.
|
521
528
|
*/
|
522
529
|
static VALUE receive(int argc, VALUE *argv, VALUE self)
|
530
|
+
{
|
531
|
+
return _receive(1, argc, argv, self);
|
532
|
+
}
|
533
|
+
|
534
|
+
/*
|
535
|
+
* call-seq:
|
536
|
+
* mq.shift([buffer, [timeout]]) => message
|
537
|
+
*
|
538
|
+
* Takes the highest priority message off the queue and returns
|
539
|
+
* the message as a String.
|
540
|
+
*
|
541
|
+
* If the optional +buffer+ is present, then it must be a String
|
542
|
+
* which will receive the data.
|
543
|
+
*
|
544
|
+
* If the optional +timeout+ is present, then it may be a Float
|
545
|
+
* or Integer specifying the timeout in seconds. Errno::ETIMEDOUT
|
546
|
+
* will be raised if +timeout+ has elapsed and there are no messages
|
547
|
+
* in the queue.
|
548
|
+
*/
|
549
|
+
static VALUE shift(int argc, VALUE *argv, VALUE self)
|
550
|
+
{
|
551
|
+
return _receive(0, argc, argv, self);
|
552
|
+
}
|
553
|
+
|
554
|
+
static VALUE _receive(int wantarray, int argc, VALUE *argv, VALUE self)
|
523
555
|
{
|
524
556
|
struct posix_mq *mq = get(self, 1);
|
525
557
|
struct rw_args x;
|
@@ -551,7 +583,9 @@ static VALUE receive(int argc, VALUE *argv, VALUE self)
|
|
551
583
|
|
552
584
|
rb_str_set_len(buffer, r);
|
553
585
|
|
554
|
-
|
586
|
+
if (wantarray)
|
587
|
+
return rb_ary_new3(2, buffer, UINT2NUM(x.msg_prio));
|
588
|
+
return buffer;
|
555
589
|
}
|
556
590
|
|
557
591
|
/*
|
@@ -569,7 +603,7 @@ static VALUE getattr(VALUE self)
|
|
569
603
|
VALUE astruct;
|
570
604
|
VALUE *ptr;
|
571
605
|
|
572
|
-
if (mq_getattr(mq->des, &attr)
|
606
|
+
if (mq_getattr(mq->des, &attr) < 0)
|
573
607
|
rb_sys_fail("mq_getattr");
|
574
608
|
|
575
609
|
astruct = rb_struct_alloc_noinit(cAttr);
|
@@ -599,7 +633,7 @@ static VALUE setattr(VALUE self, VALUE astruct)
|
|
599
633
|
|
600
634
|
attr_from_struct(&newattr, astruct, 0);
|
601
635
|
|
602
|
-
if (mq_setattr(mq->des, &newattr, NULL)
|
636
|
+
if (mq_setattr(mq->des, &newattr, NULL) < 0)
|
603
637
|
rb_sys_fail("mq_setattr");
|
604
638
|
|
605
639
|
return astruct;
|
@@ -619,7 +653,7 @@ static VALUE _close(VALUE self)
|
|
619
653
|
{
|
620
654
|
struct posix_mq *mq = get(self, 1);
|
621
655
|
|
622
|
-
if (mq_close(mq->des)
|
656
|
+
if (mq_close(mq->des) < 0)
|
623
657
|
rb_sys_fail("mq_close");
|
624
658
|
|
625
659
|
mq->des = MQD_INVALID;
|
@@ -683,6 +717,39 @@ static int lookup_sig(VALUE sig)
|
|
683
717
|
return NUM2INT(sig);
|
684
718
|
}
|
685
719
|
|
720
|
+
/* we spawn a thread just to write ONE byte into an fd (usually a pipe) */
|
721
|
+
static void thread_notify_fd(union sigval sv)
|
722
|
+
{
|
723
|
+
int fd = sv.sival_int;
|
724
|
+
|
725
|
+
while ((write(fd, "", 1) < 0) && (errno == EINTR || errno == EAGAIN));
|
726
|
+
}
|
727
|
+
|
728
|
+
static void setup_notify_io(struct sigevent *not, VALUE io)
|
729
|
+
{
|
730
|
+
VALUE fileno = rb_funcall(io, id_fileno, 0, 0);
|
731
|
+
int fd = NUM2INT(fileno);
|
732
|
+
pthread_attr_t attr;
|
733
|
+
int e;
|
734
|
+
|
735
|
+
if ((e = pthread_attr_init(&attr)))
|
736
|
+
goto err;
|
737
|
+
if ((e = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED)))
|
738
|
+
goto err;
|
739
|
+
#ifdef PTHREAD_STACK_MIN
|
740
|
+
(void)pthread_attr_setstacksize(&attr, PTHREAD_STACK_MIN);
|
741
|
+
#else
|
742
|
+
# warning PTHREAD_STACK_MIN not available,
|
743
|
+
#endif
|
744
|
+
not->sigev_notify = SIGEV_THREAD;
|
745
|
+
not->sigev_notify_function = thread_notify_fd;
|
746
|
+
not->sigev_notify_attributes = &attr;
|
747
|
+
not->sigev_value.sival_int = fd;
|
748
|
+
return;
|
749
|
+
err:
|
750
|
+
rb_raise(rb_eRuntimeError, "pthread failure: %s\n", strerror(e));
|
751
|
+
}
|
752
|
+
|
686
753
|
/*
|
687
754
|
* call-seq:
|
688
755
|
* mq.notify = signal => signal
|
@@ -693,10 +760,16 @@ static int lookup_sig(VALUE sig)
|
|
693
760
|
* request to allow other processes to register a request.
|
694
761
|
* If +signal+ is +false+, it will register a no-op notification request
|
695
762
|
* which will prevent other processes from registering a notification.
|
763
|
+
* If +signal+ is an +IO+ object, it will spawn a thread upon the
|
764
|
+
* arrival of the next message and write one "\\0" byte to the file
|
765
|
+
* descriptor belonging to that IO object.
|
696
766
|
* Only one process may have a notification request for a queue
|
697
767
|
* at a time, Errno::EBUSY will be raised if there is already
|
698
768
|
* a notification request registration for the queue.
|
699
769
|
*
|
770
|
+
* Notifications are only fired once and processes must reregister
|
771
|
+
* for subsequent notifications.
|
772
|
+
*
|
700
773
|
* For readers of the mq_notify(3) manpage, passing +false+
|
701
774
|
* is equivalent to SIGEV_NONE, and passing +nil+ is equivalent
|
702
775
|
* of passing a NULL notification pointer to mq_notify(3).
|
@@ -708,6 +781,10 @@ static VALUE setnotify(VALUE self, VALUE arg)
|
|
708
781
|
struct sigevent * notification = ¬
|
709
782
|
VALUE rv = arg;
|
710
783
|
|
784
|
+
if (!NIL_P(mq->thread)) {
|
785
|
+
rb_funcall(mq->thread, id_kill, 0, 0);
|
786
|
+
mq->thread = Qnil;
|
787
|
+
}
|
711
788
|
not.sigev_notify = SIGEV_SIGNAL;
|
712
789
|
|
713
790
|
switch (TYPE(arg)) {
|
@@ -725,12 +802,15 @@ static VALUE setnotify(VALUE self, VALUE arg)
|
|
725
802
|
not.sigev_signo = lookup_sig(arg);
|
726
803
|
rv = INT2NUM(not.sigev_signo);
|
727
804
|
break;
|
805
|
+
case T_FILE:
|
806
|
+
setup_notify_io(¬, arg);
|
807
|
+
break;
|
728
808
|
default:
|
729
809
|
/* maybe support Proc+thread via sigev_notify_function.. */
|
730
810
|
rb_raise(rb_eArgError, "must be a signal or nil");
|
731
811
|
}
|
732
812
|
|
733
|
-
if (mq_notify(mq->des, notification)
|
813
|
+
if (mq_notify(mq->des, notification) < 0)
|
734
814
|
rb_sys_fail("mq_notify");
|
735
815
|
|
736
816
|
return rv;
|
@@ -747,7 +827,7 @@ static VALUE getnonblock(VALUE self)
|
|
747
827
|
struct mq_attr attr;
|
748
828
|
struct posix_mq *mq = get(self, 1);
|
749
829
|
|
750
|
-
if (mq_getattr(mq->des, &attr)
|
830
|
+
if (mq_getattr(mq->des, &attr) < 0)
|
751
831
|
rb_sys_fail("mq_getattr");
|
752
832
|
|
753
833
|
mq->msgsize = attr.mq_msgsize; /* optimization */
|
@@ -776,7 +856,7 @@ static VALUE setnonblock(VALUE self, VALUE nb)
|
|
776
856
|
else
|
777
857
|
rb_raise(rb_eArgError, "must be true or false");
|
778
858
|
|
779
|
-
if (mq_setattr(mq->des, &newattr, &oldattr)
|
859
|
+
if (mq_setattr(mq->des, &newattr, &oldattr) < 0)
|
780
860
|
rb_sys_fail("mq_setattr");
|
781
861
|
|
782
862
|
mq->msgsize = oldattr.mq_msgsize; /* optimization */
|
@@ -784,6 +864,15 @@ static VALUE setnonblock(VALUE self, VALUE nb)
|
|
784
864
|
return nb;
|
785
865
|
}
|
786
866
|
|
867
|
+
/* :nodoc: */
|
868
|
+
static VALUE setnotifythread(VALUE self, VALUE thread)
|
869
|
+
{
|
870
|
+
struct posix_mq *mq = get(self, 1);
|
871
|
+
|
872
|
+
mq->thread = thread;
|
873
|
+
return thread;
|
874
|
+
}
|
875
|
+
|
787
876
|
void Init_posix_mq_ext(void)
|
788
877
|
{
|
789
878
|
cPOSIX_MQ = rb_define_class("POSIX_MQ", rb_cObject);
|
@@ -814,6 +903,7 @@ void Init_posix_mq_ext(void)
|
|
814
903
|
rb_define_method(cPOSIX_MQ, "send", _send, -1);
|
815
904
|
rb_define_method(cPOSIX_MQ, "<<", send0, 1);
|
816
905
|
rb_define_method(cPOSIX_MQ, "receive", receive, -1);
|
906
|
+
rb_define_method(cPOSIX_MQ, "shift", shift, -1);
|
817
907
|
rb_define_method(cPOSIX_MQ, "attr", getattr, 0);
|
818
908
|
rb_define_method(cPOSIX_MQ, "attr=", setattr, 1);
|
819
909
|
rb_define_method(cPOSIX_MQ, "close", _close, 0);
|
@@ -822,12 +912,15 @@ void Init_posix_mq_ext(void)
|
|
822
912
|
rb_define_method(cPOSIX_MQ, "name", name, 0);
|
823
913
|
rb_define_method(cPOSIX_MQ, "notify=", setnotify, 1);
|
824
914
|
rb_define_method(cPOSIX_MQ, "nonblock=", setnonblock, 1);
|
915
|
+
rb_define_method(cPOSIX_MQ, "notify_thread=", setnotifythread, 1);
|
825
916
|
rb_define_method(cPOSIX_MQ, "nonblock?", getnonblock, 0);
|
826
917
|
#ifdef MQD_TO_FD
|
827
918
|
rb_define_method(cPOSIX_MQ, "to_io", to_io, 0);
|
828
919
|
#endif
|
829
920
|
|
830
921
|
id_new = rb_intern("new");
|
922
|
+
id_kill = rb_intern("kill");
|
923
|
+
id_fileno = rb_intern("fileno");
|
831
924
|
sym_r = ID2SYM(rb_intern("r"));
|
832
925
|
sym_w = ID2SYM(rb_intern("w"));
|
833
926
|
sym_rw = ID2SYM(rb_intern("rw"));
|
data/lib/posix_mq.rb
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
# -*- encoding: binary -*-
|
2
2
|
class POSIX_MQ
|
3
3
|
|
4
|
-
# version of POSIX_MQ, currently 0.
|
5
|
-
VERSION = '0.
|
4
|
+
# version of POSIX_MQ, currently 0.3.0
|
5
|
+
VERSION = '0.3.0'
|
6
6
|
|
7
7
|
# An analogous Struct to "struct mq_attr" in C.
|
8
8
|
# This may be used in arguments for POSIX_MQ.new and
|
@@ -26,9 +26,46 @@ class POSIX_MQ
|
|
26
26
|
mq.close unless mq.closed?
|
27
27
|
end
|
28
28
|
end
|
29
|
-
|
30
29
|
end
|
31
30
|
|
31
|
+
# Executes the given block upon reception of the next message in an
|
32
|
+
# empty queue. If the message queue is not empty, then this block
|
33
|
+
# will only be fired after the queue is emptied and repopulated with
|
34
|
+
# one message.
|
35
|
+
#
|
36
|
+
# This block will only be executed upon the arrival of the
|
37
|
+
# first message and must be reset/reenabled for subsequent
|
38
|
+
# notifications. This block will execute in a separate Ruby
|
39
|
+
# Thread (and thus will safely have the GVL by default).
|
40
|
+
#
|
41
|
+
# This method is only supported on platforms that implement
|
42
|
+
# SIGEV_THREAD functionality in mq_notify(3). So far we only
|
43
|
+
# know of glibc + Linux supporting this. Please let us
|
44
|
+
# know if your platform can support this functionality and
|
45
|
+
# are willing to test for us <ruby.posix.mq@librelist.com>
|
46
|
+
def notify(&block)
|
47
|
+
block.arity == 1 or
|
48
|
+
raise ArgumentError, "arity of notify block must be 1"
|
49
|
+
r, w = IO.pipe
|
50
|
+
thr = Thread.new(r, w, self) do |r, w, mq|
|
51
|
+
begin
|
52
|
+
begin
|
53
|
+
r.read(1) or raise Errno::EINTR
|
54
|
+
rescue Errno::EINTR, Errno::EAGAIN
|
55
|
+
retry
|
56
|
+
end
|
57
|
+
block.call(mq)
|
58
|
+
ensure
|
59
|
+
mq.notify_thread = nil
|
60
|
+
r.close rescue nil
|
61
|
+
w.close rescue nil
|
62
|
+
end
|
63
|
+
end
|
64
|
+
self.notify = w
|
65
|
+
self.notify_thread = thr
|
66
|
+
nil
|
67
|
+
end if RUBY_PLATFORM =~ /linux/
|
68
|
+
|
32
69
|
end
|
33
70
|
|
34
71
|
require 'posix_mq_ext'
|
data/test/test_posix_mq.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# -*- encoding: binary -*-
|
2
2
|
require 'test/unit'
|
3
3
|
require 'posix_mq'
|
4
|
+
require 'thread'
|
4
5
|
require 'fcntl'
|
5
6
|
$stderr.sync = $stdout.sync = true
|
6
7
|
|
@@ -89,6 +90,12 @@ class Test_POSIX_MQ < Test::Unit::TestCase
|
|
89
90
|
assert_equal [ "world", 0 ], @mq.receive
|
90
91
|
end
|
91
92
|
|
93
|
+
def test_shift
|
94
|
+
@mq = POSIX_MQ.new @path, IO::CREAT|IO::RDWR, 0666
|
95
|
+
@mq << "hello"
|
96
|
+
assert_equal "hello", @mq.shift
|
97
|
+
end
|
98
|
+
|
92
99
|
def test_send_receive
|
93
100
|
@mq = POSIX_MQ.new @path, IO::CREAT|IO::RDWR, 0666
|
94
101
|
assert_nil @mq.send("hello", 0)
|
@@ -238,4 +245,28 @@ class Test_POSIX_MQ < Test::Unit::TestCase
|
|
238
245
|
assert POSIX_MQ::OPEN_MAX.kind_of?(Integer)
|
239
246
|
end
|
240
247
|
|
248
|
+
def test_notify_block_replace
|
249
|
+
q = Queue.new
|
250
|
+
@mq = POSIX_MQ.new(@path, :rw)
|
251
|
+
assert_nothing_raised { @mq.notify { |mq| q << mq } }
|
252
|
+
@mq << "hi"
|
253
|
+
assert_equal POSIX_MQ, q.pop.class
|
254
|
+
assert_equal "hi", @mq.receive.first
|
255
|
+
assert_nothing_raised { @mq.notify { |mq| q << "hi" } }
|
256
|
+
@mq << "bye"
|
257
|
+
assert_equal "hi", q.pop
|
258
|
+
end if POSIX_MQ.respond_to?(:notify)
|
259
|
+
|
260
|
+
def test_notify_thread
|
261
|
+
q = Queue.new
|
262
|
+
@mq = POSIX_MQ.new(@path, :rw)
|
263
|
+
@mq.notify_thread = thr = Thread.new { sleep }
|
264
|
+
assert thr.alive?
|
265
|
+
@mq.notify { |mq| q << Thread.current }
|
266
|
+
@mq << "."
|
267
|
+
x = q.pop
|
268
|
+
assert x.instance_of?(Thread)
|
269
|
+
assert Thread.current != x
|
270
|
+
assert ! thr.alive?
|
271
|
+
end if POSIX_MQ.respond_to?(:notify)
|
241
272
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: posix_mq
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ruby POSIX MQ hackers
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2010-01-
|
12
|
+
date: 2010-01-09 00:00:00 +00:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|