sysvmq 0.0.1 → 0.1.0
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 +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')
|