posix_mq 0.5.1 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
data/GNUmakefile CHANGED
@@ -2,7 +2,7 @@
2
2
  all::
3
3
  RUBY = ruby
4
4
  RAKE = rake
5
- GIT_URL = git://git.bogomips.org/ruby_posix_mq.git
5
+ RSYNC = rsync
6
6
 
7
7
  GIT-VERSION-FILE: .FORCE-GIT-VERSION-FILE
8
8
  @./GIT-VERSION-GEN
@@ -39,10 +39,10 @@ clean:
39
39
  -$(MAKE) -C ext/posix_mq clean
40
40
  $(RM) $(setup_rb_files) ext/posix_mq/Makefile
41
41
 
42
- man:
43
- $(MAKE) -C Documentation install-man
42
+ man html:
43
+ $(MAKE) -C Documentation install-$@
44
44
 
45
- pkg_extra := GIT-VERSION-FILE NEWS ChangeLog
45
+ pkg_extra := GIT-VERSION-FILE NEWS ChangeLog LATEST
46
46
  manifest: $(pkg_extra) man
47
47
  $(RM) .manifest
48
48
  $(MAKE) .manifest
@@ -54,48 +54,16 @@ manifest: $(pkg_extra) man
54
54
  cmp $@+ $@ || mv $@+ $@
55
55
  $(RM) $@+
56
56
 
57
- NEWS: GIT-VERSION-FILE
58
- $(RAKE) -s news_rdoc > $@+
59
- mv $@+ $@
60
-
61
- SINCE = 0.4.0
62
- ChangeLog: LOG_VERSION = \
63
- $(shell git rev-parse -q "$(GIT_VERSION)" >/dev/null 2>&1 && \
64
- echo $(GIT_VERSION) || git describe)
65
- ifneq ($(SINCE),)
66
- ChangeLog: log_range = v$(SINCE)..$(LOG_VERSION)
67
- endif
68
57
  ChangeLog: GIT-VERSION-FILE
69
- @echo "ChangeLog from $(GIT_URL) ($(log_range))" > $@+
70
- @echo >> $@+
71
- git log $(log_range) | sed -e 's/^/ /' >> $@+
72
- mv $@+ $@
73
-
74
- news_atom := http://bogomips.org/ruby_posix_mq/NEWS.atom.xml
75
- cgit_atom := http://git.bogomips.org/cgit/ruby_posix_mq.git/atom/?h=master
76
- atom = <link rel="alternate" title="Atom feed" href="$(1)" \
77
- type="application/atom+xml"/>
58
+ wrongdoc prepare
78
59
 
79
- # using rdoc 2.5.x+
80
- doc: .document NEWS ChangeLog
60
+ doc: .document NEWS ChangeLog man html
81
61
  for i in $(man1_rdoc); do > $$i; done
82
- rdoc -a -t "$(shell sed -ne '1s/^= //p' README)"
62
+ $(RM) -r doc
63
+ wrongdoc all
83
64
  install -m644 COPYING doc/COPYING
84
- install -m644 $(shell grep '^[A-Z]' .document) doc/
85
- $(MAKE) -C Documentation install-html install-man
65
+ install -m644 $(shell grep '^[A-Z]' .document) doc/
86
66
  install -m644 $(man1_paths) doc/
87
- cd doc && for i in $(base_bins); do \
88
- html=$$(echo $$i | sed 's/\.rb/_rb/')_1.html; \
89
- sed -e '/"documentation">/r man1/'$$i'.1.html' \
90
- < $$html > tmp && mv tmp $$html; done
91
- $(RUBY) -i -p -e \
92
- '$$_.gsub!("</title>",%q{\&$(call atom,$(cgit_atom))})' \
93
- doc/ChangeLog.html
94
- $(RUBY) -i -p -e \
95
- '$$_.gsub!("</title>",%q{\&$(call atom,$(news_atom))})' \
96
- doc/NEWS.html doc/README.html
97
- $(RAKE) -s news_atom > doc/NEWS.atom.xml
98
- cd doc && ln README.html tmp && mv tmp index.html
99
67
  $(RM) $(man1_rdoc)
100
68
 
101
69
  ifneq ($(VERSION),)
@@ -109,10 +77,10 @@ release_changes := release_changes-$(VERSION)
109
77
  release-notes: $(release_notes)
110
78
  release-changes: $(release_changes)
111
79
  $(release_changes):
112
- $(RAKE) -s release_changes > $@+
80
+ wrongdoc release_changes > $@+
113
81
  $(VISUAL) $@+ && test -s $@+ && mv $@+ $@
114
82
  $(release_notes):
115
- GIT_URL=$(GIT_URL) $(RAKE) -s release_notes > $@+
83
+ wrongdoc release_notes > $@+
116
84
  $(VISUAL) $@+ && test -s $@+ && mv $@+ $@
117
85
 
118
86
  # ensures we're actually on the tagged $(VERSION), only used for release
@@ -177,4 +145,22 @@ test: test-unit
177
145
  test-unit: build
178
146
  $(RUBY) -I lib:ext/posix_mq test/test_posix_mq.rb
179
147
 
180
- .PHONY: .FORCE-GIT-VERSION-FILE doc manifest man test
148
+ # publishes docs to http://bogomips.org/ruby_posix_mq/
149
+ publish_doc:
150
+ -git set-file-times
151
+ $(RM) -r doc
152
+ $(MAKE) doc
153
+ find doc/images -type f | \
154
+ TZ=UTC xargs touch -d '1970-01-01 00:00:03' doc/rdoc.css
155
+ $(MAKE) doc_gz
156
+ $(RSYNC) -av doc/ bogomips.org:/srv/bogomips/ruby_posix_mq/
157
+ git ls-files | xargs touch
158
+
159
+ # Create gzip variants of the same timestamp as the original so nginx
160
+ # "gzip_static on" can serve the gzipped versions directly.
161
+ doc_gz: docs = $(shell find doc -type f ! -regex '^.*\.\(gif\|jpg\|png\|gz\)$$')
162
+ doc_gz:
163
+ for i in $(docs); do \
164
+ gzip --rsyncable -9 < $$i > $$i.gz; touch -r $$i $$i.gz; done
165
+
166
+ .PHONY: .FORCE-GIT-VERSION-FILE doc manifest man test html
data/LATEST ADDED
@@ -0,0 +1,6 @@
1
+ === posix_mq 0.6.0 - Rubinius support /
2
+
3
+ Rubinius 1.2 as well as Ruby 1.9. Ruby 1.8 works except it will
4
+ block the entire interpreter for blocking operations. Spurious
5
+ errors during GC for POSIX_MQ#to_io users are now avoided.
6
+
data/NEWS CHANGED
@@ -1,9 +1,15 @@
1
- === 0.5.1 / 2010-05-09 08:10 UTC
1
+ === posix_mq 0.6.0 - Rubinius support / 2010-12-25 08:28 UTC
2
+
3
+ Rubinius 1.2 as well as Ruby 1.9. Ruby 1.8 works except it will
4
+ block the entire interpreter for blocking operations. Spurious
5
+ errors during GC for POSIX_MQ#to_io users are now avoided.
6
+
7
+ === posix_mq 0.5.1 / 2010-05-09 08:10 UTC
2
8
 
3
9
  Fix POSIX_MQ#notify(&block) usage, this regression was
4
10
  introduced in 0.4.0 and our tests for it were broken, as well.
5
11
 
6
- === 0.5.0 / 2010-05-05 02:54 UTC
12
+ === posix_mq 0.5.0 / 2010-05-05 02:54 UTC
7
13
 
8
14
  Uncommonly raised exceptions due to programmer error are now
9
15
  raised more safely with respect to the MRI garbage collector.
@@ -15,7 +21,7 @@
15
21
 
16
22
  Minor cleanups and documentation now uses RDoc 2.5.x
17
23
 
18
- === 0.4.0 / 2010-03-13 09:44 UTC
24
+ === posix_mq 0.4.0 / 2010-03-13 09:44 UTC
19
25
 
20
26
  Small bugfixes and small API changes to avoid potential
21
27
  issues/misuse are the focus of this release.
@@ -38,7 +44,7 @@
38
44
  * remove non-portable #warning CPP directive
39
45
  * ensure POSIX_MQ#name is clobber-proof
40
46
 
41
- === 0.3.1 / 2010-02-13 12:05 UTC
47
+ === posix_mq 0.3.1 / 2010-02-13 12:05 UTC
42
48
 
43
49
  This fixes a misuse of the Ruby API leading to memory leaks in
44
50
  cases where message queues are continually opened and closed
@@ -49,7 +55,7 @@
49
55
  multi-thread/multi-process-safe in every way imaginable and also
50
56
  capable of non-blocking operation.
51
57
 
52
- === 0.3.0 / 2010-01-09 23:11 UTC
58
+ === posix_mq 0.3.0 / 2010-01-09 23:11 UTC
53
59
 
54
60
  This release adds a few new API methods, fixes MRI 1.8.6
55
61
  support. We should now have full feature parity with
@@ -71,7 +77,7 @@
71
77
  Linux, FreeBSD and possibly any other platforms where POSIX
72
78
  message queues are implemented with a file descriptor.
73
79
 
74
- === 0.2.0 / 2010-01-03 05:52 UTC
80
+ === posix_mq 0.2.0 / 2010-01-03 05:52 UTC
75
81
 
76
82
  This release fixes notification (un)registration and should be
77
83
  fully-supported on modern FreeBSD (7.2+) releases.
@@ -81,7 +87,8 @@
81
87
  functionality. Under FreeBSD, using IO.select on POSIX_MQ
82
88
  objects is now possible as it has always been under Linux.
83
89
 
84
- === 0.1.0 / 2010-01-02 11:01 UTC
90
+ === posix_mq 0.1.0 - initial release
91
+ / 2010-01-02 11:01 UTC
85
92
 
86
93
  initial
87
94
 
data/README CHANGED
@@ -26,6 +26,8 @@ network-aware message queue implementations.
26
26
  * Thread-safe blocking operations under Ruby 1.9, releases GVL
27
27
  before blocking operations.
28
28
 
29
+ * Works under Ruby 1.8, Ruby 1.9 and Rubinius 1.2
30
+
29
31
  * Documented library API
30
32
 
31
33
  * Includes a generic "posix-mq-rb" command-line tool with manpage.
data/Rakefile CHANGED
@@ -1,108 +1,6 @@
1
1
  # -*- encoding: binary -*-
2
-
3
- # most tasks are in the GNUmakefile which offers better parallelism
4
-
5
- def tags
6
- timefmt = '%Y-%m-%dT%H:%M:%SZ'
7
- @tags ||= `git tag -l`.split(/\n/).map do |tag|
8
- if %r{\Av[\d\.]+\z} =~ tag
9
- header, subject, body = `git cat-file tag #{tag}`.split(/\n\n/, 3)
10
- header = header.split(/\n/)
11
- tagger = header.grep(/\Atagger /).first
12
- body ||= "initial"
13
- {
14
- :time => Time.at(tagger.split(/ /)[-2].to_i).utc.strftime(timefmt),
15
- :tagger_name => %r{^tagger ([^<]+)}.match(tagger)[1].strip,
16
- :tagger_email => %r{<([^>]+)>}.match(tagger)[1].strip,
17
- :id => `git rev-parse refs/tags/#{tag}`.chomp!,
18
- :tag => tag,
19
- :subject => subject,
20
- :body => body,
21
- }
22
- end
23
- end.compact.sort { |a,b| b[:time] <=> a[:time] }
24
- end
25
-
26
2
  cgit_url = "http://git.bogomips.org/cgit/ruby_posix_mq.git"
27
- git_url = ENV['GIT_URL'] || 'git://git.bogomips.org/ruby_posix_mq.git'
28
- web_url = "http://bogomips.org/ruby_posix_mq/"
29
-
30
- desc 'prints news as an Atom feed'
31
- task :news_atom do
32
- require 'nokogiri'
33
- new_tags = tags[0,10]
34
- puts(Nokogiri::XML::Builder.new do
35
- feed :xmlns => "http://www.w3.org/2005/Atom" do
36
- id! "#{web_url}NEWS.atom.xml"
37
- title "Ruby posix_mq news"
38
- subtitle "POSIX Message Queues for Ruby"
39
- link! :rel => "alternate", :type => "text/html",
40
- :href => "#{web_url}NEWS.html"
41
- updated(new_tags.empty? ? "1970-01-01T00:00:00Z" : new_tags.first[:time])
42
- new_tags.each do |tag|
43
- entry do
44
- title tag[:subject]
45
- updated tag[:time]
46
- published tag[:time]
47
- author {
48
- name tag[:tagger_name]
49
- email tag[:tagger_email]
50
- }
51
- url = "#{cgit_url}/tag/?id=#{tag[:tag]}"
52
- link! :rel => "alternate", :type => "text/html", :href =>url
53
- id! url
54
- message_only = tag[:body].split(/\n.+\(\d+\):\n {6}/s).first.strip
55
- content({:type =>:text}, message_only)
56
- content(:type =>:xhtml) { pre tag[:body] }
57
- end
58
- end
59
- end
60
- end.to_xml)
61
- end
62
-
63
- desc 'prints RDoc-formatted news'
64
- task :news_rdoc do
65
- tags.each do |tag|
66
- time = tag[:time].tr!('T', ' ').gsub!(/:\d\dZ/, ' UTC')
67
- puts "=== #{tag[:tag].sub(/^v/, '')} / #{time}"
68
- puts ""
69
-
70
- body = tag[:body]
71
- puts tag[:body].gsub(/^/sm, " ").gsub(/[ \t]+$/sm, "")
72
- puts ""
73
- end
74
- end
75
-
76
- desc "print release changelog for Rubyforge"
77
- task :release_changes do
78
- version = ENV['VERSION'] or abort "VERSION= needed"
79
- version = "v#{version}"
80
- vtags = tags.map { |tag| tag[:tag] =~ /\Av/ and tag[:tag] }.sort
81
- prev = vtags[vtags.index(version) - 1]
82
- if prev
83
- system('git', 'diff', '--stat', prev, version) or abort $?
84
- puts ""
85
- system('git', 'log', "#{prev}..#{version}") or abort $?
86
- else
87
- system('git', 'log', version) or abort $?
88
- end
89
- end
90
-
91
- desc "print release notes for Rubyforge"
92
- task :release_notes do
93
- require 'rubygems'
94
-
95
- spec = Gem::Specification.load('posix_mq.gemspec')
96
- puts spec.description.strip
97
- puts ""
98
- puts "* #{spec.homepage}"
99
- puts "* #{spec.email}"
100
- puts "* #{git_url}"
101
-
102
- _, _, body = `git cat-file tag v#{spec.version}`.split(/\n\n/, 3)
103
- print "\nChanges:\n\n"
104
- puts body
105
- end
3
+ git_url = 'git://git.bogomips.org/ruby_posix_mq.git'
106
4
 
107
5
  desc "read news article from STDIN and post to rubyforge"
108
6
  task :publish_news do
@@ -6,8 +6,8 @@ have_header("mqueue.h") or abort "mqueue.h header missing"
6
6
  have_func("__mq_oshandle")
7
7
  have_header("pthread.h")
8
8
  have_func("rb_str_set_len")
9
- have_func("rb_struct_alloc_noinit")
10
9
  have_func('rb_thread_blocking_region')
10
+ have_library("m")
11
11
  have_library("rt")
12
12
  have_library("pthread")
13
13
  dir_config("posix_mq")
@@ -10,8 +10,8 @@
10
10
  #endif
11
11
  #include <ruby.h>
12
12
 
13
- #ifndef RB_GC_GUARD
14
- # define RB_GC_GUARD(v) (*(volatile VALUE *)&(v))
13
+ #ifndef NUM2TIMET
14
+ # define NUM2TIMET NUM2INT
15
15
  #endif
16
16
 
17
17
  #include <time.h>
@@ -21,6 +21,8 @@
21
21
  #include <errno.h>
22
22
  #include <assert.h>
23
23
  #include <unistd.h>
24
+ #include <float.h>
25
+ #include <math.h>
24
26
 
25
27
  #if defined(__linux__)
26
28
  # define MQD_TO_FD(mqd) (int)(mqd)
@@ -29,11 +31,8 @@
29
31
  #else
30
32
  # define MQ_IO_MARK(mq) ((void)(0))
31
33
  # define MQ_IO_SET(mq,val) ((void)(0))
32
- #endif
33
-
34
- #ifdef MQD_TO_FD
35
- # define MQ_IO_MARK(mq) rb_gc_mark((mq)->io)
36
- # define MQ_IO_SET(mq,val) do { (mq)->io = (val); } while (0)
34
+ # define MQ_IO_CLOSE(mq) ((void)(0))
35
+ # define MQ_IO_NILP(mq) ((void)(1))
37
36
  #endif
38
37
 
39
38
  struct posix_mq {
@@ -46,8 +45,26 @@ struct posix_mq {
46
45
  #endif
47
46
  };
48
47
 
48
+ #ifdef MQD_TO_FD
49
+ # define MQ_IO_MARK(mq) rb_gc_mark((mq)->io)
50
+ # define MQ_IO_SET(mq,val) do { (mq)->io = (val); } while (0)
51
+ # define MQ_IO_NIL_P(mq) NIL_P((mq)->io)
52
+ static int MQ_IO_CLOSE(struct posix_mq *mq)
53
+ {
54
+ if (NIL_P(mq->io))
55
+ return 0;
56
+
57
+ /* not safe during GC */
58
+ rb_io_close(mq->io);
59
+ mq->io = Qnil;
60
+
61
+ return 1;
62
+ }
63
+ #endif
64
+
49
65
  static VALUE cPOSIX_MQ, cAttr;
50
- static ID id_new, id_kill, id_fileno;
66
+ static ID id_new, id_kill, id_fileno, id_mul, id_divmod;
67
+ static ID id_flags, id_maxmsg, id_msgsize, id_curmsgs;
51
68
  static ID sym_r, sym_w, sym_rw;
52
69
  static const mqd_t MQD_INVALID = (mqd_t)-1;
53
70
 
@@ -58,34 +75,22 @@ static const mqd_t MQD_INVALID = (mqd_t)-1;
58
75
  #ifndef RSTRING_LEN
59
76
  # define RSTRING_LEN(s) (RSTRING(s)->len)
60
77
  #endif
61
- #ifndef RSTRUCT_PTR
62
- # define RSTRUCT_PTR(s) (RSTRUCT(s)->ptr)
63
- #endif
64
- #ifndef RSTRUCT_LEN
65
- # define RSTRUCT_LEN(s) (RSTRUCT(s)->len)
78
+ #ifndef RFLOAT_VALUE
79
+ # define RFLOAT_VALUE(f) (RFLOAT(f)->value)
66
80
  #endif
67
81
 
68
82
  #ifndef HAVE_RB_STR_SET_LEN
69
- # ifdef RUBINIUS
70
- # define rb_str_set_len(str,len) rb_str_resize(str,len)
71
- # else /* 1.8.6 optimized version */
72
83
  /* this is taken from Ruby 1.8.7, 1.8.6 may not have it */
84
+ # ifdef RUBINIUS
85
+ # error upgrade Rubinius, rb_str_set_len should be available
86
+ # endif
73
87
  static void rb_18_str_set_len(VALUE str, long len)
74
88
  {
75
89
  RSTRING(str)->len = len;
76
90
  RSTRING(str)->ptr[len] = '\0';
77
91
  }
78
- # define rb_str_set_len(str,len) rb_18_str_set_len(str,len)
79
- # endif /* ! RUBINIUS */
80
92
  #endif /* !defined(HAVE_RB_STR_SET_LEN) */
81
93
 
82
- #ifndef HAVE_RB_STRUCT_ALLOC_NOINIT
83
- static VALUE rb_struct_alloc_noinit(VALUE class)
84
- {
85
- return rb_funcall(class, id_new, 0, 0);
86
- }
87
- #endif /* !defined(HAVE_RB_STRUCT_ALLOC_NOINIT) */
88
-
89
94
  /* partial emulation of the 1.9 rb_thread_blocking_region under 1.8 */
90
95
  #ifndef HAVE_RB_THREAD_BLOCKING_REGION
91
96
  # include <rubysig.h>
@@ -127,24 +132,63 @@ struct rw_args {
127
132
  struct timespec *timeout;
128
133
  };
129
134
 
130
- /* hope it's there..., TODO: a better version that works in rbx */
131
- struct timeval rb_time_interval(VALUE);
135
+ static void num2timespec(struct timespec *ts, VALUE t)
136
+ {
137
+ switch (TYPE(t)) {
138
+ case T_FIXNUM:
139
+ case T_BIGNUM:
140
+ ts->tv_sec = NUM2TIMET(t);
141
+ ts->tv_nsec = 0;
142
+ break;
143
+ case T_FLOAT: {
144
+ double f, d;
145
+ double val = RFLOAT_VALUE(t);
146
+
147
+ d = modf(val, &f);
148
+ if (d >= 0) {
149
+ ts->tv_nsec = (long)(d * 1e9 + 0.5);
150
+ } else {
151
+ ts->tv_nsec = (long)(-d * 1e9 + 0.5);
152
+ if (ts->tv_nsec > 0) {
153
+ ts->tv_nsec = 1000000000 - ts->tv_nsec;
154
+ f -= 1;
155
+ }
156
+ }
157
+ ts->tv_sec = (time_t)f;
158
+ if (f != ts->tv_sec)
159
+ rb_raise(rb_eRangeError, "%f out of range", val);
160
+ ts->tv_sec = (time_t)f;
161
+ }
162
+ break;
163
+ default: {
164
+ VALUE f;
165
+ VALUE ary = rb_funcall(t, id_divmod, 1, INT2FIX(1));
166
+
167
+ Check_Type(ary, T_ARRAY);
168
+
169
+ ts->tv_sec = NUM2TIMET(rb_ary_entry(ary, 0));
170
+ f = rb_ary_entry(ary, 1);
171
+ f = rb_funcall(f, id_mul, 1, INT2FIX(1000000000));
172
+ ts->tv_nsec = NUM2LONG(f);
173
+ }
174
+ }
175
+ }
132
176
 
133
177
  static struct timespec *convert_timeout(struct timespec *dest, VALUE t)
134
178
  {
135
- struct timeval tv, now;
179
+ struct timespec ts, now;
136
180
 
137
181
  if (NIL_P(t))
138
182
  return NULL;
139
183
 
140
- tv = rb_time_interval(t); /* aggregate return :( */
141
- gettimeofday(&now, NULL);
142
- dest->tv_sec = now.tv_sec + tv.tv_sec;
143
- dest->tv_nsec = (now.tv_usec + tv.tv_usec) * 1000;
184
+ num2timespec(&ts, t);
185
+ clock_gettime(CLOCK_REALTIME, &now);
186
+ dest->tv_sec = now.tv_sec + ts.tv_sec;
187
+ dest->tv_nsec = now.tv_nsec + ts.tv_nsec;
144
188
 
145
189
  if (dest->tv_nsec > 1000000000) {
146
190
  dest->tv_nsec -= 1000000000;
147
- dest->tv_sec++;
191
+ ++dest->tv_sec;
148
192
  }
149
193
 
150
194
  return dest;
@@ -205,12 +249,10 @@ static void _free(void *ptr)
205
249
  {
206
250
  struct posix_mq *mq = ptr;
207
251
 
208
- if (mq->des != MQD_INVALID) {
252
+ if (mq->des != MQD_INVALID && MQ_IO_NIL_P(mq)) {
209
253
  /* we ignore errors when gc-ing */
210
- int saved_errno = errno;
211
-
212
254
  mq_close(mq->des);
213
- errno = saved_errno;
255
+ errno = 0;
214
256
  }
215
257
  xfree(ptr);
216
258
  }
@@ -246,27 +288,33 @@ static struct posix_mq *get(VALUE self, int need_valid)
246
288
  return mq;
247
289
  }
248
290
 
249
- /* converts the POSIX_MQ::Attr astruct into a struct mq_attr attr */
250
- static void attr_from_struct(struct mq_attr *attr, VALUE astruct, int all)
291
+ static void check_struct_type(VALUE astruct)
251
292
  {
252
- VALUE *ptr;
293
+ if (CLASS_OF(astruct) == cAttr)
294
+ return;
295
+ astruct = rb_inspect(astruct);
296
+ rb_raise(rb_eTypeError, "not a POSIX_MQ::Attr: %s",
297
+ StringValuePtr(astruct));
298
+ }
253
299
 
254
- if (CLASS_OF(astruct) != cAttr) {
255
- RB_GC_GUARD(astruct) = rb_inspect(astruct);
256
- rb_raise(rb_eArgError, "not a POSIX_MQ::Attr: %s",
257
- RSTRING_PTR(astruct));
258
- }
300
+ static void rstruct2mqattr(struct mq_attr *attr, VALUE astruct, int all)
301
+ {
302
+ VALUE tmp;
303
+
304
+ check_struct_type(astruct);
305
+ attr->mq_flags = NUM2LONG(rb_funcall(astruct, id_flags, 0));
259
306
 
260
- ptr = RSTRUCT_PTR(astruct);
307
+ tmp = rb_funcall(astruct, id_maxmsg, 0);
308
+ if (all || !NIL_P(tmp))
309
+ attr->mq_maxmsg = NUM2LONG(tmp);
261
310
 
262
- attr->mq_flags = NUM2LONG(ptr[0]);
311
+ tmp = rb_funcall(astruct, id_msgsize, 0);
312
+ if (all || !NIL_P(tmp))
313
+ attr->mq_msgsize = NUM2LONG(tmp);
263
314
 
264
- if (all || !NIL_P(ptr[1]))
265
- attr->mq_maxmsg = NUM2LONG(ptr[1]);
266
- if (all || !NIL_P(ptr[2]))
267
- attr->mq_msgsize = NUM2LONG(ptr[2]);
268
- if (!NIL_P(ptr[3]))
269
- attr->mq_curmsgs = NUM2LONG(ptr[3]);
315
+ tmp = rb_funcall(astruct, id_curmsgs, 0);
316
+ if (!NIL_P(tmp))
317
+ attr->mq_curmsgs = NUM2LONG(tmp);
270
318
  }
271
319
 
272
320
  /*
@@ -312,10 +360,10 @@ static VALUE init(int argc, VALUE *argv, VALUE self)
312
360
  else if (oflags == sym_rw)
313
361
  x.oflags = O_CREAT|O_RDWR;
314
362
  else {
315
- RB_GC_GUARD(oflags) = oflags;
363
+ oflags = rb_inspect(oflags);
316
364
  rb_raise(rb_eArgError,
317
365
  "symbol must be :r, :w, or :rw: %s",
318
- RSTRING_PTR(oflags));
366
+ StringValuePtr(oflags));
319
367
  }
320
368
  break;
321
369
  case T_BIGNUM:
@@ -347,7 +395,7 @@ static VALUE init(int argc, VALUE *argv, VALUE self)
347
395
  switch (TYPE(attr)) {
348
396
  case T_STRUCT:
349
397
  x.argc = 4;
350
- attr_from_struct(&x.attr, attr, 1);
398
+ rstruct2mqattr(&x.attr, attr, 1);
351
399
 
352
400
  /* principle of least surprise */
353
401
  if (x.attr.mq_flags & O_NONBLOCK)
@@ -356,14 +404,18 @@ static VALUE init(int argc, VALUE *argv, VALUE self)
356
404
  case T_NIL:
357
405
  break;
358
406
  default:
359
- RB_GC_GUARD(attr) = rb_inspect(attr);
360
- rb_raise(rb_eArgError, "attr must be a POSIX_MQ::Attr: %s",
361
- RSTRING_PTR(attr));
407
+ check_struct_type(attr);
362
408
  }
363
409
 
364
410
  mq->des = (mqd_t)xopen(&x);
365
- if (mq->des == MQD_INVALID)
366
- rb_sys_fail("mq_open");
411
+ if (mq->des == MQD_INVALID) {
412
+ if (errno == ENOMEM || errno == EMFILE || errno == ENFILE) {
413
+ rb_gc();
414
+ mq->des = (mqd_t)xopen(&x);
415
+ }
416
+ if (mq->des == MQD_INVALID)
417
+ rb_sys_fail("mq_open");
418
+ }
367
419
 
368
420
  mq->name = rb_str_dup(name);
369
421
  if (x.oflags & O_NONBLOCK)
@@ -609,19 +661,15 @@ static VALUE getattr(VALUE self)
609
661
  {
610
662
  struct posix_mq *mq = get(self, 1);
611
663
  VALUE astruct;
612
- VALUE *ptr;
613
664
 
614
665
  if (mq_getattr(mq->des, &mq->attr) < 0)
615
666
  rb_sys_fail("mq_getattr");
616
667
 
617
- astruct = rb_struct_alloc_noinit(cAttr);
618
- ptr = RSTRUCT_PTR(astruct);
619
- ptr[0] = LONG2NUM(mq->attr.mq_flags);
620
- ptr[1] = LONG2NUM(mq->attr.mq_maxmsg);
621
- ptr[2] = LONG2NUM(mq->attr.mq_msgsize);
622
- ptr[3] = LONG2NUM(mq->attr.mq_curmsgs);
623
-
624
- return astruct;
668
+ return rb_funcall(cAttr, id_new, 4,
669
+ LONG2NUM(mq->attr.mq_flags),
670
+ LONG2NUM(mq->attr.mq_maxmsg),
671
+ LONG2NUM(mq->attr.mq_msgsize),
672
+ LONG2NUM(mq->attr.mq_curmsgs));
625
673
  }
626
674
 
627
675
  /*
@@ -639,7 +687,7 @@ static VALUE setattr(VALUE self, VALUE astruct)
639
687
  struct posix_mq *mq = get(self, 1);
640
688
  struct mq_attr newattr;
641
689
 
642
- attr_from_struct(&newattr, astruct, 0);
690
+ rstruct2mqattr(&newattr, astruct, 0);
643
691
 
644
692
  if (mq_setattr(mq->des, &newattr, NULL) < 0)
645
693
  rb_sys_fail("mq_setattr");
@@ -661,11 +709,11 @@ static VALUE _close(VALUE self)
661
709
  {
662
710
  struct posix_mq *mq = get(self, 1);
663
711
 
664
- if (mq_close(mq->des) < 0)
665
- rb_sys_fail("mq_close");
666
-
712
+ if (! MQ_IO_CLOSE(mq)) {
713
+ if (mq_close(mq->des) == -1)
714
+ rb_sys_fail("mq_close");
715
+ }
667
716
  mq->des = MQD_INVALID;
668
- MQ_IO_SET(mq, Qnil);
669
717
 
670
718
  return Qnil;
671
719
  }
@@ -736,6 +784,20 @@ static void thread_notify_fd(union sigval sv)
736
784
  while ((write(fd, "", 1) < 0) && (errno == EINTR || errno == EAGAIN));
737
785
  }
738
786
 
787
+ static void my_mq_notify(mqd_t des, struct sigevent *not)
788
+ {
789
+ mqd_t rv = mq_notify(des, not);
790
+
791
+ if (rv == MQD_INVALID) {
792
+ if (errno == ENOMEM) {
793
+ rb_gc();
794
+ rv = mq_notify(des, not);
795
+ }
796
+ if (rv == MQD_INVALID)
797
+ rb_sys_fail("mq_notify");
798
+ }
799
+ }
800
+
739
801
  /* :nodoc: */
740
802
  static VALUE setnotify_exec(VALUE self, VALUE io, VALUE thr)
741
803
  {
@@ -763,8 +825,7 @@ static VALUE setnotify_exec(VALUE self, VALUE io, VALUE thr)
763
825
  rb_funcall(mq->thread, id_kill, 0, 0);
764
826
  mq->thread = thr;
765
827
 
766
- if (mq_notify(mq->des, &not) < 0)
767
- rb_sys_fail("mq_notify");
828
+ my_mq_notify(mq->des, &not);
768
829
 
769
830
  return thr;
770
831
  }
@@ -834,8 +895,7 @@ static VALUE setnotify(VALUE self, VALUE arg)
834
895
  rb_raise(rb_eArgError, "must be a signal or nil");
835
896
  }
836
897
 
837
- if (mq_notify(mq->des, notification) < 0)
838
- rb_sys_fail("mq_notify");
898
+ my_mq_notify(mq->des, notification);
839
899
 
840
900
  return rv;
841
901
  }
@@ -931,6 +991,12 @@ void Init_posix_mq_ext(void)
931
991
  id_new = rb_intern("new");
932
992
  id_kill = rb_intern("kill");
933
993
  id_fileno = rb_intern("fileno");
994
+ id_mul = rb_intern("*");
995
+ id_divmod = rb_intern("divmod");
996
+ id_flags = rb_intern("flags");
997
+ id_maxmsg = rb_intern("maxmsg");
998
+ id_msgsize = rb_intern("msgsize");
999
+ id_curmsgs = rb_intern("curmsgs");
934
1000
  sym_r = ID2SYM(rb_intern("r"));
935
1001
  sym_w = ID2SYM(rb_intern("w"));
936
1002
  sym_rw = ID2SYM(rb_intern("rw"));