sysvmq 0.0.1 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +0 -1
- data/README.md +22 -4
- data/ext/sysvmq.c +79 -38
- data/script/compile +8 -0
- data/script/prepare +4 -0
- data/script/run +6 -0
- data/sysvmq.gemspec +1 -1
- data/test/sysv_mq_test.rb +6 -0
- metadata +6 -9
- data/bin/coderay +0 -16
- data/bin/pry +0 -16
- data/bin/rake +0 -16
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7705e20450c90577d603ea17aaedd9b00cb361ae
|
4
|
+
data.tar.gz: 90973d795df822f14ce2c0efad3734d58496802b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 582fe7249dc328068fe68811443f96a2c8fa7c89269ac06928452d0f28df875263b6b621919559ec656809f4fec4a155f7c31863d6112ebf6f77c1efb3209174
|
7
|
+
data.tar.gz: 06c5acb4e3d6d7fea2d1feaab25fc0600f2083a4188e80e9acf123df77e3ddff9e49d4480f1936d328c9c57eac3932aaec2a5dbda795cb261addf576d879b691
|
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -1,7 +1,21 @@
|
|
1
1
|
# sysvmq
|
2
2
|
|
3
|
-
`sysvmq` is a C extension that wraps SysV IPC Message Queues.
|
4
|
-
|
3
|
+
`sysvmq` is a C extension that wraps SysV IPC Message Queues. It's similar to
|
4
|
+
the [POSIX MQ Ruby wrapper](https://github.com/Sirupsen/posix-mqueue). Message
|
5
|
+
queues are handy for interprocess communication where you want to be able to
|
6
|
+
take down either endpoint easily. The main disadvantage of SysV message queues
|
7
|
+
over POSIX MQs (on Linux) is that SysV doesn't expose a file descriptor to do
|
8
|
+
e.g. `select(2)` on.
|
9
|
+
|
10
|
+
## Installation
|
11
|
+
|
12
|
+
Add `sysvm` to your Gemfile.
|
13
|
+
|
14
|
+
`gem 'sysvmq'`
|
15
|
+
|
16
|
+
Currently known to work on Linux (MRI >= 2.0).
|
17
|
+
|
18
|
+
## Usage
|
5
19
|
|
6
20
|
```ruby
|
7
21
|
# Create a message queue with a 1024 byte buffer.
|
@@ -21,6 +35,10 @@ ensure
|
|
21
35
|
mq.destroy
|
22
36
|
```
|
23
37
|
|
24
|
-
##
|
38
|
+
## Todo
|
25
39
|
|
26
|
-
|
40
|
+
* Explain messages types
|
41
|
+
* Add named params for flags (e.g. `mq.receive(:front, blocking: false)`)
|
42
|
+
instead of ORing flags directly.
|
43
|
+
* Add `IPC_INFO` on Linux
|
44
|
+
* Add all of `IPC_STAT`
|
data/ext/sysvmq.c
CHANGED
@@ -11,12 +11,15 @@
|
|
11
11
|
#include <errno.h>
|
12
12
|
#include <stdio.h>
|
13
13
|
#include <string.h>
|
14
|
+
#include <assert.h>
|
15
|
+
|
16
|
+
#define UNINITIALIZED_ERROR -2
|
14
17
|
|
15
18
|
// This is the buffer passed to msg{rcv,snd,ctl}(2)
|
16
19
|
typedef struct {
|
17
20
|
long mtype;
|
18
21
|
char mtext[];
|
19
|
-
}
|
22
|
+
}
|
20
23
|
sysvmq_msgbuf_t;
|
21
24
|
|
22
25
|
// Used for rb_thread_wait_for to signal time between EINTR tries
|
@@ -81,7 +84,7 @@ sysvmq_alloc(VALUE klass)
|
|
81
84
|
//
|
82
85
|
// Controls the queue with IPC_SET, IPC_INFO and IPC_RMID via msgctl(2). When no
|
83
86
|
// argument is passed, it'll return the information about the queue from
|
84
|
-
// IPC_INFO.
|
87
|
+
// IPC_INFO.
|
85
88
|
//
|
86
89
|
// TODO: IPC_SET is currently not supported.
|
87
90
|
static VALUE
|
@@ -118,7 +121,7 @@ sysvmq_stats(int argc, VALUE *argv, VALUE self)
|
|
118
121
|
// TODO: They are probably not ints..
|
119
122
|
info_hash = rb_hash_new();
|
120
123
|
rb_hash_aset(info_hash, ID2SYM(rb_intern("count")), INT2FIX(info.msg_qnum));
|
121
|
-
rb_hash_aset(info_hash, ID2SYM(rb_intern("maximum_size")), INT2FIX(info.msg_qbytes));
|
124
|
+
rb_hash_aset(info_hash, ID2SYM(rb_intern("maximum_size")), INT2FIX(info.msg_qbytes));
|
122
125
|
|
123
126
|
// TODO: Can probably make a better checker here for whether the struct
|
124
127
|
// actually has the member.
|
@@ -144,14 +147,16 @@ sysvmq_destroy(VALUE self)
|
|
144
147
|
// This is used for passing values between the `maybe_blocking` function and the
|
145
148
|
// Ruby function. There's definitely a better way.
|
146
149
|
typedef struct {
|
150
|
+
ssize_t error;
|
151
|
+
ssize_t length;
|
152
|
+
|
147
153
|
size_t size;
|
148
154
|
int flags;
|
149
|
-
|
150
|
-
size_t msg_size; // TODO: typelol
|
155
|
+
long type;
|
151
156
|
sysvmq_t* sysv;
|
152
157
|
|
153
158
|
int retval;
|
154
|
-
}
|
159
|
+
}
|
155
160
|
sysvmq_blocking_call_t;
|
156
161
|
|
157
162
|
// Blocking call to msgsnd(2) (see sysvmq_send). This is to be called without
|
@@ -160,7 +165,10 @@ static void*
|
|
160
165
|
sysvmq_maybe_blocking_receive(void *args)
|
161
166
|
{
|
162
167
|
sysvmq_blocking_call_t* arguments = (sysvmq_blocking_call_t*) args;
|
163
|
-
arguments->
|
168
|
+
arguments->error = msgrcv(arguments->sysv->id, arguments->sysv->msgbuf, arguments->sysv->buffer_size, arguments->type, arguments->flags);
|
169
|
+
|
170
|
+
if (arguments->error >= 0)
|
171
|
+
arguments->length = arguments->error;
|
164
172
|
|
165
173
|
return NULL;
|
166
174
|
}
|
@@ -191,26 +199,42 @@ sysvmq_receive(int argc, VALUE *argv, VALUE self)
|
|
191
199
|
|
192
200
|
// Attach blocking call parameters to the struct passed to the blocking
|
193
201
|
// function wrapper.
|
194
|
-
blocking.flags
|
195
|
-
blocking.type
|
196
|
-
blocking.sysv
|
197
|
-
|
198
|
-
//
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
202
|
+
blocking.flags = FIX2INT(flags);
|
203
|
+
blocking.type = FIX2LONG(type);
|
204
|
+
blocking.sysv = sysv;
|
205
|
+
// Initialize error so it's never a garbage value, if
|
206
|
+
// `sysvmq_maybe_blocking_receive` was interrupted at a non-nice time.
|
207
|
+
blocking.error = UNINITIALIZED_ERROR;
|
208
|
+
blocking.length = UNINITIALIZED_ERROR;
|
209
|
+
|
210
|
+
if ((blocking.flags & IPC_NOWAIT) == IPC_NOWAIT) {
|
211
|
+
while(sysvmq_maybe_blocking_receive(&blocking) == NULL && blocking.error < 0) {
|
212
|
+
if (errno == EINTR) {
|
213
|
+
continue;
|
214
|
+
}
|
215
|
+
|
216
|
+
rb_sys_fail("Failed recieving message from queue");
|
217
|
+
}
|
218
|
+
} else {
|
219
|
+
// msgrcv(2) can block sending a message, if IPC_NOWAIT is not passed.
|
220
|
+
// We unlock the GVL waiting for the call so other threads (e.g. signal
|
221
|
+
// handling) can continue to work. Sets `length` on `blocking` with the size
|
222
|
+
// of the message returned.
|
223
|
+
while (rb_thread_call_without_gvl(sysvmq_maybe_blocking_receive, &blocking, RUBY_UBF_IO, NULL) == NULL
|
224
|
+
&& blocking.error < 0) {
|
225
|
+
if (errno == EINTR || blocking.error == UNINITIALIZED_ERROR) {
|
226
|
+
continue;
|
227
|
+
}
|
228
|
+
|
229
|
+
rb_sys_fail("Failed receiving message from queue");
|
207
230
|
}
|
208
|
-
|
209
|
-
rb_sys_fail("Failed receiving message from queue");
|
210
231
|
}
|
211
232
|
|
233
|
+
// Guard it..
|
234
|
+
assert(blocking.length != UNINITIALIZED_ERROR);
|
235
|
+
|
212
236
|
// Reencode with default external encoding
|
213
|
-
return rb_enc_str_new(sysv->msgbuf->mtext, blocking.
|
237
|
+
return rb_enc_str_new(sysv->msgbuf->mtext, blocking.length, rb_default_external_encoding());
|
214
238
|
}
|
215
239
|
|
216
240
|
// Blocking call to msgsnd(2) (see sysvmq_send). This is to be called without
|
@@ -219,8 +243,7 @@ static void*
|
|
219
243
|
sysvmq_maybe_blocking_send(void *data)
|
220
244
|
{
|
221
245
|
sysvmq_blocking_call_t* arguments = (sysvmq_blocking_call_t*) data;
|
222
|
-
|
223
|
-
arguments->retval = msgsnd(arguments->sysv->id, arguments->sysv->msgbuf, arguments->size, arguments->flags);
|
246
|
+
arguments->error = msgsnd(arguments->sysv->id, arguments->sysv->msgbuf, arguments->size, arguments->flags);
|
224
247
|
|
225
248
|
return NULL;
|
226
249
|
}
|
@@ -257,6 +280,9 @@ sysvmq_send(int argc, VALUE *argv, VALUE self)
|
|
257
280
|
blocking.flags = FIX2INT(flags);
|
258
281
|
blocking.size = RSTRING_LEN(message);
|
259
282
|
blocking.sysv = sysv;
|
283
|
+
// See msgrcv(2) wrapper
|
284
|
+
blocking.error = UNINITIALIZED_ERROR;
|
285
|
+
blocking.length = UNINITIALIZED_ERROR;
|
260
286
|
|
261
287
|
// The buffer can be obtained from `sysvmq_maybe_blocking_send`, instead of
|
262
288
|
// passing it, set it directly on the instance struct.
|
@@ -269,17 +295,27 @@ sysvmq_send(int argc, VALUE *argv, VALUE self)
|
|
269
295
|
// TODO: Can a string copy be avoided?
|
270
296
|
strncpy(sysv->msgbuf->mtext, StringValueCStr(message), blocking.size);
|
271
297
|
|
272
|
-
//
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
rb_thread_check_ints();
|
279
|
-
continue;
|
280
|
-
}
|
298
|
+
// Non-blocking call, skip the expensive GVL release/acquire
|
299
|
+
if ((blocking.flags & IPC_NOWAIT) == IPC_NOWAIT) {
|
300
|
+
while(sysvmq_maybe_blocking_send(&blocking) == NULL && blocking.error < 0) {
|
301
|
+
if (errno == EINTR) {
|
302
|
+
continue;
|
303
|
+
}
|
281
304
|
|
282
|
-
|
305
|
+
rb_sys_fail("Failed sending message to queue");
|
306
|
+
}
|
307
|
+
} else {
|
308
|
+
// msgsnd(2) can block waiting for a message, if IPC_NOWAIT is not passed.
|
309
|
+
// We unlock the GVL waiting for the call so other threads (e.g. signal
|
310
|
+
// handling) can continue to work.
|
311
|
+
while (rb_thread_call_without_gvl(sysvmq_maybe_blocking_send, &blocking, RUBY_UBF_IO, NULL) == NULL
|
312
|
+
&& blocking.error < 0) {
|
313
|
+
if (errno == EINTR || blocking.error == UNINITIALIZED_ERROR) {
|
314
|
+
continue;
|
315
|
+
}
|
316
|
+
|
317
|
+
rb_sys_fail("Failed sending message to queue");
|
318
|
+
}
|
283
319
|
}
|
284
320
|
|
285
321
|
return message;
|
@@ -305,8 +341,11 @@ sysvmq_initialize(VALUE self, VALUE key, VALUE buffer_size, VALUE flags)
|
|
305
341
|
|
306
342
|
TypedData_Get_Struct(self, sysvmq_t, &sysvmq_type, sysv);
|
307
343
|
|
308
|
-
//
|
309
|
-
|
344
|
+
// (key_t) is a 32-bit integer (int). It's defined as `int` (at least on OS X
|
345
|
+
// and Linux). However, `FIX2INT()` (from Ruby) will complain if the key is
|
346
|
+
// something in the range 2^31-2^32, because of the sign bit. We use UINT to
|
347
|
+
// trick Ruby, so it won't complain.
|
348
|
+
sysv->key = (key_t) FIX2UINT(key);
|
310
349
|
|
311
350
|
while ((sysv->id = msgget(sysv->key, FIX2INT(flags))) < 0) {
|
312
351
|
if (errno == EINTR) {
|
@@ -320,7 +359,7 @@ sysvmq_initialize(VALUE self, VALUE key, VALUE buffer_size, VALUE flags)
|
|
320
359
|
// for each message sent. This makes SysVMQ not thread-safe (requiring a
|
321
360
|
// buffer for each thread), but is a reasonable trade-off for now for the
|
322
361
|
// performance.
|
323
|
-
sysv->buffer_size =
|
362
|
+
sysv->buffer_size = (size_t) FIX2LONG(buffer_size + 1);
|
324
363
|
msgbuf_size = sysv->buffer_size * sizeof(char) + sizeof(long);
|
325
364
|
|
326
365
|
// Note that this is a zero-length array, so we size the struct to size of the
|
@@ -346,7 +385,9 @@ void Init_sysvmq()
|
|
346
385
|
rb_define_const(sysvmq, "IPC_RMID", INT2NUM(IPC_RMID));
|
347
386
|
rb_define_const(sysvmq, "IPC_SET", INT2NUM(IPC_SET));
|
348
387
|
rb_define_const(sysvmq, "IPC_STAT", INT2NUM(IPC_STAT));
|
388
|
+
#ifdef __linux__
|
349
389
|
rb_define_const(sysvmq, "IPC_INFO", INT2NUM(IPC_INFO));
|
390
|
+
#endif
|
350
391
|
|
351
392
|
// Define the SysVMQ class and its methods
|
352
393
|
rb_define_alloc_func(sysvmq, sysvmq_alloc);
|
data/script/compile
ADDED
data/script/prepare
ADDED
data/script/run
ADDED
data/sysvmq.gemspec
CHANGED
@@ -4,7 +4,7 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
4
|
|
5
5
|
Gem::Specification.new do |spec|
|
6
6
|
spec.name = "sysvmq"
|
7
|
-
spec.version = '0.0
|
7
|
+
spec.version = '0.1.0'
|
8
8
|
spec.authors = ["Simon Eskildsen"]
|
9
9
|
spec.email = ["sirup@sirupsen.com"]
|
10
10
|
spec.summary = %q{Ruby wrapper for SysV Message Queues}
|
data/test/sysv_mq_test.rb
CHANGED
@@ -111,4 +111,10 @@ class SysVMQTest < MiniTest::Unit::TestCase
|
|
111
111
|
sleep 0.01
|
112
112
|
thread.kill
|
113
113
|
end
|
114
|
+
|
115
|
+
def test_nonblocking_send_and_receive
|
116
|
+
message = "Hello World"
|
117
|
+
@mq.send(message, 1, SysVMQ::IPC_NOWAIT)
|
118
|
+
assert_equal message, @mq.receive(0, SysVMQ::IPC_NOWAIT)
|
119
|
+
end
|
114
120
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sysvmq
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Simon Eskildsen
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-02-
|
11
|
+
date: 2014-02-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -55,10 +55,7 @@ dependencies:
|
|
55
55
|
description: Ruby wrapper for SysV Message Queues
|
56
56
|
email:
|
57
57
|
- sirup@sirupsen.com
|
58
|
-
executables:
|
59
|
-
- coderay
|
60
|
-
- pry
|
61
|
-
- rake
|
58
|
+
executables: []
|
62
59
|
extensions:
|
63
60
|
- ext/extconf.rb
|
64
61
|
extra_rdoc_files: []
|
@@ -69,11 +66,11 @@ files:
|
|
69
66
|
- LICENSE.txt
|
70
67
|
- README.md
|
71
68
|
- Rakefile
|
72
|
-
- bin/coderay
|
73
|
-
- bin/pry
|
74
|
-
- bin/rake
|
75
69
|
- ext/extconf.rb
|
76
70
|
- ext/sysvmq.c
|
71
|
+
- script/compile
|
72
|
+
- script/prepare
|
73
|
+
- script/run
|
77
74
|
- sysvmq.gemspec
|
78
75
|
- test/sysv_mq_test.rb
|
79
76
|
- test/test_helper.rb
|
data/bin/coderay
DELETED
@@ -1,16 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
#
|
3
|
-
# This file was generated by Bundler.
|
4
|
-
#
|
5
|
-
# The application 'coderay' is installed as part of a gem, and
|
6
|
-
# this file is here to facilitate running it.
|
7
|
-
#
|
8
|
-
|
9
|
-
require 'pathname'
|
10
|
-
ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
|
11
|
-
Pathname.new(__FILE__).realpath)
|
12
|
-
|
13
|
-
require 'rubygems'
|
14
|
-
require 'bundler/setup'
|
15
|
-
|
16
|
-
load Gem.bin_path('coderay', 'coderay')
|
data/bin/pry
DELETED
@@ -1,16 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
#
|
3
|
-
# This file was generated by Bundler.
|
4
|
-
#
|
5
|
-
# The application 'pry' is installed as part of a gem, and
|
6
|
-
# this file is here to facilitate running it.
|
7
|
-
#
|
8
|
-
|
9
|
-
require 'pathname'
|
10
|
-
ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
|
11
|
-
Pathname.new(__FILE__).realpath)
|
12
|
-
|
13
|
-
require 'rubygems'
|
14
|
-
require 'bundler/setup'
|
15
|
-
|
16
|
-
load Gem.bin_path('pry', 'pry')
|
data/bin/rake
DELETED
@@ -1,16 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
#
|
3
|
-
# This file was generated by Bundler.
|
4
|
-
#
|
5
|
-
# The application 'rake' is installed as part of a gem, and
|
6
|
-
# this file is here to facilitate running it.
|
7
|
-
#
|
8
|
-
|
9
|
-
require 'pathname'
|
10
|
-
ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
|
11
|
-
Pathname.new(__FILE__).realpath)
|
12
|
-
|
13
|
-
require 'rubygems'
|
14
|
-
require 'bundler/setup'
|
15
|
-
|
16
|
-
load Gem.bin_path('rake', 'rake')
|