posix-mqueue 0.0.4 → 0.0.5
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.
- 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
|