├── .travis.yml ├── lib └── posix │ └── mqueue │ └── version.rb ├── Gemfile ├── ext └── posix │ ├── extconf.rb │ └── mqueue.c ├── test ├── test_helper.rb └── mqueue_test.rb ├── Rakefile ├── .gitignore ├── posix-mqueue.gemspec ├── LICENSE.txt └── README.md /.travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | rvm: 3 | - 2.0.0 4 | -------------------------------------------------------------------------------- /lib/posix/mqueue/version.rb: -------------------------------------------------------------------------------- 1 | module POSIX 2 | class Mqueue 3 | VERSION = "0.0.8" 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | # Specify your gem's dependencies in posix-mqueue.gemspec 4 | gemspec 5 | -------------------------------------------------------------------------------- /ext/posix/extconf.rb: -------------------------------------------------------------------------------- 1 | require 'mkmf' 2 | have_header('mqueue.h') 3 | have_library("rt") 4 | create_makefile('posix/mqueue') 5 | -------------------------------------------------------------------------------- /test/test_helper.rb: -------------------------------------------------------------------------------- 1 | require 'minitest/unit' 2 | require 'minitest/autorun' 3 | 4 | $: << File.dirname(__FILE__) + '/../ext' 5 | 6 | require "posix/mqueue" 7 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require "bundler/gem_tasks" 2 | 3 | task :default => [:test] 4 | 5 | require 'rake/testtask' 6 | Rake::TestTask.new do |t| 7 | `cd ext/posix && ruby extconf.rb && make` 8 | t.libs << "test" 9 | t.pattern = "test/**/*_test.rb" 10 | end 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 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 | *.o 19 | *.so 20 | Makefile 21 | *.log 22 | -------------------------------------------------------------------------------- /posix-mqueue.gemspec: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | lib = File.expand_path('../lib', __FILE__) 3 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 4 | require 'posix/mqueue/version' 5 | 6 | Gem::Specification.new do |spec| 7 | spec.name = "posix-mqueue" 8 | spec.version = POSIX::Mqueue::VERSION 9 | spec.authors = ["Simon Eskildsen"] 10 | spec.email = ["sirup@sirupsen.com"] 11 | spec.description = %q{posix-mqueue is a simple wrapper around the mqueue(7).} 12 | spec.summary = %q{posix-mqueue is a simple wrapper around the mqueue(7). It only works on Linux.} 13 | spec.homepage = "" 14 | spec.license = "MIT" 15 | 16 | spec.files = `git ls-files`.split($/) 17 | spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) } 18 | spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) 19 | spec.require_paths = ["lib", "ext"] 20 | spec.extensions = ["ext/posix/extconf.rb"] 21 | 22 | spec.add_development_dependency "bundler", "~> 1.3" 23 | spec.add_development_dependency "rake" 24 | end 25 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 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. 23 | -------------------------------------------------------------------------------- /test/mqueue_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class MqueueTest < MiniTest::Unit::TestCase 4 | def setup 5 | @queue_name = "/test-queue" 6 | @queue = POSIX::Mqueue.new(@queue_name) 7 | end 8 | 9 | def teardown 10 | @queue.unlink 11 | end 12 | 13 | def test_send_and_receive_single_message 14 | @queue.send "hello" 15 | assert_equal "hello", @queue.receive 16 | end 17 | 18 | def test_send_and_receive_multiple_messages 19 | @queue.send "hello" 20 | @queue.send "world" 21 | 22 | assert_equal "hello", @queue.receive 23 | assert_equal "world", @queue.receive 24 | end 25 | 26 | def test_receiver_blocks 27 | @queue.send "hello" 28 | 29 | assert_equal "hello", @queue.receive 30 | 31 | fork { POSIX::Mqueue.new(@queue_name).send("world") } 32 | 33 | assert_equal "world", @queue.receive 34 | end 35 | 36 | def test_multiple_queues 37 | @queue.send "hello" 38 | 39 | with_queue "/other-test-queue" do |q| 40 | q.send "world" 41 | assert_equal "world", q.receive 42 | end 43 | 44 | assert_equal "hello", @queue.receive 45 | end 46 | 47 | def test_timedsend_raises_exception_instead_of_blocking 48 | 10.times { @queue.timedsend "walrus", 0, 0 } 49 | 50 | assert_raises POSIX::Mqueue::QueueFull do 51 | @queue.timedsend("penguin") 52 | end 53 | end 54 | 55 | def test_timedreceive_raises_exception_instead_of_blocking 56 | assert_raises POSIX::Mqueue::QueueEmpty do 57 | @queue.timedreceive 58 | end 59 | end 60 | 61 | def test_errors_when_queue_name_is_not_slash_prefixed 62 | assert_raises Errno::EINVAL do 63 | POSIX::Mqueue.new("notvalid") 64 | end 65 | end 66 | 67 | def test_custom_message_size 68 | assert_raises Errno::EMSGSIZE do 69 | @queue.send('c' * 4097) # one byte too large 70 | end 71 | 72 | # Set to the maximum for Linux 73 | with_queue "/big-queue", msgsize: 2 ** 13 do |q| 74 | assert_equal 2 ** 13, q.msgsize 75 | 76 | q.send('c' * (2 ** 13)) 77 | end 78 | end 79 | 80 | def test_custom_max_queue_size 81 | with_queue "/small-queue", maxmsg: 2 do |q| 82 | 2.times { q.send "narwhal" } 83 | 84 | assert_raises POSIX::Mqueue::QueueFull do 85 | q.timedsend("narwhal", 0, 0) 86 | end 87 | end 88 | end 89 | 90 | def test_count_in_queue 91 | assert_equal 0, @queue.size 92 | 93 | @queue.send "first" 94 | @queue.send "second" 95 | @queue.send "third" 96 | 97 | assert_equal 3, @queue.size 98 | end 99 | 100 | def test_to_io 101 | assert_instance_of IO, @queue.to_io 102 | end 103 | 104 | private 105 | def with_queue(name, options = {}) 106 | queue = POSIX::Mqueue.new(name, options) 107 | begin 108 | yield(queue) 109 | ensure 110 | queue.unlink 111 | end 112 | end 113 | end 114 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # posix-mqueue [![Build Status](https://travis-ci.org/Sirupsen/posix-mqueue.png?branch=master)](https://travis-ci.org/Sirupsen/posix-mqueue) 2 | 3 | Minimal wrapper around the [POSIX message queue](pmq). The POSIX message queue 4 | offers: 5 | 6 | * Persistence. Push messages while nothing is listening. 7 | * Simplicity. Nothing to set up. Built into Linux. 8 | * IPC. Blazingly fast communication between processes on the same machine. 9 | * Blocking and non-blocking. Listeners block until a message arrives on the 10 | queue. No polling. Sending messages doesn't block. 11 | 12 | 13 | Note that this requires no third party message broker. The messages are handled 14 | by the kernel of your computer. Not all kernels have support for POSIX message 15 | queues, a notably example is Darwin (OS X). Darwin implements the older System V 16 | IPC API. See my [SysV MQ wrapper](https://github.com/Sirupsen/sysvmq). 17 | 18 | ## Usage 19 | 20 | In your Gemfile: 21 | 22 | `gem 'posix-mqueue'` 23 | 24 | ### Important notes 25 | 26 | 1. This will not work on OS X, but on Linux and probably BSD (not tested). 27 | 2. `send` and `receive` block. `timedsend` and `timedreceive` do not. 28 | 3. The default message size is `4096` bytes. 29 | 4. Linux's default queue size is `10` bytes. 30 | 31 | Read on for details. 32 | 33 | ### Example 34 | 35 | ```ruby 36 | require 'posix/mqueue' 37 | 38 | # On Linux the queue name must be prefixed with a slash. Note it is not a file 39 | # created at `/whatever`. It's just the name of the queue. 40 | # Set maximum default Linux options. See next section to push those limits. 41 | # Default options are msgsize: 4096 and maxmsg: 10 42 | m = POSIX::Mqueue.new("/whatever", msgsize: 8192, maxmsg: 10) 43 | m.send "hello" 44 | m.receive 45 | # => "hello" 46 | 47 | fork { POSIX::Mqueue.new("/whatever").send("world") } 48 | 49 | # Blocks until the forked process pushes to the queue 50 | m.receive 51 | # => "world" 52 | 53 | # Queue is now full by default Linux settings, see below on how to increase it. 54 | 10.times { m.send rand(100).to_s } 55 | 56 | # #size returns the size of the queue 57 | m.size 58 | # => 10 59 | 60 | # #send will block until something is popped off the now full queue. 61 | # timesend takes timeout arguments (first one is seconds, second is 62 | # nanoseconds). Pass 0 for for both to not block, this is default. 63 | 64 | assert_raises POSIX::Mqueue::QueueFull do 65 | m.timedsend "I will fail" 66 | end 67 | 68 | # Empty the queue again 69 | 10.times { m.receive } 70 | 71 | # Like timedsend, timedreceive takes timeout arguments and will raise 72 | # POSIX::Mqueue::Queueempty when it would otherwise block. 73 | assert_raises POSIX::Mqueue::QueueEmpty do 74 | m.timedreceive 75 | end 76 | 77 | # Deletes the queue and any messages remaining. 78 | # None in this case. If not unlinked, the queue will persist till reboot. 79 | m.unlink 80 | 81 | ``` 82 | 83 | ### mqueue 84 | 85 | Most important information from the manpages, with a little added information 86 | about the behavior of `posix-mqueue`. 87 | 88 | ### /proc interfaces 89 | 90 | Linux has some default limits you can easily change. 91 | 92 | 1. `/proc/sys/fs/mqueue/msg_max`. Contains the maximum number of messages in a 93 | single queue. Defaults to 10. You should increase that number. `#send` will 94 | eventually block if the queue is full. `#timedsend` will throw `QueueFull`. 95 | 2. `/proc/sys/fs/mqueue/msgsize_max`. Maximum size of a single message. Defaults 96 | to 8192 bytes. `posix-mqueue` defaults to 4096 bytes. Overwrite this by 97 | passing `{msgsize: 8192}` as the second argument when initializing. 98 | 3. `/proc/sys/fs/mqueue/queues_max`. Maximum number of queues on the system. 99 | Defaults to 256. 100 | 101 | ## Virtual filesystem 102 | 103 | The message queue is created as a virtual file system. That means you can mount 104 | it: 105 | 106 | ```bash 107 | # sudo mkdir /dev/queue 108 | # sudo mount -t mqueue none /dev/queue 109 | ``` 110 | 111 | Add a queue and a few tasks, count the characters (19): 112 | 113 | ```ruby 114 | $ irb 115 | > require 'posix/mqueue' 116 | => true 117 | > m = POSIX::Mqueue.new("/queue") 118 | => # 119 | > m.send "narwhal" 120 | => true 121 | > m.send "walrus" 122 | => true 123 | > m.send "ponies" 124 | => true 125 | ``` 126 | 127 | Inspect the mounted filesystem: 128 | 129 | ```bash 130 | $ ls /dev/queue/ 131 | important mails queue 132 | $ cat /dev/queue/queue 133 | QSIZE:19 NOTIFY:0 SIGNO:0 NOTIFY_PID:0 134 | ``` 135 | 136 | Here `QSIZE` is the bytes of data in the queue. The other flags are about 137 | notifications which `posix-mqueue` does not support currently, read about them 138 | in [mq_overview(7)][pmq]. 139 | 140 | [pmq]: http://man7.org/linux/man-pages/man7/mq_overview.7.html 141 | -------------------------------------------------------------------------------- /ext/posix/mqueue.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | 13 | VALUE rb_cQueueFull = Qnil; 14 | VALUE rb_cQueueEmpty = Qnil; 15 | 16 | typedef struct { 17 | mqd_t fd; 18 | VALUE io; 19 | struct mq_attr attr; 20 | size_t queue_len; 21 | char *queue; 22 | } 23 | mqueue_t; 24 | 25 | static void 26 | mqueue_mark(void* ptr) 27 | { 28 | mqueue_t* data = ptr; 29 | rb_gc_mark(data->io); 30 | (void)ptr; 31 | } 32 | 33 | static void 34 | mqueue_free(void* ptr) 35 | { 36 | mqueue_t* data = ptr; 37 | mq_close(data->fd); 38 | xfree(data->queue); 39 | // ?? 40 | // xfree(data->io); 41 | xfree(ptr); 42 | } 43 | 44 | static size_t 45 | mqueue_memsize(const void* ptr) 46 | { 47 | const mqueue_t* data = ptr; 48 | return sizeof(mqueue_t) + sizeof(char) * data->queue_len; 49 | } 50 | 51 | static const rb_data_type_t 52 | mqueue_type = { 53 | "mqueue_type", 54 | { 55 | mqueue_mark, 56 | mqueue_free, 57 | mqueue_memsize 58 | } 59 | }; 60 | 61 | static VALUE 62 | posix_mqueue_alloc(VALUE klass) 63 | { 64 | mqueue_t* data; 65 | VALUE obj = TypedData_Make_Struct(klass, mqueue_t, &mqueue_type, data); 66 | 67 | data->fd = -1; 68 | data->queue = NULL; 69 | data->queue_len = 0; 70 | 71 | return obj; 72 | } 73 | 74 | VALUE posix_mqueue_unlink(VALUE self) 75 | { 76 | mqueue_t* data; 77 | 78 | TypedData_Get_Struct(self, mqueue_t, &mqueue_type, data); 79 | 80 | if (mq_unlink(data->queue) == -1) { 81 | rb_sys_fail("Message queue unlinking failed, please consume mq_unlink(3)"); 82 | } 83 | 84 | return Qtrue; 85 | } 86 | 87 | VALUE posix_mqueue_send(VALUE self, VALUE message) 88 | { 89 | int err; 90 | mqueue_t* data; 91 | 92 | TypedData_Get_Struct(self, mqueue_t, &mqueue_type, data); 93 | 94 | if (!RB_TYPE_P(message, T_STRING)) { 95 | rb_raise(rb_eTypeError, "Message must be a string"); 96 | } 97 | 98 | rb_io_wait_writable(data->fd); 99 | 100 | // TODO: Custom priority 101 | err = mq_send(data->fd, RSTRING_PTR(message), RSTRING_LEN(message), 10); 102 | 103 | if(err < 0 && errno == EINTR) { 104 | err = mq_send(data->fd, RSTRING_PTR(message), RSTRING_LEN(message), 10); 105 | } 106 | 107 | if (err < 0) { 108 | rb_sys_fail("Message sending failed, please consult mq_send(3)"); 109 | } 110 | 111 | return Qtrue; 112 | } 113 | 114 | VALUE posix_mqueue_timedreceive(VALUE self, VALUE args) 115 | { 116 | int err; 117 | mqueue_t* data; 118 | size_t buf_size; 119 | char *buf; 120 | struct timespec timeout; 121 | VALUE str; 122 | VALUE seconds = rb_ary_entry(args, 0); 123 | VALUE nanoseconds = rb_ary_entry(args, 1); 124 | 125 | if (seconds == Qnil) seconds = INT2FIX(0); 126 | if (nanoseconds == Qnil) nanoseconds = INT2FIX(0); 127 | 128 | TypedData_Get_Struct(self, mqueue_t, &mqueue_type, data); 129 | 130 | if (!RB_TYPE_P(seconds, T_FIXNUM)) { 131 | rb_raise(rb_eTypeError, "First argument must be a Fixnum"); 132 | } 133 | 134 | if (!RB_TYPE_P(nanoseconds, T_FIXNUM)) { 135 | rb_raise(rb_eTypeError, "Second argument must be a Fixnum"); 136 | } 137 | 138 | timeout.tv_sec = FIX2ULONG(seconds); 139 | timeout.tv_nsec = FIX2ULONG(nanoseconds); 140 | 141 | buf_size = data->attr.mq_msgsize + 1; 142 | 143 | // Make sure the buffer is capable 144 | buf = (char*)malloc(buf_size); 145 | 146 | // TODO: Specify priority 147 | err = mq_timedreceive(data->fd, buf, buf_size, NULL, &timeout); 148 | 149 | if (err < 0) { 150 | if(errno == 110) { 151 | rb_raise(rb_cQueueEmpty, "Queue empty"); 152 | } else { 153 | rb_sys_fail("Message sending failed, please consult mq_send(3)"); 154 | } 155 | } 156 | 157 | str = rb_str_new(buf, err); 158 | free(buf); 159 | 160 | return str; 161 | } 162 | 163 | VALUE posix_mqueue_timedsend(VALUE self, VALUE args) 164 | { 165 | int err; 166 | mqueue_t* data; 167 | struct timespec timeout; 168 | VALUE message = rb_ary_entry(args, 0); 169 | VALUE seconds = rb_ary_entry(args, 1); 170 | VALUE nanoseconds = rb_ary_entry(args, 2); 171 | 172 | if (seconds == Qnil) seconds = INT2FIX(0); 173 | if (nanoseconds == Qnil) nanoseconds = INT2FIX(0); 174 | 175 | TypedData_Get_Struct(self, mqueue_t, &mqueue_type, data); 176 | 177 | if (!RB_TYPE_P(message, T_STRING)) { 178 | rb_raise(rb_eTypeError, "Message must be a string"); 179 | } 180 | 181 | if (!RB_TYPE_P(seconds, T_FIXNUM)) { 182 | rb_raise(rb_eTypeError, "First argument must be a Fixnum"); 183 | } 184 | 185 | if (!RB_TYPE_P(nanoseconds, T_FIXNUM)) { 186 | rb_raise(rb_eTypeError, "Second argument must be a Fixnum"); 187 | } 188 | 189 | timeout.tv_sec = FIX2ULONG(seconds); 190 | timeout.tv_nsec = FIX2ULONG(nanoseconds); 191 | 192 | err = mq_timedsend(data->fd, RSTRING_PTR(message), RSTRING_LEN(message), 10, &timeout); 193 | 194 | if (err < 0) { 195 | if(errno == 110) { 196 | rb_raise(rb_cQueueFull, "Queue full, most likely you want to bump /proc/sys/fs/mqueue/msg_max from the default maximum queue size of 10."); 197 | } else { 198 | rb_sys_fail("Message sending failed, please consult mq_send(3)"); 199 | } 200 | } 201 | 202 | return Qtrue; 203 | } 204 | 205 | VALUE posix_mqueue_size(VALUE self) 206 | { 207 | mqueue_t* data; 208 | struct mq_attr queue; 209 | 210 | TypedData_Get_Struct(self, mqueue_t, &mqueue_type, data); 211 | 212 | if (mq_getattr(data->fd, &queue) < 0) { 213 | rb_sys_fail("Failed reading queue attributes, please consult mq_getattr(3)"); 214 | } 215 | 216 | return INT2FIX(queue.mq_curmsgs); 217 | } 218 | 219 | VALUE posix_mqueue_to_io(VALUE self) 220 | { 221 | mqueue_t* data; 222 | TypedData_Get_Struct(self, mqueue_t, &mqueue_type, data); 223 | 224 | return data->io; 225 | } 226 | 227 | VALUE posix_mqueue_msgsize(VALUE self) 228 | { 229 | mqueue_t* data; 230 | struct mq_attr queue; 231 | 232 | TypedData_Get_Struct(self, mqueue_t, &mqueue_type, data); 233 | 234 | if (mq_getattr(data->fd, &queue) < 0) { 235 | rb_sys_fail("Failed reading queue attributes, please consult mq_getattr(3)"); 236 | } 237 | 238 | return INT2FIX(queue.mq_msgsize); 239 | } 240 | 241 | VALUE posix_mqueue_receive(VALUE self) 242 | { 243 | int err; 244 | size_t buf_size; 245 | char *buf; 246 | VALUE str; 247 | 248 | mqueue_t* data; 249 | 250 | TypedData_Get_Struct(self, mqueue_t, &mqueue_type, data); 251 | 252 | buf_size = data->attr.mq_msgsize + 1; 253 | 254 | // Make sure the buffer is capable 255 | buf = (char*)malloc(buf_size); 256 | 257 | rb_thread_wait_fd(data->fd); 258 | 259 | err = mq_receive(data->fd, buf, buf_size, NULL); 260 | 261 | if(err < 0 && errno == EINTR) { 262 | err = mq_receive(data->fd, buf, buf_size, NULL); 263 | } 264 | 265 | if (err < 0) { 266 | rb_sys_fail("Message retrieval failed, please consult mq_receive(3)"); 267 | } 268 | 269 | str = rb_str_new(buf, err); 270 | free(buf); 271 | 272 | return str; 273 | } 274 | 275 | VALUE posix_mqueue_initialize(int argc, VALUE* argv, VALUE self) 276 | { 277 | if(argc < 1) 278 | { 279 | rb_raise(rb_eArgError, "initialize requires at least one argument"); 280 | } 281 | 282 | VALUE queue = argv[0]; 283 | if (!RB_TYPE_P(queue, T_STRING)) { 284 | rb_raise(rb_eTypeError, "Queue name must be a string"); 285 | } 286 | 287 | VALUE options; 288 | if (argc < 2) 289 | { 290 | options = rb_hash_new(); 291 | } 292 | else 293 | { 294 | options = argv[1]; 295 | } 296 | 297 | int msgsize = FIX2INT(rb_hash_lookup2(options, ID2SYM(rb_intern("msgsize")), INT2FIX(4096))); 298 | int maxmsg = FIX2INT(rb_hash_lookup2(options, ID2SYM(rb_intern("maxmsg")), INT2FIX(10))); 299 | 300 | struct mq_attr attr = { 301 | .mq_flags = 0, // Flags, 0 or O_NONBLOCK 302 | .mq_maxmsg = maxmsg, // Max messages in queue 303 | .mq_msgsize = msgsize, // Max message size (bytes) 304 | .mq_curmsgs = 0 // # currently in queue 305 | }; 306 | 307 | mqueue_t* data; 308 | TypedData_Get_Struct(self, mqueue_t, &mqueue_type, data); 309 | 310 | if (data->fd != -1) { 311 | // This would cause a memleak otherwise 312 | rb_raise(rb_eRuntimeError, "Illegal reinitialization"); 313 | } 314 | 315 | data->attr = attr; 316 | data->queue_len = RSTRING_LEN(queue); 317 | data->queue = ruby_strdup(StringValueCStr(queue)); 318 | data->fd = mq_open(data->queue, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR, &data->attr); 319 | 320 | if (data->fd == (mqd_t)-1) { 321 | rb_sys_fail("Failed opening the message queue, please consult mq_open(3)"); 322 | } 323 | 324 | VALUE args[1]; 325 | args[0] = INT2FIX(data->fd); 326 | 327 | data->io = rb_class_new_instance(1, args, rb_path2class("IO")); 328 | 329 | return self; 330 | } 331 | 332 | void Init_mqueue() 333 | { 334 | VALUE posix = rb_define_module("POSIX"); 335 | VALUE mqueue = rb_define_class_under(posix, "Mqueue", rb_cObject); 336 | rb_cQueueFull = rb_define_class_under(mqueue, "QueueFull", rb_eStandardError); 337 | rb_cQueueEmpty = rb_define_class_under(mqueue, "QueueEmpty", rb_eStandardError); 338 | 339 | rb_define_alloc_func(mqueue, posix_mqueue_alloc); 340 | rb_define_method(mqueue, "initialize", posix_mqueue_initialize, -1); 341 | rb_define_method(mqueue, "send", posix_mqueue_send, 1); 342 | rb_define_method(mqueue, "receive", posix_mqueue_receive, 0); 343 | rb_define_method(mqueue, "timedsend", posix_mqueue_timedsend, -2); 344 | rb_define_method(mqueue, "timedreceive", posix_mqueue_timedreceive, -2); 345 | rb_define_method(mqueue, "msgsize", posix_mqueue_msgsize, 0); 346 | rb_define_method(mqueue, "size", posix_mqueue_size, 0); 347 | rb_define_method(mqueue, "unlink", posix_mqueue_unlink, 0); 348 | rb_define_method(mqueue, "to_io", posix_mqueue_to_io, 0); 349 | } 350 | 351 | --------------------------------------------------------------------------------