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 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')