posix-mqueue 0.0.4 → 0.0.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +3 -0
- data/README.md +38 -22
- data/Rakefile +2 -0
- data/ext/posix/mqueue.c +77 -21
- data/lib/posix/mqueue/version.rb +1 -1
- data/test/mqueue_test.rb +50 -8
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1ca2c22ede6eec604fc5c61e311fc94036dda433
|
4
|
+
data.tar.gz: b4bc3c44253878f51ebcae0ddd01d34bbdc89068
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 428afcd5fb9521142d0f65f62381deaf83ef21c18d3b702152bcc9869832535f4253c6735876ad87355a06eaa5ccaeae7177a27aeb1357b4151dcc5620124043
|
7
|
+
data.tar.gz: f1653576e2b418a39021bd4618d0b54b2283b6afc5e36f07ce7171233a2095ff802392d0d405fae0b87a4897d44729a6e23a2adb79369fe6bda29ebaf54bfd6d
|
data/.travis.yml
ADDED
data/README.md
CHANGED
@@ -9,18 +9,31 @@ offers:
|
|
9
9
|
* Blocking and non-blocking. Listeners block until a message arrives on the
|
10
10
|
queue. No polling. Sending messages doesn't block.
|
11
11
|
|
12
|
-
Add `gem 'posix-mqueue'` to your favorite Gemfile.
|
13
|
-
|
14
|
-
Still WIP. Not stable. Not everything works as promised.
|
12
|
+
Add `gem 'posix-mqueue', :git => "git@github.com:Sirupsen/posix-mqueue.git"` to your favorite Gemfile.
|
15
13
|
|
16
14
|
## Usage
|
17
15
|
|
16
|
+
### Important notes
|
17
|
+
|
18
|
+
1. This will not work on OS X, but on Linux and probably BSD (not tested).
|
19
|
+
2. `send` and `receive` block. `timedsend` and `timedreceive` do not.
|
20
|
+
3. The default message size is `4096` bytes.
|
21
|
+
4. Linux's default queue size is `10` bytes.
|
22
|
+
|
23
|
+
Read on for details.
|
24
|
+
|
25
|
+
### Example
|
26
|
+
|
18
27
|
```ruby
|
19
28
|
require 'posix/mqueue'
|
20
29
|
|
21
|
-
|
30
|
+
# On Linux the queue name must be prefixed with a slash. Note it is not a file
|
31
|
+
# created at `/whatever`. It's just the name of the queue.
|
32
|
+
# Set maximum default Linux options. See next section to push those limits.
|
33
|
+
# Default options are msgsize: 10 and maxmsg: 4096
|
34
|
+
m = POSIX::Mqueue.new("/whatever", msgsize: 10, maxmsg: 8192)
|
22
35
|
m.send "hello"
|
23
|
-
|
36
|
+
m.receive
|
24
37
|
# => "hello"
|
25
38
|
|
26
39
|
fork { POSIX::Mqueue.new("/whatever").send("world") }
|
@@ -29,50 +42,53 @@ fork { POSIX::Mqueue.new("/whatever").send("world") }
|
|
29
42
|
m.receive
|
30
43
|
# => "world"
|
31
44
|
|
32
|
-
# Deletes the queue and any messages remaining.
|
33
|
-
# None in this case. Otherwise the queue will persist till reboot.
|
34
|
-
m.unlink
|
35
|
-
|
36
45
|
# Queue is now full by default Linux settings, see below on how to increase it.
|
37
46
|
10.times { m.send rand(100).to_s }
|
38
47
|
|
39
|
-
# #
|
40
|
-
|
41
|
-
#
|
48
|
+
# #size returns the size of the queue
|
49
|
+
m.size
|
50
|
+
# => 10
|
51
|
+
|
52
|
+
# #send will block until something is popped off the now full queue.
|
53
|
+
# timesend takes timeout arguments (first one is seconds, second is
|
54
|
+
# nanoseconds). Pass 0 for for both to not block, this is default.
|
42
55
|
|
43
56
|
assert_raises POSIX::Mqueue::QueueFull do
|
44
|
-
m.timedsend
|
57
|
+
m.timedsend "I will fail"
|
45
58
|
end
|
46
59
|
|
47
60
|
# Empty the queue again
|
48
61
|
10.times { m.receive }
|
49
62
|
|
50
63
|
# Like timedsend, timedreceive takes timeout arguments and will raise
|
51
|
-
# POSIX::Mqueue::Queueempty when it would otherwise block
|
64
|
+
# POSIX::Mqueue::Queueempty when it would otherwise block.
|
52
65
|
assert_raises POSIX::Mqueue::QueueEmpty do
|
53
|
-
m.timedreceive
|
66
|
+
m.timedreceive
|
54
67
|
end
|
55
68
|
|
69
|
+
# Deletes the queue and any messages remaining.
|
70
|
+
# None in this case. If not unlinked, the queue will persist till reboot.
|
71
|
+
m.unlink
|
72
|
+
|
56
73
|
```
|
57
74
|
|
58
|
-
|
75
|
+
### mqueue
|
59
76
|
|
60
77
|
Most important information from the manpages, with a little added information
|
61
78
|
about the behavior of `posix-mqueue`.
|
62
79
|
|
63
|
-
|
80
|
+
### /proc interfaces
|
64
81
|
|
65
82
|
Linux has some default limits you can easily change.
|
66
83
|
|
67
84
|
1. `/proc/sys/fs/mqueue/msg_max`. Contains the maximum number of messages in a
|
68
85
|
single queue. Defaults to 10. You should increase that number. `#send` will
|
69
|
-
eventually
|
70
|
-
does not do that right now.
|
86
|
+
eventually block if the queue is full. `#timedsend` will throw `QueueFull`.
|
71
87
|
2. `/proc/sys/fs/mqueue/msgsize_max`. Maximum size of a single message. Defaults
|
72
|
-
to 8192 bytes. `posix-mqueue` allows up to 4096 bytes.
|
73
|
-
|
88
|
+
to 8192 bytes. `posix-mqueue` allows up to 4096 bytes. Overwrite this by
|
89
|
+
passing `{maxmsg: 8192}` as the second argument when initializing.
|
74
90
|
3. `/proc/sys/fs/mqueue/queues_max`. Maximum number of queues on the system.
|
75
|
-
Defaults to 256.
|
91
|
+
Defaults to 256.
|
76
92
|
|
77
93
|
## Virtual filesystem
|
78
94
|
|
data/Rakefile
CHANGED
data/ext/posix/mqueue.c
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
#include <ruby.h>
|
2
2
|
#include <ruby/util.h>
|
3
|
+
#include <ruby/io.h>
|
3
4
|
|
4
5
|
#include <mqueue.h>
|
5
6
|
#include <fcntl.h>
|
@@ -89,9 +90,15 @@ VALUE posix_mqueue_send(VALUE self, VALUE message)
|
|
89
90
|
rb_raise(rb_eTypeError, "Message must be a string");
|
90
91
|
}
|
91
92
|
|
93
|
+
rb_io_wait_writable(data->fd);
|
94
|
+
|
92
95
|
// TODO: Custom priority
|
93
96
|
err = mq_send(data->fd, RSTRING_PTR(message), RSTRING_LEN(message), 10);
|
94
97
|
|
98
|
+
if(err < 0 && errno == EINTR) {
|
99
|
+
err = mq_send(data->fd, RSTRING_PTR(message), RSTRING_LEN(message), 10);
|
100
|
+
}
|
101
|
+
|
95
102
|
if (err < 0) {
|
96
103
|
rb_sys_fail("Message sending failed, please consult mq_send(3)");
|
97
104
|
}
|
@@ -99,7 +106,7 @@ VALUE posix_mqueue_send(VALUE self, VALUE message)
|
|
99
106
|
return Qtrue;
|
100
107
|
}
|
101
108
|
|
102
|
-
VALUE posix_mqueue_timedreceive(VALUE self, VALUE
|
109
|
+
VALUE posix_mqueue_timedreceive(VALUE self, VALUE args)
|
103
110
|
{
|
104
111
|
int err;
|
105
112
|
mqueue_t* data;
|
@@ -107,6 +114,11 @@ VALUE posix_mqueue_timedreceive(VALUE self, VALUE seconds, VALUE nanoseconds)
|
|
107
114
|
char *buf;
|
108
115
|
struct timespec timeout;
|
109
116
|
VALUE str;
|
117
|
+
VALUE seconds = rb_ary_entry(args, 0);
|
118
|
+
VALUE nanoseconds = rb_ary_entry(args, 1);
|
119
|
+
|
120
|
+
if (seconds == Qnil) seconds = INT2FIX(0);
|
121
|
+
if (nanoseconds == Qnil) nanoseconds = INT2FIX(0);
|
110
122
|
|
111
123
|
TypedData_Get_Struct(self, mqueue_t, &mqueue_type, data);
|
112
124
|
|
@@ -115,7 +127,7 @@ VALUE posix_mqueue_timedreceive(VALUE self, VALUE seconds, VALUE nanoseconds)
|
|
115
127
|
}
|
116
128
|
|
117
129
|
if (!RB_TYPE_P(nanoseconds, T_FIXNUM)) {
|
118
|
-
rb_raise(rb_eTypeError, "
|
130
|
+
rb_raise(rb_eTypeError, "Second argument must be a Fixnum");
|
119
131
|
}
|
120
132
|
|
121
133
|
timeout.tv_sec = FIX2ULONG(seconds);
|
@@ -141,28 +153,32 @@ VALUE posix_mqueue_timedreceive(VALUE self, VALUE seconds, VALUE nanoseconds)
|
|
141
153
|
free(buf);
|
142
154
|
|
143
155
|
return str;
|
144
|
-
|
145
|
-
return Qtrue;
|
146
156
|
}
|
147
157
|
|
148
|
-
VALUE posix_mqueue_timedsend(VALUE self, VALUE
|
158
|
+
VALUE posix_mqueue_timedsend(VALUE self, VALUE args)
|
149
159
|
{
|
150
160
|
int err;
|
151
161
|
mqueue_t* data;
|
152
162
|
struct timespec timeout;
|
163
|
+
VALUE message = rb_ary_entry(args, 0);
|
164
|
+
VALUE seconds = rb_ary_entry(args, 1);
|
165
|
+
VALUE nanoseconds = rb_ary_entry(args, 2);
|
166
|
+
|
167
|
+
if (seconds == Qnil) seconds = INT2FIX(0);
|
168
|
+
if (nanoseconds == Qnil) nanoseconds = INT2FIX(0);
|
153
169
|
|
154
170
|
TypedData_Get_Struct(self, mqueue_t, &mqueue_type, data);
|
155
171
|
|
156
|
-
if (!RB_TYPE_P(
|
157
|
-
rb_raise(rb_eTypeError, "
|
172
|
+
if (!RB_TYPE_P(message, T_STRING)) {
|
173
|
+
rb_raise(rb_eTypeError, "Message must be a string");
|
158
174
|
}
|
159
175
|
|
160
|
-
if (!RB_TYPE_P(
|
161
|
-
rb_raise(rb_eTypeError, "First argument must be a
|
176
|
+
if (!RB_TYPE_P(seconds, T_FIXNUM)) {
|
177
|
+
rb_raise(rb_eTypeError, "First argument must be a Fixnum");
|
162
178
|
}
|
163
179
|
|
164
|
-
if (!RB_TYPE_P(
|
165
|
-
rb_raise(rb_eTypeError, "
|
180
|
+
if (!RB_TYPE_P(nanoseconds, T_FIXNUM)) {
|
181
|
+
rb_raise(rb_eTypeError, "Second argument must be a Fixnum");
|
166
182
|
}
|
167
183
|
|
168
184
|
timeout.tv_sec = FIX2ULONG(seconds);
|
@@ -172,7 +188,7 @@ VALUE posix_mqueue_timedsend(VALUE self, VALUE seconds, VALUE nanoseconds, VALUE
|
|
172
188
|
|
173
189
|
if (err < 0) {
|
174
190
|
if(errno == 110) {
|
175
|
-
rb_raise(rb_cQueueFull, "Queue full, most likely you
|
191
|
+
rb_raise(rb_cQueueFull, "Queue full, most likely you want to bump /proc/sys/fs/mqueue/msg_max from the default maximum queue size of 10.");
|
176
192
|
} else {
|
177
193
|
rb_sys_fail("Message sending failed, please consult mq_send(3)");
|
178
194
|
}
|
@@ -181,6 +197,34 @@ VALUE posix_mqueue_timedsend(VALUE self, VALUE seconds, VALUE nanoseconds, VALUE
|
|
181
197
|
return Qtrue;
|
182
198
|
}
|
183
199
|
|
200
|
+
VALUE posix_mqueue_size(VALUE self)
|
201
|
+
{
|
202
|
+
mqueue_t* data;
|
203
|
+
struct mq_attr queue;
|
204
|
+
|
205
|
+
TypedData_Get_Struct(self, mqueue_t, &mqueue_type, data);
|
206
|
+
|
207
|
+
if (mq_getattr(data->fd, &queue) < 0) {
|
208
|
+
rb_sys_fail("Failed reading queue attributes, please consult mq_getattr(3)");
|
209
|
+
}
|
210
|
+
|
211
|
+
return INT2FIX(queue.mq_curmsgs);
|
212
|
+
}
|
213
|
+
|
214
|
+
VALUE posix_mqueue_msgsize(VALUE self)
|
215
|
+
{
|
216
|
+
mqueue_t* data;
|
217
|
+
struct mq_attr queue;
|
218
|
+
|
219
|
+
TypedData_Get_Struct(self, mqueue_t, &mqueue_type, data);
|
220
|
+
|
221
|
+
if (mq_getattr(data->fd, &queue) < 0) {
|
222
|
+
rb_sys_fail("Failed reading queue attributes, please consult mq_getattr(3)");
|
223
|
+
}
|
224
|
+
|
225
|
+
return INT2FIX(queue.mq_msgsize);
|
226
|
+
}
|
227
|
+
|
184
228
|
VALUE posix_mqueue_receive(VALUE self)
|
185
229
|
{
|
186
230
|
int err;
|
@@ -197,9 +241,14 @@ VALUE posix_mqueue_receive(VALUE self)
|
|
197
241
|
// Make sure the buffer is capable
|
198
242
|
buf = (char*)malloc(buf_size);
|
199
243
|
|
200
|
-
|
244
|
+
rb_thread_wait_fd(data->fd);
|
245
|
+
|
201
246
|
err = mq_receive(data->fd, buf, buf_size, NULL);
|
202
247
|
|
248
|
+
if(err < 0 && errno == EINTR) {
|
249
|
+
err = mq_receive(data->fd, buf, buf_size, NULL);
|
250
|
+
}
|
251
|
+
|
203
252
|
if (err < 0) {
|
204
253
|
rb_sys_fail("Message retrieval failed, please consult mq_receive(3)");
|
205
254
|
}
|
@@ -210,14 +259,19 @@ VALUE posix_mqueue_receive(VALUE self)
|
|
210
259
|
return str;
|
211
260
|
}
|
212
261
|
|
213
|
-
VALUE posix_mqueue_initialize(VALUE self, VALUE
|
262
|
+
VALUE posix_mqueue_initialize(VALUE self, VALUE args)
|
214
263
|
{
|
215
|
-
|
216
|
-
|
264
|
+
VALUE options = rb_ary_entry(args, 1);
|
265
|
+
if (options == Qnil) options = rb_hash_new();
|
266
|
+
|
267
|
+
int msgsize = FIX2INT(rb_hash_lookup2(options, ID2SYM(rb_intern("msgsize")), INT2FIX(4096)));
|
268
|
+
int maxmsg = FIX2INT(rb_hash_lookup2(options, ID2SYM(rb_intern("maxmsg")), INT2FIX(10)));
|
269
|
+
VALUE queue = rb_ary_entry(args, 0);
|
270
|
+
|
217
271
|
struct mq_attr attr = {
|
218
272
|
.mq_flags = 0, // Flags, 0 or O_NONBLOCK
|
219
|
-
.mq_maxmsg =
|
220
|
-
.mq_msgsize =
|
273
|
+
.mq_maxmsg = maxmsg, // Max messages in queue
|
274
|
+
.mq_msgsize = msgsize, // Max message size (bytes)
|
221
275
|
.mq_curmsgs = 0 // # currently in queue
|
222
276
|
};
|
223
277
|
|
@@ -249,11 +303,13 @@ void Init_mqueue()
|
|
249
303
|
rb_cQueueEmpty = rb_define_class_under(mqueue, "QueueEmpty", rb_eStandardError);
|
250
304
|
|
251
305
|
rb_define_alloc_func(mqueue, posix_mqueue_alloc);
|
252
|
-
rb_define_method(mqueue, "initialize", posix_mqueue_initialize,
|
306
|
+
rb_define_method(mqueue, "initialize", posix_mqueue_initialize, -2);
|
253
307
|
rb_define_method(mqueue, "send", posix_mqueue_send, 1);
|
254
308
|
rb_define_method(mqueue, "receive", posix_mqueue_receive, 0);
|
255
|
-
rb_define_method(mqueue, "timedsend", posix_mqueue_timedsend,
|
256
|
-
rb_define_method(mqueue, "timedreceive", posix_mqueue_timedreceive, 2);
|
309
|
+
rb_define_method(mqueue, "timedsend", posix_mqueue_timedsend, -2);
|
310
|
+
rb_define_method(mqueue, "timedreceive", posix_mqueue_timedreceive, -2);
|
311
|
+
rb_define_method(mqueue, "msgsize", posix_mqueue_msgsize, 0);
|
312
|
+
rb_define_method(mqueue, "size", posix_mqueue_size, 0);
|
257
313
|
rb_define_method(mqueue, "unlink", posix_mqueue_unlink, 0);
|
258
314
|
}
|
259
315
|
|
data/lib/posix/mqueue/version.rb
CHANGED
data/test/mqueue_test.rb
CHANGED
@@ -36,26 +36,25 @@ class MqueueTest < MiniTest::Unit::TestCase
|
|
36
36
|
def test_multiple_queues
|
37
37
|
@queue.send "hello"
|
38
38
|
|
39
|
-
|
40
|
-
|
39
|
+
with_queue "/other-test-queue" do |q|
|
40
|
+
q.send "world"
|
41
|
+
assert_equal "world", q.receive
|
42
|
+
end
|
41
43
|
|
42
|
-
assert_equal "world", other.receive
|
43
44
|
assert_equal "hello", @queue.receive
|
44
|
-
|
45
|
-
other.unlink
|
46
45
|
end
|
47
46
|
|
48
47
|
def test_timedsend_raises_exception_instead_of_blocking
|
49
|
-
10.times { @queue.timedsend
|
48
|
+
10.times { @queue.timedsend "walrus", 0, 0 }
|
50
49
|
|
51
50
|
assert_raises POSIX::Mqueue::QueueFull do
|
52
|
-
@queue.timedsend(
|
51
|
+
@queue.timedsend("penguin")
|
53
52
|
end
|
54
53
|
end
|
55
54
|
|
56
55
|
def test_timedreceive_raises_exception_instead_of_blocking
|
57
56
|
assert_raises POSIX::Mqueue::QueueEmpty do
|
58
|
-
@queue.timedreceive
|
57
|
+
@queue.timedreceive
|
59
58
|
end
|
60
59
|
end
|
61
60
|
|
@@ -64,4 +63,47 @@ class MqueueTest < MiniTest::Unit::TestCase
|
|
64
63
|
POSIX::Mqueue.new("notvalid")
|
65
64
|
end
|
66
65
|
end
|
66
|
+
|
67
|
+
def test_custom_message_size
|
68
|
+
assert_raises Errno::EMSGSIZE do
|
69
|
+
@queue.send('c' * 4097) # one byte too large
|
70
|
+
end
|
71
|
+
|
72
|
+
# Set to the maximum for Linux
|
73
|
+
with_queue "/big-queue", msgsize: 2 ** 13 do |q|
|
74
|
+
assert_equal 2 ** 13, q.msgsiza
|
75
|
+
|
76
|
+
q.send('c' * (2 ** 13))
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def test_custom_max_queue_size
|
81
|
+
with_queue "/small-queue", maxmsg: 2 do |q|
|
82
|
+
2.times { q.send "narwhal" }
|
83
|
+
|
84
|
+
assert_raises POSIX::Mqueue::QueueFull do
|
85
|
+
q.timedsend("narwhal", 0, 0)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def test_count_in_queue
|
91
|
+
assert_equal 0, @queue.size
|
92
|
+
|
93
|
+
@queue.send "first"
|
94
|
+
@queue.send "second"
|
95
|
+
@queue.send "third"
|
96
|
+
|
97
|
+
assert_equal 3, @queue.size
|
98
|
+
end
|
99
|
+
|
100
|
+
private
|
101
|
+
def with_queue(name, options = {})
|
102
|
+
queue = POSIX::Mqueue.new(name, options)
|
103
|
+
begin
|
104
|
+
yield(queue)
|
105
|
+
ensure
|
106
|
+
queue.unlink
|
107
|
+
end
|
108
|
+
end
|
67
109
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: posix-mqueue
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Simon Eskildsen
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-09-
|
11
|
+
date: 2013-09-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -47,6 +47,7 @@ extensions:
|
|
47
47
|
extra_rdoc_files: []
|
48
48
|
files:
|
49
49
|
- .gitignore
|
50
|
+
- .travis.yml
|
50
51
|
- Gemfile
|
51
52
|
- LICENSE.txt
|
52
53
|
- README.md
|