sysvmq 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: d84404b90167dfecfe52eae482a6284f2af9bbc1
4
+ data.tar.gz: c4b84de9d1fa95c093f953797cd8d794e52e69bb
5
+ SHA512:
6
+ metadata.gz: 1ad89cfac4699a9f283024e0a75c2587200196b8e91410c5cdb2e10e70df3612d2887ba5ce92c9a9efc81a2dc76fb757446444ae3038f72cf79b0a1d3a50ebcd
7
+ data.tar.gz: acdf82f69e86f30542da3f98917c8b2ee75f859222bd7f41905d0d8a749d85070801d6fdb5e663dd0cd3add051b96145cadfa4d6cc0761f50df4aff98038d33e
data/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ bundle
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ - 2.0.0
5
+ - 2.1.0
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in sysv-mq.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Simon Eskildsen
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,26 @@
1
+ # sysvmq
2
+
3
+ `sysvmq` is a C extension that wraps SysV IPC Message Queues. Only compatible
4
+ with MRI 2.0 and 2.1 currently.
5
+
6
+ ```ruby
7
+ # Create a message queue with a 1024 byte buffer.
8
+ require 'sysvmq'
9
+ mq = SysVMQ.new(0xDEADC0DE, 1024, SysVMQ::IPC_CREAT | 0666)
10
+
11
+ mq.send "Hellø Wårld!"
12
+ assert_equal 1, mq.stats[:count]
13
+
14
+ assert_equal "Hellø Wårld!", mq.receive
15
+
16
+ # Raise an exception instead of blocking until a message is available
17
+ mq.receive(0, SysVMQ::IPC_NOWAIT)
18
+
19
+ ensure
20
+ # Delete queue
21
+ mq.destroy
22
+ ```
23
+
24
+ ## Installation
25
+
26
+ gem 'sysv-mq', github: "Sirupsen/sysvmq" # until published to rubygems
data/Rakefile ADDED
@@ -0,0 +1,15 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rake/testtask'
3
+ require 'rake/extensiontask'
4
+
5
+ Rake::ExtensionTask.new 'sysvmq' do |ext|
6
+ ext.ext_dir = 'ext/'
7
+ end
8
+
9
+ task :default => [:compile, :test]
10
+
11
+ Rake::TestTask.new do |t|
12
+ t.libs << "test"
13
+ t.test_files = FileList['test/*_test.rb']
14
+ t.verbose = true
15
+ end
data/bin/coderay ADDED
@@ -0,0 +1,16 @@
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 ADDED
@@ -0,0 +1,16 @@
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 ADDED
@@ -0,0 +1,16 @@
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')
data/ext/extconf.rb ADDED
@@ -0,0 +1,5 @@
1
+ require 'mkmf'
2
+ have_header 'sys/ipc.h'
3
+ have_header 'sys/msg.h'
4
+ have_header 'sys/types.h'
5
+ create_makefile 'sysvmq'
data/ext/sysvmq.c ADDED
@@ -0,0 +1,358 @@
1
+ #include <ruby.h>
2
+ #include <ruby/util.h>
3
+ #include <ruby/thread.h>
4
+ #include <ruby/io.h>
5
+
6
+ #include <sys/types.h>
7
+ #include <sys/ipc.h>
8
+ #include <sys/msg.h>
9
+
10
+ #include <stdlib.h>
11
+ #include <errno.h>
12
+ #include <stdio.h>
13
+ #include <string.h>
14
+
15
+ // This is the buffer passed to msg{rcv,snd,ctl}(2)
16
+ typedef struct {
17
+ long mtype;
18
+ char mtext[];
19
+ }
20
+ sysvmq_msgbuf_t;
21
+
22
+ // Used for rb_thread_wait_for to signal time between EINTR tries
23
+ struct timeval polling_interval;
24
+
25
+ // C struct linked to all Ruby objects
26
+ typedef struct {
27
+ key_t key;
28
+ int id;
29
+ size_t buffer_size;
30
+ sysvmq_msgbuf_t* msgbuf;
31
+ }
32
+ sysvmq_t;
33
+
34
+ static void
35
+ sysvmq_mark(void *ptr)
36
+ {
37
+ // noop, no Ruby objects in the internal struct currently
38
+ }
39
+
40
+ static void
41
+ sysvmq_free(void *ptr)
42
+ {
43
+ sysvmq_t* sysv = ptr;
44
+ xfree(sysv->msgbuf);
45
+ xfree(sysv);
46
+ }
47
+
48
+ static size_t
49
+ sysvmq_memsize(const void* ptr)
50
+ {
51
+ const sysvmq_t* sysv = ptr;
52
+ return sizeof(sysvmq_t) + sizeof(char) * sysv->buffer_size;
53
+ }
54
+
55
+ static const rb_data_type_t
56
+ sysvmq_type = {
57
+ "sysvmq_type",
58
+ {
59
+ sysvmq_mark,
60
+ sysvmq_free,
61
+ sysvmq_memsize
62
+ }
63
+ };
64
+
65
+ static VALUE
66
+ sysvmq_alloc(VALUE klass)
67
+ {
68
+ sysvmq_t* sysv;
69
+ VALUE obj = TypedData_Make_Struct(klass, sysvmq_t, &sysvmq_type, sysv);
70
+
71
+ sysv->key = 0;
72
+ sysv->id = -1;
73
+ sysv->buffer_size = 0;
74
+ sysv->msgbuf = memset(sysv, 0, sizeof(sysvmq_t));
75
+
76
+ return obj;
77
+ }
78
+
79
+ // int msgctl(int msqid, int cmd, struct msqid_ds *buf);
80
+ // http://man7.org/linux/man-pages/man2/msgctl.2.html
81
+ //
82
+ // Controls the queue with IPC_SET, IPC_INFO and IPC_RMID via msgctl(2). When no
83
+ // argument is passed, it'll return the information about the queue from
84
+ // IPC_INFO.
85
+ //
86
+ // TODO: IPC_SET is currently not supported.
87
+ static VALUE
88
+ sysvmq_stats(int argc, VALUE *argv, VALUE self)
89
+ {
90
+ struct msqid_ds info;
91
+ VALUE info_hash;
92
+ VALUE cmd;
93
+ sysvmq_t* sysv;
94
+
95
+ // Optional argument handling
96
+ if (argc > 1) {
97
+ rb_raise(rb_eArgError, "Wrong number of arguments (0..1)");
98
+ }
99
+
100
+ // Default to IPC_STAT
101
+ cmd = argc == 1 ? argv[0] : INT2FIX(IPC_STAT);
102
+
103
+ TypedData_Get_Struct(self, sysvmq_t, &sysvmq_type, sysv);
104
+
105
+ // TODO: Does FIX2INT actually perform this check already?
106
+ Check_Type(cmd, T_FIXNUM);
107
+
108
+ while (msgctl(sysv->id, FIX2INT(cmd), &info) < 0) {
109
+ if (errno == EINTR) {
110
+ rb_thread_wait_for(polling_interval);
111
+ continue;
112
+ }
113
+ rb_sys_fail("Failed executing msgctl(2) command.");
114
+ }
115
+
116
+ // Map values from struct to a hash
117
+ // TODO: Add all the fields
118
+ // TODO: They are probably not ints..
119
+ info_hash = rb_hash_new();
120
+ 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));
122
+
123
+ // TODO: Can probably make a better checker here for whether the struct
124
+ // actually has the member.
125
+ // TODO: BSD support?
126
+ #ifdef __linux__
127
+ rb_hash_aset(info_hash, ID2SYM(rb_intern("size")), INT2FIX(info.__msg_cbytes));
128
+ #elif __APPLE__
129
+ rb_hash_aset(info_hash, ID2SYM(rb_intern("size")), INT2FIX(info.msg_cbytes));
130
+ #endif
131
+
132
+ return info_hash;
133
+ }
134
+
135
+ // Proxies a call with IPC_RMID to `sysvmq_stats` to remove the queue.
136
+ static VALUE
137
+ sysvmq_destroy(VALUE self)
138
+ {
139
+ VALUE argv[1];
140
+ argv[0] = INT2FIX(IPC_RMID);
141
+ return sysvmq_stats(1, argv, self);
142
+ }
143
+
144
+ // This is used for passing values between the `maybe_blocking` function and the
145
+ // Ruby function. There's definitely a better way.
146
+ typedef struct {
147
+ size_t size;
148
+ int flags;
149
+ int type;
150
+ size_t msg_size; // TODO: typelol
151
+ sysvmq_t* sysv;
152
+
153
+ int retval;
154
+ }
155
+ sysvmq_blocking_call_t;
156
+
157
+ // Blocking call to msgsnd(2) (see sysvmq_send). This is to be called without
158
+ // the GVL, and must therefore not use any Ruby functions.
159
+ static void*
160
+ sysvmq_maybe_blocking_receive(void *args)
161
+ {
162
+ sysvmq_blocking_call_t* arguments = (sysvmq_blocking_call_t*) args;
163
+ arguments->retval = msgrcv(arguments->sysv->id, arguments->sysv->msgbuf, arguments->sysv->buffer_size, arguments->type, arguments->flags);
164
+
165
+ return NULL;
166
+ }
167
+
168
+ // ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
169
+ // http://man7.org/linux/man-pages/man2/msgsnd.2.html
170
+ //
171
+ // Receive a message from the message queue.
172
+ VALUE
173
+ sysvmq_receive(int argc, VALUE *argv, VALUE self)
174
+ {
175
+ VALUE type = INT2FIX(0);
176
+ VALUE flags = INT2FIX(0);
177
+ sysvmq_t* sysv;
178
+ sysvmq_blocking_call_t blocking;
179
+
180
+ if (argc > 2) {
181
+ rb_raise(rb_eArgError, "Wrong number of arguments (0..2)");
182
+ }
183
+
184
+ if (argc >= 1) type = argv[0];
185
+ if (argc == 2) flags = argv[1];
186
+
187
+ TypedData_Get_Struct(self, sysvmq_t, &sysvmq_type, sysv);
188
+
189
+ Check_Type(type, T_FIXNUM);
190
+ Check_Type(flags, T_FIXNUM);
191
+
192
+ // Attach blocking call parameters to the struct passed to the blocking
193
+ // function wrapper.
194
+ blocking.flags = FIX2INT(flags);
195
+ blocking.type = FIX2INT(type);
196
+ blocking.sysv = sysv;
197
+
198
+ // msgrcv(2) can block sending a message, if IPC_NOWAIT is not passed.
199
+ // We unlock the GVL waiting for the call so other threads (e.g. signal
200
+ // handling) can continue to work. Sets `msg_size` on `blocking` with the size
201
+ // of the message returned.
202
+ while (rb_thread_call_without_gvl2(sysvmq_maybe_blocking_receive, &blocking, RUBY_UBF_IO, NULL) == NULL
203
+ && blocking.retval < 0) {
204
+ if (errno == EINTR) {
205
+ rb_thread_check_ints();
206
+ continue;
207
+ }
208
+
209
+ rb_sys_fail("Failed receiving message from queue");
210
+ }
211
+
212
+ // Reencode with default external encoding
213
+ return rb_enc_str_new(sysv->msgbuf->mtext, blocking.retval, rb_default_external_encoding());
214
+ }
215
+
216
+ // Blocking call to msgsnd(2) (see sysvmq_send). This is to be called without
217
+ // the GVL, and must therefore not use any Ruby functions.
218
+ static void*
219
+ sysvmq_maybe_blocking_send(void *data)
220
+ {
221
+ 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);
224
+
225
+ return NULL;
226
+ }
227
+
228
+ // int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
229
+ // http://man7.org/linux/man-pages/man2/msgsnd.2.html
230
+ //
231
+ // Sends a message to the message queue.
232
+ VALUE
233
+ sysvmq_send(int argc, VALUE *argv, VALUE self)
234
+ {
235
+ VALUE message;
236
+ VALUE priority = INT2FIX(1);
237
+ VALUE flags = INT2FIX(0);
238
+ sysvmq_blocking_call_t blocking;
239
+ sysvmq_t* sysv;
240
+
241
+ if (argc > 3 || argc == 0) {
242
+ rb_raise(rb_eArgError, "Wrong number of arguments (1..3)");
243
+ }
244
+
245
+ message = argv[0];
246
+ if (argc >= 2) priority = argv[1];
247
+ if (argc == 3) flags = argv[2];
248
+
249
+ TypedData_Get_Struct(self, sysvmq_t, &sysvmq_type, sysv);
250
+
251
+ Check_Type(flags, T_FIXNUM);
252
+ Check_Type(priority, T_FIXNUM);
253
+ // TODO: Call to_s on message if it responds to
254
+
255
+ // Attach blocking call parameters to the struct passed to the blocking
256
+ // function wrapper.
257
+ blocking.flags = FIX2INT(flags);
258
+ blocking.size = RSTRING_LEN(message);
259
+ blocking.sysv = sysv;
260
+
261
+ // The buffer can be obtained from `sysvmq_maybe_blocking_send`, instead of
262
+ // passing it, set it directly on the instance struct.
263
+ sysv->msgbuf->mtype = FIX2INT(priority);
264
+
265
+ if (blocking.size > sysv->buffer_size) {
266
+ rb_raise(rb_eArgError, "Size of message is bigger than buffer size.");
267
+ }
268
+
269
+ // TODO: Can a string copy be avoided?
270
+ strncpy(sysv->msgbuf->mtext, StringValueCStr(message), blocking.size);
271
+
272
+ // msgsnd(2) can block waiting for a message, if IPC_NOWAIT is not passed.
273
+ // We unlock the GVL waiting for the call so other threads (e.g. signal
274
+ // handling) can continue to work.
275
+ while (rb_thread_call_without_gvl2(sysvmq_maybe_blocking_send, &blocking, RUBY_UBF_IO, NULL) == NULL
276
+ && blocking.retval < 0) {
277
+ if (errno == EINTR) {
278
+ rb_thread_check_ints();
279
+ continue;
280
+ }
281
+
282
+ rb_sys_fail("Failed sending message to queue");
283
+ }
284
+
285
+ return message;
286
+ }
287
+
288
+ // int msgget(key_t key, int msgflg);
289
+ // http://man7.org/linux/man-pages/man2/msgget.2.html
290
+ //
291
+ // Instead of calling `msgget` method directly to get the `msgid` (the return
292
+ // value of `msgget`) from Rubyland and pass it around, it's stored internally
293
+ // in a struct only directly accessible from C-land. It's passed to all the
294
+ // other calls that require a `msgid`, for convienence and to share the buffer.
295
+ VALUE
296
+ sysvmq_initialize(VALUE self, VALUE key, VALUE buffer_size, VALUE flags)
297
+ {
298
+ sysvmq_t* sysv;
299
+ size_t msgbuf_size;
300
+
301
+ // TODO: Also support string keys, so you can pass '0xDEADC0DE'
302
+ Check_Type(key, T_FIXNUM);
303
+ Check_Type(flags, T_FIXNUM);
304
+ Check_Type(buffer_size, T_FIXNUM);
305
+
306
+ TypedData_Get_Struct(self, sysvmq_t, &sysvmq_type, sysv);
307
+
308
+ // TODO: This probably doesn't hold on all platforms.
309
+ sysv->key = FIX2LONG(key);
310
+
311
+ while ((sysv->id = msgget(sysv->key, FIX2INT(flags))) < 0) {
312
+ if (errno == EINTR) {
313
+ rb_thread_wait_for(polling_interval); // TODO: Really necessary here?
314
+ continue;
315
+ }
316
+ rb_sys_fail("Failed opening the message queue.");
317
+ }
318
+
319
+ // Allocate the msgbuf buffer once for the instance, to not allocate a buffer
320
+ // for each message sent. This makes SysVMQ not thread-safe (requiring a
321
+ // buffer for each thread), but is a reasonable trade-off for now for the
322
+ // performance.
323
+ sysv->buffer_size = FIX2INT(buffer_size);
324
+ msgbuf_size = sysv->buffer_size * sizeof(char) + sizeof(long);
325
+
326
+ // Note that this is a zero-length array, so we size the struct to size of the
327
+ // header (long, the mtype) and then the rest of the space for message buffer.
328
+ sysv->msgbuf = (sysvmq_msgbuf_t*) xmalloc(msgbuf_size);
329
+
330
+ return self;
331
+ }
332
+
333
+ void Init_sysvmq()
334
+ {
335
+ VALUE sysvmq = rb_define_class("SysVMQ", rb_cObject);
336
+
337
+ // Waiting between blocking calls that have been interrupted outside the GVL,
338
+ // this is to allow time for signal handlers to process signals.
339
+ polling_interval.tv_sec = 0;
340
+ polling_interval.tv_usec = 5;
341
+
342
+ // Define platform specific constants from headers
343
+ rb_define_const(sysvmq, "IPC_CREAT", INT2NUM(IPC_CREAT));
344
+ rb_define_const(sysvmq, "IPC_EXCL", INT2NUM(IPC_EXCL));
345
+ rb_define_const(sysvmq, "IPC_NOWAIT", INT2NUM(IPC_NOWAIT));
346
+ rb_define_const(sysvmq, "IPC_RMID", INT2NUM(IPC_RMID));
347
+ rb_define_const(sysvmq, "IPC_SET", INT2NUM(IPC_SET));
348
+ rb_define_const(sysvmq, "IPC_STAT", INT2NUM(IPC_STAT));
349
+ rb_define_const(sysvmq, "IPC_INFO", INT2NUM(IPC_INFO));
350
+
351
+ // Define the SysVMQ class and its methods
352
+ rb_define_alloc_func(sysvmq, sysvmq_alloc);
353
+ rb_define_method(sysvmq, "initialize", sysvmq_initialize, 3);
354
+ rb_define_method(sysvmq, "send", sysvmq_send, -1);
355
+ rb_define_method(sysvmq, "receive", sysvmq_receive, -1);
356
+ rb_define_method(sysvmq, "stats", sysvmq_stats, -1);
357
+ rb_define_method(sysvmq, "destroy", sysvmq_destroy, 0);
358
+ }
data/sysvmq.gemspec ADDED
@@ -0,0 +1,24 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "sysvmq"
7
+ spec.version = '0.0.1'
8
+ spec.authors = ["Simon Eskildsen"]
9
+ spec.email = ["sirup@sirupsen.com"]
10
+ spec.summary = %q{Ruby wrapper for SysV Message Queues}
11
+ spec.description = %q{Ruby wrapper for SysV Message Queues}
12
+ spec.homepage = "https://github.com/Sirupsen/sysvmq"
13
+ spec.license = "MIT"
14
+
15
+ spec.files = `git ls-files`.split($/)
16
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
17
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
+ spec.require_paths = ["lib"]
19
+ spec.extensions = ["ext/extconf.rb"]
20
+
21
+ spec.add_development_dependency "bundler"
22
+ spec.add_development_dependency "rake"
23
+ spec.add_development_dependency "rake-compiler", "~> 0.9"
24
+ end
@@ -0,0 +1,114 @@
1
+ require_relative 'test_helper'
2
+
3
+ class SysVMQTest < MiniTest::Unit::TestCase
4
+ def setup
5
+ @size = 1024
6
+ @mq = SysVMQ.new(0xDEADC0DE, @size, SysVMQ::IPC_CREAT | 0666)
7
+ end
8
+
9
+ def teardown
10
+ @mq.destroy
11
+ end
12
+
13
+ def test_send_message
14
+ @mq.send("Hello world")
15
+ end
16
+
17
+ def test_send_and_receive_message
18
+ message = "Hello World"
19
+ @mq.send message
20
+ assert_equal message, @mq.receive
21
+ end
22
+
23
+ def test_send_and_count_message
24
+ @mq.send "test"
25
+ assert_equal 1, @mq.stats[:count]
26
+ @mq.send "test"
27
+ assert_equal 2, @mq.stats[:count]
28
+ end
29
+
30
+ def test_send_and_receive_many_times
31
+ many = 100_000
32
+ message = "Hello World"
33
+
34
+ many.times do
35
+ @mq.send message
36
+ assert_equal message, @mq.receive
37
+ end
38
+ end
39
+
40
+ def test_sends_and_receives_utf8
41
+ message = "simån hørup"
42
+ @mq.send message
43
+ assert_equal message, @mq.receive
44
+ end
45
+
46
+ def test_sending_5_bytes_should_report_5_byte_queue
47
+ message = "B" * 5
48
+ @mq.send message
49
+ assert_equal 5, @mq.stats[:size]
50
+ end
51
+
52
+ def test_sending_utf_should_report_correct_size_queue
53
+ message = "ø" * 5
54
+ @mq.send message
55
+ assert_equal "ø".bytes.size * 5, @mq.stats[:size]
56
+ end
57
+
58
+ def test_receive_on_empty_queue_raises_enomsg_if_ipc_nowait
59
+ assert_raises Errno::ENOMSG do
60
+ @mq.receive(0, SysVMQ::IPC_NOWAIT)
61
+ end
62
+ end
63
+
64
+ def test_send_and_receive_empty_message
65
+ @mq.send ""
66
+ assert_equal "", @mq.receive
67
+ end
68
+
69
+ def test_allow_multiple_queues_with_different_sizes
70
+ mq2 = SysVMQ.new(0xDEADCAFE, 2048, SysVMQ::IPC_CREAT | 0660)
71
+ mq2.send("B" * 2048)
72
+ mq2.destroy
73
+ end
74
+
75
+ def test_send_and_receive_with_type
76
+ @mq.send("10", 10)
77
+ @mq.send("5", 5)
78
+
79
+ assert_equal "5", @mq.receive(5)
80
+ assert_equal "10", @mq.receive(10)
81
+ end
82
+
83
+ def test_send_and_receive_with_negative_type
84
+ @mq.send("10", 10)
85
+ @mq.send("5", 5)
86
+
87
+ assert_equal "5", @mq.receive(-7)
88
+ assert_equal "10", @mq.receive(-10)
89
+ end
90
+
91
+ def test_responds_to_sigint
92
+ pid = fork {
93
+ begin
94
+ mq = SysVMQ.new(0xDEADCAFE, 2048, SysVMQ::IPC_CREAT | 0660)
95
+ mq.receive
96
+ rescue Interrupt
97
+ mq.destroy
98
+ end
99
+ }
100
+ sleep 0.01
101
+ Process.kill(:SIGINT, pid)
102
+ Process.wait(pid)
103
+ end
104
+
105
+ def test_kills_thread_cleanly
106
+ thread = Thread.new {
107
+ mq = SysVMQ.new(0xDEADCAFE, 2048, SysVMQ::IPC_CREAT | 0660)
108
+ mq.receive
109
+ }
110
+
111
+ sleep 0.01
112
+ thread.kill
113
+ end
114
+ end
@@ -0,0 +1,3 @@
1
+ require 'minitest/unit'
2
+ require 'minitest/autorun'
3
+ require 'sysvmq'
metadata ADDED
@@ -0,0 +1,106 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sysvmq
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Simon Eskildsen
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-02-16 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake-compiler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '0.9'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '0.9'
55
+ description: Ruby wrapper for SysV Message Queues
56
+ email:
57
+ - sirup@sirupsen.com
58
+ executables:
59
+ - coderay
60
+ - pry
61
+ - rake
62
+ extensions:
63
+ - ext/extconf.rb
64
+ extra_rdoc_files: []
65
+ files:
66
+ - ".gitignore"
67
+ - ".travis.yml"
68
+ - Gemfile
69
+ - LICENSE.txt
70
+ - README.md
71
+ - Rakefile
72
+ - bin/coderay
73
+ - bin/pry
74
+ - bin/rake
75
+ - ext/extconf.rb
76
+ - ext/sysvmq.c
77
+ - sysvmq.gemspec
78
+ - test/sysv_mq_test.rb
79
+ - test/test_helper.rb
80
+ homepage: https://github.com/Sirupsen/sysvmq
81
+ licenses:
82
+ - MIT
83
+ metadata: {}
84
+ post_install_message:
85
+ rdoc_options: []
86
+ require_paths:
87
+ - lib
88
+ required_ruby_version: !ruby/object:Gem::Requirement
89
+ requirements:
90
+ - - ">="
91
+ - !ruby/object:Gem::Version
92
+ version: '0'
93
+ required_rubygems_version: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - ">="
96
+ - !ruby/object:Gem::Version
97
+ version: '0'
98
+ requirements: []
99
+ rubyforge_project:
100
+ rubygems_version: 2.2.0
101
+ signing_key:
102
+ specification_version: 4
103
+ summary: Ruby wrapper for SysV Message Queues
104
+ test_files:
105
+ - test/sysv_mq_test.rb
106
+ - test/test_helper.rb