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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d84404b90167dfecfe52eae482a6284f2af9bbc1
4
- data.tar.gz: c4b84de9d1fa95c093f953797cd8d794e52e69bb
3
+ metadata.gz: 7705e20450c90577d603ea17aaedd9b00cb361ae
4
+ data.tar.gz: 90973d795df822f14ce2c0efad3734d58496802b
5
5
  SHA512:
6
- metadata.gz: 1ad89cfac4699a9f283024e0a75c2587200196b8e91410c5cdb2e10e70df3612d2887ba5ce92c9a9efc81a2dc76fb757446444ae3038f72cf79b0a1d3a50ebcd
7
- data.tar.gz: acdf82f69e86f30542da3f98917c8b2ee75f859222bd7f41905d0d8a749d85070801d6fdb5e663dd0cd3add051b96145cadfa4d6cc0761f50df4aff98038d33e
6
+ metadata.gz: 582fe7249dc328068fe68811443f96a2c8fa7c89269ac06928452d0f28df875263b6b621919559ec656809f4fec4a155f7c31863d6112ebf6f77c1efb3209174
7
+ data.tar.gz: 06c5acb4e3d6d7fea2d1feaab25fc0600f2083a4188e80e9acf123df77e3ddff9e49d4480f1936d328c9c57eac3932aaec2a5dbda795cb261addf576d879b691
data/.travis.yml CHANGED
@@ -1,5 +1,4 @@
1
1
  language: ruby
2
2
  rvm:
3
- - 1.9.3
4
3
  - 2.0.0
5
4
  - 2.1.0
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. Only compatible
4
- with MRI 2.0 and 2.1 currently.
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
- ## Installation
38
+ ## Todo
25
39
 
26
- gem 'sysv-mq', github: "Sirupsen/sysvmq" # until published to rubygems
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
- int type;
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->retval = msgrcv(arguments->sysv->id, arguments->sysv->msgbuf, arguments->sysv->buffer_size, arguments->type, arguments->flags);
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 = 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;
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.retval, rb_default_external_encoding());
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
- // 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
- }
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
- rb_sys_fail("Failed sending message to queue");
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
- // TODO: This probably doesn't hold on all platforms.
309
- sysv->key = FIX2LONG(key);
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 = FIX2INT(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
@@ -0,0 +1,8 @@
1
+ #!/bin/bash
2
+ set -ex
3
+
4
+ bundle install
5
+
6
+ if [[ $1 == "final" ]]; then
7
+ bundle exec rake compile
8
+ fi
data/script/prepare ADDED
@@ -0,0 +1,4 @@
1
+ #!/bin/bash
2
+ set -ex
3
+
4
+ gem install bundler
data/script/run ADDED
@@ -0,0 +1,6 @@
1
+ #!/bin/bash -ex
2
+
3
+ while true; do
4
+ echo "Hello World!"
5
+ sleep 5
6
+ done
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.1'
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.1
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-16 00:00:00.000000000 Z
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')