ioplus 0.0.1.PROTOTYPE

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 6242bf07dabdee5e581a67b3fe6139deb291b924
4
+ data.tar.gz: 569d32580f365d037f8e4bb7342b1b1a1963b13c
5
+ SHA512:
6
+ metadata.gz: e655c0bea9f742c59c432982470e89618c0fd2a81128c3d72617d473c47681fd96b7e8fadea0995e12267004ce6dafb731a520ae6f1fd223a00fdf63eb5b8376
7
+ data.tar.gz: 145738e030b55c8838b2a7ba460e3ffa04f4e8d5df9c5c38fb14dc9ea3a931428bc6cce2ccaea43b1e3e48be3e48091c2bdd3271ff237e4f591867d8633184a9
@@ -0,0 +1,35 @@
1
+ # ioplus License (2-clause BSD License)
2
+
3
+ ----
4
+
5
+ Copyright (c) 2015, dearblue
6
+ All rights reserved.
7
+
8
+ Redistribution and use in source and binary forms, with or without
9
+ modification, are permitted provided that the following conditions are met:
10
+
11
+ 1. Redistributions of source code must retain the above copyright notice,
12
+ this list of conditions and the following disclaimer.
13
+
14
+ 2. Redistributions in binary form must reproduce the above copyright
15
+ notice, this list of conditions and the following disclaimer in the
16
+ documentation and/or other materials provided with the distribution.
17
+
18
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
22
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
23
+ OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28
+ POSSIBILITY OF SUCH DAMAGE.
29
+
30
+ ----
31
+
32
+ In Japanese.
33
+
34
+ 日本語訳については次のページが参考になるでしょう:
35
+ http://www.freebsd.org/ja/copyright/freebsd-license.html
@@ -0,0 +1,20 @@
1
+ # ioplus - IO 拡張モジュール
2
+
3
+ このライブラリは IO クラスを拡張させるモジュールが含まれています。
4
+
5
+ * 非同期読み込みメソッド - ``IO#aio_read`` / ``IO#aread`` <- ``IOPlus::IO#aio_read``
6
+ * 非同期書き込みメソッド - ``IO#aio_write`` / ``IO#awrite`` <- ``IOPlus::IO#aio_write``
7
+
8
+ これらのメソッドは ``aio_read(2)`` と ``aio_write(2)`` に似た機能を提供します。
9
+
10
+ ***このライブラリはセーフレベルを考慮しないため、セキュリティリスクがあります。信頼できない外部データを取り扱う環境では、このライブラリを読み込まないで下さい。***
11
+
12
+ ## 注意点
13
+
14
+ * FreeBSD 上では ``aio.ko`` がカーネルに読み込まれている必要があります。
15
+
16
+ ``/boot/loader.conf`` に ``aio_load="YES"`` を追加するか、実行前に ``kldload aio`` を行って下さい。
17
+
18
+ * ``IO#read`` / ``IO#write`` を経由するメソッドとの使用は不可解な動作の原因となります。
19
+
20
+ 必要であれば ``IO#sysread`` / ``IO#syswrite`` などのメソッドを用いて下さい。
@@ -0,0 +1,24 @@
1
+ # ioplus - IO 拡張モジュール
2
+
3
+ This library is included a module for IO class extension.
4
+
5
+ These methods are provide similar functions as ``pread(2)``, ``pwrite(2)``, ``aio_read(2)`` and ``aio_write(2)``.
6
+
7
+ ***This library has a security risk, because it is not consideration for the safe-level.***
8
+
9
+ ## Required kernel object on FreeBSD
10
+
11
+ ``aio.ko`` is required for asynchronouse I/O operation on FreeBSD.
12
+
13
+ Execute ruby script before try following:
14
+
15
+ ```shell:shell
16
+ # echo 'aio_load="YES"' >> /boot/loader.conf
17
+ # shutdown -r now
18
+ ```
19
+
20
+ OR
21
+
22
+ ```shell:shell
23
+ # kldload aio
24
+ ```
@@ -0,0 +1,143 @@
1
+
2
+ require "rake/clean"
3
+
4
+ DOC = FileList["{README,LICENSE,CHANGELOG,Changelog,HISTORY}{,.ja}{,.txt,.rd,.rdoc,.md,.markdown}"] +
5
+ FileList["ext/**/{README,LICENSE,CHANGELOG,Changelog,HISTORY}{,.ja}{,.txt,.rd,.rdoc,.md,.markdown}"]
6
+ #EXT = FileList["ext/**/*.{h,hh,c,cc,cpp,cxx}"] +
7
+ # FileList["ext/externals/**/*"]
8
+ EXT = FileList["ext/**/*"]
9
+ BIN = FileList["bin/*"]
10
+ LIB = FileList["lib/**/*.rb"]
11
+ SPEC = FileList["spec/**/*"]
12
+ EXAMPLE = FileList["examples/**/*"]
13
+ RAKEFILE = [File.basename(__FILE__), "gemstub.rb"]
14
+ EXTRA = []
15
+
16
+ load "gemstub.rb"
17
+
18
+ EXTCONF = FileList["ext/extconf.rb"]
19
+ EXTCONF.reject! { |n| !File.file?(n) }
20
+ GEMSTUB.extensions += EXTCONF
21
+ GEMSTUB.executables += FileList["bin/*"].map { |n| File.basename n }
22
+ GEMSTUB.executables.sort!
23
+
24
+ GEMFILE = "#{GEMSTUB.name}-#{GEMSTUB.version}.gem"
25
+ GEMSPEC = "#{GEMSTUB.name}.gemspec"
26
+
27
+ GEMSTUB.files += DOC + EXT + EXTCONF + BIN + LIB + SPEC + EXAMPLE + RAKEFILE + EXTRA
28
+ GEMSTUB.files.sort!
29
+ GEMSTUB.rdoc_options ||= %w(--charset UTF-8)
30
+ GEMSTUB.extra_rdoc_files += DOC + LIB + EXT.reject { |n| n.include?("/externals/") || !%w(.h .hh .c .cc .cpp .cxx).include?(File.extname(n)) }
31
+ GEMSTUB.extra_rdoc_files.sort!
32
+
33
+ CLEAN << GEMSPEC
34
+ CLOBBER << GEMFILE
35
+
36
+ task :default => :all
37
+
38
+
39
+ unless EXTCONF.empty?
40
+ RUBYSET ||= (ENV["RUBYSET"] || "").split(",")
41
+
42
+ if RUBYSET.nil? || RUBYSET.empty?
43
+ $stderr.puts <<-EOS
44
+ #{__FILE__}:
45
+ |
46
+ | If you want binary gem package, launch rake with ``RUBYSET`` enviroment
47
+ | variable for set ruby interpreters by comma separated.
48
+ |
49
+ | e.g.) $ rake RUBYSET=ruby
50
+ | or) $ rake RUBYSET=ruby20,ruby21,ruby22
51
+ |
52
+ EOS
53
+ else
54
+ platforms = RUBYSET.map { |ruby| `#{ruby} --disable gems -rrbconfig -e "puts RbConfig::CONFIG['arch']"`.chomp }
55
+ platforms1 = platforms.uniq
56
+ unless platforms1.size == 1 && !platforms1[0].empty?
57
+ raise "different platforms (#{Hash[*RUBYSET.zip(platforms).flatten].inspect})"
58
+ end
59
+ PLATFORM = platforms1[0]
60
+
61
+ RUBY_VERSIONS = RUBYSET.map do |ruby|
62
+ ver = `#{ruby} --disable gem -rrbconfig -e "puts RbConfig::CONFIG['ruby_version']"`.slice(/\d+\.\d+/)
63
+ raise "failed ruby checking - ``#{ruby}''" unless $?.success?
64
+ [ver, ruby]
65
+ end
66
+ SOFILES_SET = RUBY_VERSIONS.map { |(ver, ruby)| ["lib/#{ver}/#{GEMSTUB.name}.so", ruby] }
67
+ SOFILES = SOFILES_SET.map { |(lib, ruby)| lib }
68
+
69
+ GEMSTUB_NATIVE = GEMSTUB.dup
70
+ GEMSTUB_NATIVE.files += SOFILES
71
+ GEMSTUB_NATIVE.platform = Gem::Platform.new(PLATFORM).to_s
72
+ GEMSTUB_NATIVE.extensions.clear
73
+ GEMFILE_NATIVE = "#{GEMSTUB_NATIVE.name}-#{GEMSTUB_NATIVE.version}-#{GEMSTUB_NATIVE.platform}.gem"
74
+ GEMSPEC_NATIVE = "#{GEMSTUB_NATIVE.name}-#{GEMSTUB_NATIVE.platform}.gemspec"
75
+
76
+ task :all => ["native-gem", GEMFILE]
77
+
78
+ desc "build binary gem package"
79
+ task "native-gem" => GEMFILE_NATIVE
80
+
81
+ desc "generate binary gemspec"
82
+ task "native-gemspec" => GEMSPEC_NATIVE
83
+
84
+ file GEMFILE_NATIVE => DOC + EXT + EXTCONF + BIN + LIB + SPEC + EXAMPLE + SOFILES + RAKEFILE + [GEMSPEC_NATIVE] do
85
+ sh "gem build #{GEMSPEC_NATIVE}"
86
+ end
87
+
88
+ file GEMSPEC_NATIVE => __FILE__ do
89
+ File.write(GEMSPEC_NATIVE, GEMSTUB_NATIVE.to_ruby, mode: "wb")
90
+ end
91
+
92
+ SOFILES_SET.each do |(soname, ruby)|
93
+ sodir = File.dirname(soname)
94
+ makefile = File.join(sodir, "Makefile")
95
+
96
+ CLEAN << GEMSPEC_NATIVE << sodir
97
+ CLOBBER << GEMFILE_NATIVE
98
+
99
+ directory sodir
100
+
101
+ desc "generate Makefile for binary extension library"
102
+ file makefile => [sodir] + EXTCONF do
103
+ cd sodir do
104
+ sh "#{ruby} ../../#{EXTCONF[0]} \"--ruby=#{ruby}\""
105
+ end
106
+ end
107
+
108
+ desc "build binary extension library"
109
+ file soname => [makefile] + EXT do
110
+ cd sodir do
111
+ sh "make"
112
+ end
113
+ end
114
+ end
115
+ end
116
+ end
117
+
118
+
119
+ task :all => GEMFILE
120
+
121
+ desc "generate local rdoc"
122
+ task :rdoc => DOC + EXT + LIB do
123
+ sh *(%w(rdoc) + GEMSTUB.rdoc_options + DOC + EXT + LIB)
124
+ end
125
+
126
+ desc "launch rspec"
127
+ task rspec: :all do
128
+ sh "rspec"
129
+ end
130
+
131
+ desc "build gem package"
132
+ task gem: GEMFILE
133
+
134
+ desc "generate gemspec"
135
+ task gemspec: GEMSPEC
136
+
137
+ file GEMFILE => DOC + EXT + EXTCONF + BIN + LIB + SPEC + EXAMPLE + RAKEFILE + [GEMSPEC] do
138
+ sh "gem build #{GEMSPEC}"
139
+ end
140
+
141
+ file GEMSPEC => RAKEFILE do
142
+ File.write(GEMSPEC, GEMSTUB.to_ruby, mode: "wb")
143
+ end
@@ -0,0 +1,28 @@
1
+ #!/usr/bin/env ruby
2
+ #vim: set fileencoding:utf-8
3
+
4
+ require "pp"
5
+ require "ioplus"
6
+
7
+ file = ARGV[0] || "/usr/bin/cc"
8
+
9
+ puts "read file: #{file}"
10
+
11
+ File.open(file, "rb") do |f|
12
+ aio = []
13
+ aio << f.aio_read(0, 500, b = "".b) { |x| puts "<aio done! offset #{x.offset} +#{x.status}>\n" }
14
+ aio << f.aio_read(rand(1000000), 1000) { |x| puts "<aio done! offset #{x.offset} +#{x.status}>\n" }
15
+ aio << f.aio_read(rand(1000000), 100) { |x| puts "<aio done! offset #{x.offset} +#{x.status}>\n" }
16
+ aio << f.aio_read(rand(1000000), 200) { |x| puts "<aio done! offset #{x.offset} +#{x.status}>\n" }
17
+ aio << f.aio_read(0, 300, "a".b) { |x| puts "<aio done! offset #{x.offset} +#{x.status}>\n" }
18
+ p aio[1].cancel rescue nil
19
+ pp aio
20
+ IO.aio_suspend(*aio)
21
+ p b
22
+ pp aio
23
+ end
24
+
25
+ GC.start
26
+
27
+ puts "<finished main thread and 1 sec sleeping>\n"
28
+ sleep 1
@@ -0,0 +1,38 @@
1
+ #!/usr/bin/env ruby
2
+ #vim: set fileencoding:utf-8
3
+
4
+ require "mkmf"
5
+
6
+ srcdirpat = File.dirname(__FILE__).gsub(/[\[\{\},]/, "[\\0]")
7
+ $srcs = Dir.glob(File.join(srcdirpat, "*.{c,cc}"))
8
+ vpath = []
9
+
10
+ case
11
+ when have_header("aio.h")
12
+ if have_func("kevent", %w(sys/types.h sys/event.h sys/time.h))
13
+ $defs << " -DEXTIO_HAVE_KEVENT=1"
14
+ else
15
+ $stderr.puts <<EOS
16
+ |
17
+ | #{__FILE__}:#{__LINE__}: NOT SUPPORTED PLATFORM.
18
+ |
19
+ EOS
20
+ exit 1
21
+ end
22
+ $srcs += Dir.glob(File.join(srcdirpat, "posix/*.c"))
23
+ vpath << "$(srcdir)/posix"
24
+ $defs << " -DEXTIO_WITH_POSIX=1"
25
+ #when have_header("windows.h")
26
+ # $srcs += Dir.glob(File.join(srcdirpat, "win32/*.c"))
27
+ # vpath << "$(srcdir)/win32"
28
+ # $defs << " -DEXTIO_WITH_WIN32=1"
29
+ else
30
+ $stderr.puts <<EOS
31
+ |
32
+ | #{__FILE__}:#{__LINE__}: NOT SUPPORTED PLATFORM.
33
+ |
34
+ EOS
35
+ exit 1
36
+ end
37
+
38
+ create_makefile "ioplus" or exit 1
@@ -0,0 +1,865 @@
1
+ #include <ruby.h>
2
+ #include <ruby/io.h>
3
+ #include <ruby/thread.h>
4
+ #include <errno.h>
5
+ #include <pthread.h>
6
+ #include <fcntl.h>
7
+ #include <sys/stat.h>
8
+
9
+ #if EXTIO_WITH_WIN32
10
+ # include <windows.h>
11
+ #elif EXTIO_WITH_POSIX
12
+ # include <aio.h>
13
+ # if EXTIO_HAVE_KEVENT
14
+ # include <sys/types.h>
15
+ # include <sys/event.h>
16
+ # include <sys/time.h>
17
+ # endif
18
+ #endif
19
+
20
+ enum {
21
+ EXTAIO_READ = 0x00,
22
+ EXTAIO_WRITE = 0x01,
23
+ };
24
+
25
+ struct extaio
26
+ {
27
+ aiocb_t aio;
28
+ VALUE self;
29
+ VALUE notify_block;
30
+ VALUE file;
31
+ VALUE buffer;
32
+ VALUE status; /* converted value of aio_return */
33
+ uint64_t offset;
34
+ int operation; /* EXTAIO_READ, EXTAIO_WRITE */
35
+ #if EXTIO_HAVE_KEVENT
36
+ struct kevent kev;
37
+ #else
38
+ struct extaio *next;
39
+ #endif
40
+ };
41
+
42
+ static pthread_mutex_t extaio_mutex = PTHREAD_MUTEX_INITIALIZER;
43
+ static pthread_cond_t extaio_cond = PTHREAD_COND_INITIALIZER;
44
+
45
+ #if EXTIO_HAVE_KEVENT
46
+ static int extaio_kqueue;
47
+ #else
48
+ static struct extaio *extaio_queue, *extaio_queue_tail;
49
+ #endif
50
+
51
+ static VALUE mIOPlus, mIO, mIOClass, cAIO;
52
+ static VALUE extio_lives;
53
+ static ID IDto_time;
54
+
55
+ static inline struct timespec
56
+ aux_conv_to_timespec(VALUE time)
57
+ {
58
+ struct timespec ts = { 0, 0 };
59
+
60
+ switch (TYPE(time)) {
61
+ case RUBY_T_FIXNUM:
62
+ case RUBY_T_BIGNUM:
63
+ ts.tv_sec = NUM2LL(time);
64
+ return ts;
65
+ case RUBY_T_FLOAT:
66
+ ts.tv_sec = NUM2LL(time);
67
+ ts.tv_nsec = (int64_t)(NUM2DBL(time) * 1000000000) % 1000000000;
68
+ return ts;
69
+ default:
70
+ if (rb_obj_is_kind_of(time, rb_cTime)) {
71
+ return rb_time_timespec(time);
72
+ } else if (rb_respond_to(time, IDto_time)) {
73
+ time = rb_funcall(time, IDto_time, 0);
74
+ if (rb_obj_is_kind_of(time, rb_cTime)) {
75
+ return rb_time_timespec(time);
76
+ } else {
77
+ rb_raise(rb_eTypeError,
78
+ "not convert to time object by ``to_time''");
79
+ }
80
+ } else {
81
+ time = rb_Float(time);
82
+ ts.tv_sec = NUM2LL(time);
83
+ ts.tv_nsec = (int64_t)(NUM2DBL(time) * 1000000000) % 1000000000;
84
+ return ts;
85
+ }
86
+ }
87
+ }
88
+
89
+ static inline int
90
+ ext_io_getfd(VALUE file)
91
+ {
92
+ struct rb_io_t *fp;
93
+ GetOpenFile(file, fp);
94
+ return fp->fd;
95
+ }
96
+
97
+ static inline ssize_t
98
+ ext_getiosize(int d)
99
+ {
100
+ struct stat s;
101
+ if (fstat(d, &s) == 0) {
102
+ return s.st_size;
103
+ } else {
104
+ return -1;
105
+ }
106
+ }
107
+
108
+ static inline struct extaio *
109
+ extaio_refp(VALUE aio)
110
+ {
111
+ struct extaio *p;
112
+ Data_Get_Struct(aio, struct extaio, p);
113
+ return p;
114
+ }
115
+
116
+ static inline struct extaio *
117
+ extaio_ref(VALUE aio)
118
+ {
119
+ struct extaio *p = extaio_refp(aio);
120
+ if (!p) {
121
+ VALUE v = rb_inspect(aio);
122
+ rb_raise(rb_eRuntimeError,
123
+ "invalid reference - %s",
124
+ StringValueCStr(v));
125
+ }
126
+ return p;
127
+ }
128
+
129
+ #if !EXTIO_HAVE_KEVENT
130
+ static void
131
+ extaio_read_notify(union sigval sv)
132
+ {
133
+ /* NOT RUBY MANAGED THREAD! */
134
+
135
+ struct extaio *p = sv.sival_ptr;
136
+
137
+ pthread_mutex_lock(&extaio_mutex);
138
+ if (extaio_queue) {
139
+ extaio_queue_tail = extaio_queue_tail->next = p;
140
+ } else {
141
+ extaio_queue_tail = extaio_queue = p;
142
+ }
143
+ pthread_cond_signal(&extaio_cond);
144
+ pthread_mutex_unlock(&extaio_mutex);
145
+ }
146
+ #endif
147
+
148
+ static void
149
+ extaio_gcmark(struct extaio *p)
150
+ {
151
+ if (p) {
152
+ rb_gc_mark(p->self);
153
+ rb_gc_mark(p->notify_block);
154
+ rb_gc_mark(p->file);
155
+ rb_gc_mark(p->buffer);
156
+ rb_gc_mark(p->status);
157
+ }
158
+ }
159
+
160
+ static void
161
+ extaio_free(struct extaio *p)
162
+ {
163
+ if (p) {
164
+ aio_cancel(ext_io_getfd(p->file), &p->aio);
165
+ const struct aiocb *pp = &p->aio;
166
+ aio_suspend(&pp, 1, NULL); /* FIXME: GC 中に無限待機の可能性あり? */
167
+ free(p);
168
+ }
169
+ }
170
+
171
+ static inline struct extaio *
172
+ extaio_new(int operation, VALUE file, rb_io_t *fp, uint64_t offset, VALUE buf, uint64_t size)
173
+ {
174
+ struct extaio *p;
175
+ VALUE objaio = Data_Make_Struct(cAIO, struct extaio, extaio_gcmark, extaio_free, p);
176
+ rb_str_locktmp(buf);
177
+ p->aio.aio_offset = offset;
178
+ p->aio.aio_buf = RSTRING_PTR(buf);
179
+ p->aio.aio_nbytes = size;
180
+ p->aio.aio_reqprio = 0;
181
+
182
+ #if EXTIO_HAVE_KEVENT
183
+ p->aio.aio_sigevent.sigev_notify = SIGEV_KEVENT;
184
+ p->aio.aio_sigevent.sigev_notify_kqueue = extaio_kqueue;
185
+ p->aio.aio_sigevent.sigev_notify_kevent_flags = EV_ONESHOT;
186
+ #else
187
+ p->aio.aio_sigevent.sigev_notify = SIGEV_THREAD;
188
+ p->aio.aio_sigevent.sigev_notify_function = extaio_read_notify;
189
+ p->aio.aio_sigevent.sigev_notify_attributes = NULL;
190
+ #endif
191
+
192
+ p->aio.aio_fildes = fp->fd;
193
+ p->aio.aio_sigevent.sigev_value.sival_ptr = p;
194
+
195
+ p->notify_block = rb_block_given_p() ? rb_block_proc() : Qnil;
196
+ p->operation = operation;
197
+ p->self = objaio;
198
+ p->file = file;
199
+ p->buffer = buf;
200
+ p->status = Qnil;
201
+ p->offset = offset;
202
+
203
+ #if EXTIO_HAVE_KEVENT
204
+ //EV_SET(&p->kev, fp->fd, EVFILT_AIO, EV_ADD, 0, 0, NULL);
205
+ //kevent(extaio_kqueue, &p->kev, 1, NULL, 0, NULL);
206
+ #else
207
+ # error IMPLEMENT ME!
208
+ #endif
209
+
210
+ return p;
211
+ }
212
+
213
+ static void
214
+ extio_aio_read_scanargs(int argc, VALUE argv[], int d, off_t *offset, size_t *size, VALUE *buf)
215
+ {
216
+ switch (argc) {
217
+ case 1:
218
+ *offset = NUM2OFFT(argv[0]);
219
+ // TODO: エラーの場合 (size_t)-1 が返ってくるけれど、メモリ要求でパンクする
220
+ // TODO: これではエラーメッセージがわかりにくいからどうにかする
221
+ *size = ext_getiosize(d);
222
+ *buf = rb_str_buf_new(*size);
223
+ break;
224
+ case 2:
225
+ *offset = NUM2OFFT(argv[0]);
226
+ *size = NUM2SIZET(argv[1]);
227
+ *buf = rb_str_buf_new(*size);
228
+ break;
229
+ case 3:
230
+ *offset = NUM2OFFT(argv[0]);
231
+ *size = NUM2SIZET(argv[1]);
232
+ *buf = argv[2];
233
+ break;
234
+ default:
235
+ rb_error_arity(argc, 1, 3);
236
+ break;
237
+ }
238
+
239
+ rb_check_type(*buf, RUBY_T_STRING);
240
+ if (OBJ_FROZEN(*buf)) {
241
+ rb_raise(rb_eTypeError,
242
+ "wrong immutable object for ``buffer''");
243
+ }
244
+ rb_str_resize(*buf, *size);
245
+ rb_str_set_len(*buf, 0);
246
+ }
247
+
248
+ /*
249
+ * call-seq:
250
+ * aio_read(offset) -> aio_object
251
+ * aio_read(offset) { |aio_obj| ... } -> aio_object
252
+ * aio_read(offset, size, buf = nil) -> aio_object
253
+ * aio_read(offset, size, buf = nil) { |aio_obj| ... } -> aio_object
254
+ *
255
+ * I/O オブジェクトに対して非同期読み込みを行います。
256
+ *
257
+ * システムに対して読み込み要求を出すだけなので、すぐに制御が戻ります。
258
+ *
259
+ * この時の戻り値は、読み込み要求の状態を保持するオブジェクトです。
260
+ *
261
+ * このオブジェクトを用いて待機する、処理の完了状態を取得するなどを行うことが出来ます。
262
+ *
263
+ * ブロックを指定した場合、読み込みが完了した時に評価されます。
264
+ *
265
+ * ブロック引数と戻り値は同じオブジェクトです。
266
+ *
267
+ * ioplus ライブラリの実装上の問題として aio_read(2) システムコールよりも大きな遅延があります。
268
+ *
269
+ * 数ミリ秒単位の遅延すら許容できないアプリケーションでは使い物にならないことに注意して下さい。
270
+ *
271
+ * また、aio_read を呼んだスレッドとは異なるスレッドがブロックを評価します。
272
+ *
273
+ * [RETURN]
274
+ * IOPlus::AIO インスタンスです。
275
+ *
276
+ * [YIELD aio_obj]
277
+ * IOPlus::AIO インスタンスです。
278
+ */
279
+ static VALUE
280
+ extio_aio_read(int argc, VALUE argv[], VALUE file)
281
+ {
282
+ rb_check_type(file, RUBY_T_FILE);
283
+ struct rb_io_t *fp;
284
+ GetOpenFile(file, fp);
285
+
286
+ off_t offset;
287
+ size_t size;
288
+ VALUE buf;
289
+ extio_aio_read_scanargs(argc, argv, fp->fd, &offset, &size, &buf);
290
+
291
+ struct extaio *p = extaio_new(EXTAIO_READ, file, fp, offset, buf, size);
292
+
293
+ if (aio_read(&p->aio) != 0) {
294
+ DATA_PTR(p->self) = NULL;
295
+ free(p);
296
+ VALUE v = rb_inspect(file);
297
+ rb_sys_fail_str(v);
298
+ }
299
+
300
+ rb_ary_push(extio_lives, p->self);
301
+
302
+ return p->self;
303
+ }
304
+
305
+ static VALUE
306
+ extio_aio_write(VALUE file, VALUE off, VALUE buf)
307
+ {
308
+ rb_check_type(file, RUBY_T_FILE);
309
+ struct rb_io_t *fp;
310
+ GetOpenFile(file, fp);
311
+
312
+ off_t offset = NUM2OFFT(off);
313
+ rb_check_type(buf, RUBY_T_STRING);
314
+ size_t size = RSTRING_LEN(buf);
315
+
316
+ struct extaio *p = extaio_new(EXTAIO_WRITE, file, fp, offset, buf, size);
317
+
318
+ if (aio_write(&p->aio) != 0) {
319
+ DATA_PTR(p->self) = NULL;
320
+ free(p);
321
+ VALUE v = rb_inspect(file);
322
+ rb_sys_fail_str(v);
323
+ }
324
+
325
+ rb_ary_push(extio_lives, p->self);
326
+
327
+ return p->self;
328
+ }
329
+
330
+ /*
331
+ * このファイルに関するすべての非同期処理の取り消しをシステムに要求します。
332
+ *
333
+ * 常に取り消されるわけではないことに注意してください。
334
+ */
335
+ static VALUE
336
+ extio_aio_cancel(VALUE file)
337
+ {
338
+ int fd = ext_io_getfd(file);
339
+ int status = aio_cancel(fd, NULL);
340
+ switch (status) {
341
+ case AIO_CANCELED:
342
+ return ID2SYM(rb_intern("canceled"));
343
+ case AIO_NOTCANCELED:
344
+ return ID2SYM(rb_intern("notcanceled"));
345
+ case AIO_ALLDONE:
346
+ return ID2SYM(rb_intern("alldone"));
347
+ case -1:
348
+ rb_sys_fail_str(rb_inspect(file));
349
+ default:
350
+ return rb_sprintf("unknown-status:0x%02x", status);
351
+ }
352
+ }
353
+
354
+ static VALUE
355
+ extaio_validate(VALUE aio)
356
+ {
357
+ if (extaio_refp(aio)) {
358
+ return aio;
359
+ } else {
360
+ return Qnil;
361
+ }
362
+ }
363
+
364
+ static VALUE
365
+ extaio_file(VALUE aio)
366
+ {
367
+ return extaio_ref(aio)->file;
368
+ }
369
+
370
+ static inline VALUE
371
+ ext_aio_operation_inspect(struct extaio *p)
372
+ {
373
+ int operation = p->operation;
374
+ switch (operation) {
375
+ case EXTAIO_READ:
376
+ return ID2SYM(rb_intern("read"));
377
+ case EXTAIO_WRITE:
378
+ return ID2SYM(rb_intern("write"));
379
+ default:
380
+ return rb_sprintf("unknown:0x%02x", operation);
381
+ }
382
+ }
383
+
384
+ static VALUE
385
+ extaio_operation(VALUE aio)
386
+ {
387
+ return ext_aio_operation_inspect(extaio_ref(aio));
388
+ }
389
+
390
+ static VALUE
391
+ extaio_buffer(VALUE aio)
392
+ {
393
+ return extaio_ref(aio)->buffer;
394
+ }
395
+
396
+ static VALUE
397
+ extaio_offset(VALUE aio)
398
+ {
399
+ return ULL2NUM(extaio_ref(aio)->offset);
400
+ }
401
+
402
+ static VALUE
403
+ extaio_to_s(VALUE aio)
404
+ {
405
+ return rb_sprintf("%s:%p", rb_obj_classname(aio), (void *)aio);
406
+ }
407
+
408
+ static VALUE
409
+ extaio_inspect(VALUE aio)
410
+ {
411
+ struct extaio *p = extaio_refp(aio);
412
+ if (!p) {
413
+ return rb_sprintf("#<%s:%p **INVALID-REFERENCE**>",
414
+ rb_obj_classname(aio), (void *)aio);
415
+ } else {
416
+ VALUE ioobj = rb_inspect(p->file);
417
+ int bufsize = RSTRING_LEN(p->buffer);
418
+ VALUE retval = rb_inspect(p->status);
419
+ VALUE operation = rb_String(ext_aio_operation_inspect(p));
420
+ return rb_sprintf("#<%s:%p %s %s offset=%d buffer.bytesize=%d return=%s>",
421
+ rb_obj_classname(aio), (void *)aio,
422
+ StringValueCStr(operation), StringValueCStr(ioobj),
423
+ (int)p->offset, bufsize, StringValueCStr(retval));
424
+ }
425
+ }
426
+
427
+ struct extaio_suspend_wait_args
428
+ {
429
+ struct extaio *aio;
430
+ int64_t timeout;
431
+ };
432
+
433
+ enum {
434
+ EXTAIO_POLLING_INTERVAL_NSEC = 100 * 1000 * 1000,
435
+ EXTAIO_POLLING_INTERVAL_INFINITY = -1ll,
436
+ EXTAIO_DONE = 0,
437
+ EXTAIO_TIMEDOUT = 1,
438
+ EXTAIO_ERROR = -1,
439
+ };
440
+
441
+ enum {
442
+ EXT_1SEC_TO_NSEC = 1 * 1000 * 1000 * 1000,
443
+ };
444
+
445
+ static int
446
+ extaio_suspend_wait_ungvl(struct extaio_suspend_wait_args *args)
447
+ {
448
+ /*
449
+ * aio_suspend を中断するにはシグナルを必要とするが、ruby と
450
+ * 適合するかどうかわからないためポーリング処理する。
451
+ * このため、指定された timeout よりも大きな待機時間となる。
452
+ * 現時点では 100 ms 単位で行う。
453
+ */
454
+ struct timespec timeout = { 0, EXTAIO_POLLING_INTERVAL_NSEC };
455
+ int64_t timerest = args->timeout;
456
+ VALUE thcur = rb_thread_current();
457
+
458
+ for (;;) {
459
+ if (timerest >= 0 && timerest < EXTAIO_POLLING_INTERVAL_NSEC) {
460
+ timeout.tv_sec = 0;
461
+ timeout.tv_nsec = timerest;
462
+ }
463
+ int state = aio_suspend((const struct aiocb *const *)&args->aio->aio, 1, &timeout);
464
+ if (rb_thread_interrupted(thcur)) { return EXTAIO_ERROR; }
465
+ if (state == 0) { break; }
466
+ if (state == -1) {
467
+ switch (errno) {
468
+ case EAGAIN:
469
+ if (timerest >= 0 && timerest <= EXTAIO_POLLING_INTERVAL_NSEC) {
470
+ return EXTAIO_TIMEDOUT;
471
+ } else {
472
+ timerest -= EXTAIO_POLLING_INTERVAL_NSEC;
473
+ continue;
474
+ }
475
+ //case EINVAL:
476
+ //case EINTR:
477
+ //case ENOSYS:
478
+ default:
479
+ return EXTAIO_ERROR;
480
+ }
481
+ }
482
+ }
483
+
484
+ /*
485
+ * 非同期 I/O 要求が正常完了して aio_suspend から戻ってきたが、
486
+ * ruby 側の処理が完了していないかもしれないため待機する。
487
+ */
488
+
489
+ int status = EXTAIO_DONE;
490
+
491
+ timeout.tv_sec = 0;
492
+ timeout.tv_nsec = EXTAIO_POLLING_INTERVAL_NSEC;
493
+
494
+ if (pthread_mutex_lock(&extaio_mutex) == 0) {
495
+ while (NIL_P(args->aio->status)) {
496
+ if (timerest >= 0 && timerest <= EXTAIO_POLLING_INTERVAL_NSEC) {
497
+ timeout.tv_nsec = timerest;
498
+ }
499
+
500
+ if (rb_thread_interrupted(thcur)) {
501
+ status = EXTAIO_ERROR;
502
+ break;
503
+ }
504
+ int s = pthread_cond_timedwait(&extaio_cond, &extaio_mutex, &timeout);
505
+ if (s == 0) {
506
+ break;
507
+ } else if (s == ETIMEDOUT) {
508
+ if (timerest <= EXTAIO_POLLING_INTERVAL_NSEC) {
509
+ status = EXTAIO_TIMEDOUT;
510
+ break;
511
+ }
512
+ timerest -= EXTAIO_POLLING_INTERVAL_NSEC;
513
+ } else if (s == EINTR) {
514
+ status = EXTAIO_ERROR;
515
+ errno = s;
516
+ break;
517
+ } else {
518
+ /* TODO: 仕様の上ではなさそうだけれども、どうするべきか考えておこう */
519
+ status = EXTAIO_ERROR;
520
+ break;
521
+ }
522
+ }
523
+ pthread_mutex_unlock(&extaio_mutex);
524
+ }
525
+
526
+ return status;
527
+ }
528
+
529
+ static inline VALUE
530
+ extaio_suspend_wait(struct extaio *aio, int64_t timeout_nsec)
531
+ {
532
+ if (!NIL_P(aio->status)) {
533
+ // すでに完了しているため待機の必要はない
534
+ return aio->self;
535
+ }
536
+
537
+ struct extaio_suspend_wait_args args = {
538
+ .aio = aio,
539
+ .timeout = timeout_nsec,
540
+ };
541
+ int s = (int)rb_thread_call_without_gvl((void *(*)(void *))extaio_suspend_wait_ungvl,
542
+ &args, RUBY_UBF_PROCESS, NULL);
543
+
544
+ if (s == 0) {
545
+ return aio->self;
546
+ } else if (s == EXTAIO_TIMEDOUT) {
547
+ return Qnil;
548
+ } else if (s == EXTAIO_ERROR) {
549
+ VALUE v = rb_inspect(aio->self);
550
+ rb_sys_fail_str(v);
551
+ } else {
552
+ rb_bug("%s:%d:%s: extaio_suspend_wait_ungvl returned not handled code (0x%04x)",
553
+ __FILE__, __LINE__, __func__, s);
554
+ }
555
+ }
556
+
557
+ /*
558
+ * call-seq:
559
+ * suspend(timeout = nil) -> self or nil
560
+ */
561
+ static VALUE
562
+ extaio_suspend(int argc, VALUE argv[], VALUE aio)
563
+ {
564
+ VALUE timeout;
565
+ switch (argc) {
566
+ case 0:
567
+ timeout = Qnil;
568
+ break;
569
+ case 1:
570
+ timeout = argv[0];
571
+ break;
572
+ default:
573
+ rb_error_arity(argc, 0, 1);
574
+ timeout = Qnil; /* clear warnings for compiler */
575
+ }
576
+
577
+ if (NIL_P(timeout)) {
578
+ return extaio_suspend_wait(extaio_ref(aio), EXTAIO_POLLING_INTERVAL_INFINITY);
579
+ } else {
580
+ struct timespec time = aux_conv_to_timespec(timeout);
581
+ int64_t t = time.tv_sec * EXT_1SEC_TO_NSEC + time.tv_nsec;
582
+ if (t / EXT_1SEC_TO_NSEC != time.tv_sec) {
583
+ t = EXTAIO_POLLING_INTERVAL_INFINITY;
584
+ }
585
+ return extaio_suspend_wait(extaio_ref(aio), t);
586
+ }
587
+ }
588
+
589
+ static VALUE
590
+ extaio_fsync(VALUE aio)
591
+ {
592
+ struct extaio *p = extaio_ref(aio);
593
+ if (aio_fsync(O_SYNC, &p->aio) != 0) {
594
+ VALUE v = rb_inspect(p->file);
595
+ rb_sys_fail_str(v);
596
+ }
597
+
598
+ return aio;
599
+ }
600
+
601
+ #if 0
602
+ static VALUE
603
+ extaio_fdatasync(VALUE aio)
604
+ {
605
+ struct extaio *p = extaio_ref(aio);
606
+ if (aio_fsync(O_DSYNC, p->aio) != 0) {
607
+ VALUE v = rb_inspect(p->file);
608
+ rb_sys_fail_str(v);
609
+ }
610
+
611
+ return aio;
612
+ }
613
+ #endif
614
+
615
+ /*
616
+ * call-seq:
617
+ * status -> object
618
+ *
619
+ * <tt>aio_return</tt> の戻り値です。
620
+ *
621
+ * 正常完了時は読み込みまたは書き込みデータサイズを返します。
622
+ *
623
+ * 処理中であれば nil を返します。
624
+ *
625
+ * 異常完了の場合は errno に対する Errno::EXXX クラスを返します。
626
+ */
627
+ static VALUE
628
+ extaio_status(VALUE aio)
629
+ {
630
+ return extaio_ref(aio)->status;
631
+ }
632
+
633
+ /*
634
+ * 非同期処理の取り消しをシステムに要求します。
635
+ *
636
+ * 常に取り消されるわけではないことに注意してください。
637
+ */
638
+ static VALUE
639
+ extaio_cancel(VALUE aio)
640
+ {
641
+ struct extaio *p = extaio_ref(aio);
642
+ int fd = ext_io_getfd(p->file);
643
+ int status = aio_cancel(fd, &p->aio);
644
+ switch (status) {
645
+ case AIO_CANCELED:
646
+ return ID2SYM(rb_intern("canceled"));
647
+ case AIO_NOTCANCELED:
648
+ return ID2SYM(rb_intern("notcanceled"));
649
+ case AIO_ALLDONE:
650
+ return ID2SYM(rb_intern("alldone"));
651
+ case -1:
652
+ rb_sys_fail_str(rb_inspect(p->file));
653
+ default:
654
+ return rb_sprintf("unknown-status:0x%02x", status);
655
+ }
656
+ }
657
+
658
+ /*
659
+ * 正常値であれば自身を返す。
660
+ *
661
+ * 非同期処理中であれば nil を返す。
662
+ *
663
+ * エラーがあった場合は errno の例外を発生させる。
664
+ */
665
+ static VALUE
666
+ extaio_error(VALUE aio)
667
+ {
668
+ struct extaio *p = extaio_ref(aio);
669
+ int err = aio_error(&p->aio);
670
+ switch (err) {
671
+ case 0:
672
+ return p->file;
673
+ case EINPROGRESS:
674
+ return Qnil;
675
+ case -1:
676
+ {
677
+ VALUE v = rb_inspect(p->file);
678
+ rb_sys_fail_str(v);
679
+ }
680
+ /* fall through */
681
+ default:
682
+ {
683
+ errno = err;
684
+ VALUE v = rb_inspect(p->file);
685
+ rb_sys_fail_str(v);
686
+ }
687
+ //return LL2NUM(err);
688
+ }
689
+ }
690
+
691
+ struct extaio_notify_dequeue_args
692
+ {
693
+ struct kevent kevent;
694
+ const struct timespec *timeout;
695
+ //int status;
696
+ };
697
+
698
+ static int
699
+ extaio_notify_dequeue_ungvl(struct extaio_notify_dequeue_args *args)
700
+ {
701
+ return kevent(extaio_kqueue, NULL, 0, &args->kevent, 1, args->timeout);
702
+ }
703
+
704
+ static inline struct extaio *
705
+ extaio_notify_dequeue(const struct timespec *timeout)
706
+ {
707
+ struct extaio_notify_dequeue_args args = {
708
+ .timeout = timeout,
709
+ };
710
+ int s = (int)rb_thread_call_without_gvl((void *(*)(void *))extaio_notify_dequeue_ungvl,
711
+ &args, RUBY_UBF_PROCESS, NULL);
712
+ if (s == 0) {
713
+ return NULL;
714
+ } else if (s > 0) {
715
+ return (struct extaio *)args.kevent.ident;
716
+ } else {
717
+ rb_sys_fail("kevent failed (signaled event)");
718
+ }
719
+ }
720
+
721
+ static void *
722
+ extaio_notify_cond_broadcast_ungvl(void *unused__)
723
+ {
724
+ (void)unused__;
725
+
726
+ if (pthread_mutex_lock(&extaio_mutex) == 0) {
727
+ pthread_cond_broadcast(&extaio_cond);
728
+ pthread_mutex_unlock(&extaio_mutex);
729
+ }
730
+
731
+ return NULL;
732
+ }
733
+
734
+ static inline void
735
+ extaio_notify_cond_broadcast(void)
736
+ {
737
+ rb_thread_call_without_gvl(extaio_notify_cond_broadcast_ungvl,
738
+ NULL, RUBY_UBF_PROCESS, NULL);
739
+ }
740
+
741
+ static VALUE
742
+ extaio_notify_main(VALUE unused__)
743
+ {
744
+ (void)unused__;
745
+
746
+ struct timespec timeout = { 1, 0 };
747
+ struct extaio *aiop = extaio_notify_dequeue(&timeout);
748
+
749
+ if (aiop) {
750
+ VALUE aio = aiop->self;
751
+ rb_str_unlocktmp(aiop->buffer);
752
+ rb_ary_delete(extio_lives, aio);
753
+
754
+ int s = aio_return(&aiop->aio);
755
+ if (s < 0) {
756
+ aiop->status = rb_obj_class(rb_syserr_new_str(errno, Qnil));
757
+ if (aiop->operation == EXTAIO_READ) {
758
+ rb_str_set_len(aiop->buffer, 0);
759
+ }
760
+ } else {
761
+ aiop->status = INT2FIX(s);
762
+ if (aiop->operation == EXTAIO_READ) {
763
+ rb_str_set_len(aiop->buffer, s);
764
+ }
765
+ }
766
+
767
+ VALUE notify_block = aiop->notify_block;
768
+ if (!NIL_P(notify_block)) {
769
+ // rb_proc_call は array オブジェクトを生成する必要があるため
770
+ // rb_proc_call_with_block に置き換えている
771
+ rb_proc_call_with_block(notify_block, 1, &aio, Qnil);
772
+ }
773
+
774
+ extaio_notify_cond_broadcast();
775
+ }
776
+
777
+ return 0;
778
+ }
779
+
780
+ static VALUE
781
+ extaio_notify(VALUE unused__)
782
+ {
783
+ (void)unused__;
784
+
785
+ int state;
786
+ for (;;) {
787
+ rb_set_errinfo(Qnil);
788
+ rb_protect(extaio_notify_main, 0, &state);
789
+ if (state != 0) {
790
+ //fprintf(stderr, "%s:%d:%s: rb_protect was returned state=%d (0x%08x)\n",
791
+ // __FILE__, __LINE__, __func__, state, state);
792
+ VALUE e = rb_errinfo();
793
+ if (!NIL_P(e)) {
794
+ if (TYPE(e) == RUBY_T_FIXNUM) {
795
+ rb_jump_tag(state);
796
+ }
797
+ VALUE v = rb_inspect(e);
798
+ //fprintf(stderr, "%s:%d:%s: %s\n",
799
+ // __FILE__, __LINE__, __func__, StringValueCStr(v));
800
+ rb_funcall(rb_thread_main(), rb_intern("raise"), 1, e);
801
+ rb_set_errinfo(Qnil);
802
+ }
803
+ //rb_jump_tag(state);
804
+ }
805
+ }
806
+ }
807
+
808
+ void
809
+ Init_ioplus(void)
810
+ {
811
+ #if EXTIO_HAVE_KEVENT
812
+ extaio_kqueue = kqueue();
813
+ #else
814
+ # error NOT IMPLEMENTED WITHOUT EXTIO_HAVE_KEVENT
815
+ #endif
816
+
817
+ IDto_time = rb_intern_const("to_time");
818
+
819
+ mIOPlus = rb_define_module("IOPlus");
820
+
821
+ extio_lives = rb_ary_new();
822
+ rb_gc_register_address(&extio_lives);
823
+
824
+ VALUE notify_thread = rb_thread_create(extaio_notify, 0);
825
+
826
+ /*
827
+ * This is extension module for IO.
828
+ */
829
+ mIO = rb_define_module_under(mIOPlus, "IO");
830
+ rb_include_module(rb_cIO, mIO);
831
+ rb_define_method(mIO, "aio_cancel", RUBY_METHOD_FUNC(extio_aio_cancel), 0);
832
+ rb_define_method(mIO, "aio_read", RUBY_METHOD_FUNC(extio_aio_read), -1);
833
+ rb_define_method(mIO, "aio_write", RUBY_METHOD_FUNC(extio_aio_write), 2);
834
+ rb_define_alias(mIO, "aread", "aio_read");
835
+ rb_define_alias(mIO, "awrite", "aio_write");
836
+
837
+ /*
838
+ * This is extension module for IO class.
839
+ */
840
+ mIOClass = rb_define_module_under(mIOPlus, "IOClass");
841
+ rb_extend_object(rb_cIO, mIOClass);
842
+
843
+ /*
844
+ * This is the request context of asynchronous I/O for <tt>IO#aio_read</tt> and <tt>IO#aio_write</tt>.
845
+ *
846
+ * The instance of this class can not directly created by <tt>IOPlus::AIO.new</tt>.
847
+ */
848
+ cAIO = rb_define_class_under(mIOPlus, "AIO", rb_cObject);
849
+ rb_undef_alloc_func(cAIO);
850
+ rb_define_method(cAIO, "to_s", RUBY_METHOD_FUNC(extaio_to_s), 0);
851
+ rb_define_method(cAIO, "inspect", RUBY_METHOD_FUNC(extaio_inspect), 0);
852
+ rb_define_method(cAIO, "validate", RUBY_METHOD_FUNC(extaio_validate), 0);
853
+ rb_define_method(cAIO, "file", RUBY_METHOD_FUNC(extaio_file), 0);
854
+ rb_define_method(cAIO, "operation", RUBY_METHOD_FUNC(extaio_operation), 0);
855
+ rb_define_method(cAIO, "buffer", RUBY_METHOD_FUNC(extaio_buffer), 0);
856
+ rb_define_method(cAIO, "offset", RUBY_METHOD_FUNC(extaio_offset), 0);
857
+ rb_define_method(cAIO, "suspend", RUBY_METHOD_FUNC(extaio_suspend), -1);
858
+ rb_define_method(cAIO, "fsync", RUBY_METHOD_FUNC(extaio_fsync), 0);
859
+ #if 0
860
+ rb_define_method(cAIO, "fdatasync", RUBY_METHOD_FUNC(extaio_fdatasync), 0);
861
+ #endif
862
+ rb_define_method(cAIO, "status", RUBY_METHOD_FUNC(extaio_status), 0);
863
+ rb_define_method(cAIO, "cancel", RUBY_METHOD_FUNC(extaio_cancel), 0);
864
+ rb_define_method(cAIO, "error", RUBY_METHOD_FUNC(extaio_error), 0);
865
+ }
@@ -0,0 +1,18 @@
1
+ GEMSTUB = Gem::Specification.new do |s|
2
+ s.name = "ioplus"
3
+ s.version = "0.0.1.PROTOTYPE"
4
+ s.summary = "Append IO#aio_read/aio_write methods. These provide similar functionality to aio_read(2)/aio_write(2)."
5
+ s.description = <<EOS
6
+ "ioplus" is appended IO#aio_read/aio_write methods. These provide similar functionality to aio_read(2)/aio_write(2).
7
+
8
+ This library need posix aio(4) and kqueue(2). Tested on FreeBSD 10.1 only.
9
+ EOS
10
+ s.license = "2-clause BSD License"
11
+ s.author = "dearblue"
12
+ s.email = "dearblue@users.sourceforge.jp"
13
+ s.homepage = "http://sourceforge.jp/projects/rutsubo/"
14
+
15
+ s.required_ruby_version = ">= 2.0"
16
+ s.add_development_dependency "rspec", "~> 2.14"
17
+ s.add_development_dependency "rake", "~> 10.0"
18
+ end
@@ -0,0 +1,47 @@
1
+
2
+ require "rbconfig"
3
+
4
+ ver = RbConfig::CONFIG["ruby_version"]
5
+ soname = File.basename(__FILE__, ".rb") << ".so"
6
+ lib = File.join(File.dirname(__FILE__), ver, soname)
7
+ if File.file?(lib)
8
+ require_relative File.join(ver, soname)
9
+ else
10
+ require_relative soname
11
+ end
12
+
13
+ module IOPlus
14
+ module IOClass
15
+ def aio_suspend(*aio, timeout: nil)
16
+ aio.each { |x| x.suspend(timeout) }
17
+ nil
18
+ end
19
+ end
20
+
21
+ class AIO
22
+ def pretty_print(q)
23
+ return q.text("#<#{to_s} **INVALID-REFERENCE**>") unless validate
24
+
25
+ q.group(2, "#<#{to_s}") do
26
+ q.breakable " "
27
+ q.text "operation=#{operation},"
28
+ q.breakable " "
29
+ q.text "file="
30
+ q.pp file
31
+ q.text ","
32
+ q.breakable " "
33
+ q.text "offset="
34
+ q.pp offset
35
+ q.text ","
36
+ q.breakable " "
37
+ q.text "buffer.bytesize="
38
+ q.pp buffer.bytesize
39
+ q.text ","
40
+ q.breakable " "
41
+ q.text "status="
42
+ q.pp status
43
+ q.text ">"
44
+ end
45
+ end
46
+ end
47
+ end
metadata ADDED
@@ -0,0 +1,90 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ioplus
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1.PROTOTYPE
5
+ platform: ruby
6
+ authors:
7
+ - dearblue
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-02-08 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rspec
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.14'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2.14'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ description: |
42
+ "ioplus" is appended IO#aio_read/aio_write methods. These provide similar functionality to aio_read(2)/aio_write(2).
43
+
44
+ This library need posix aio(4) and kqueue(2). Tested on FreeBSD 10.1 only.
45
+ email: dearblue@users.sourceforge.jp
46
+ executables: []
47
+ extensions:
48
+ - ext/extconf.rb
49
+ extra_rdoc_files:
50
+ - LICENSE.md
51
+ - README.ja.md
52
+ - README.md
53
+ - ext/extio.c
54
+ - lib/ioplus.rb
55
+ files:
56
+ - LICENSE.md
57
+ - README.ja.md
58
+ - README.md
59
+ - Rakefile
60
+ - examples/aio_read.rb
61
+ - ext/extconf.rb
62
+ - ext/extio.c
63
+ - gemstub.rb
64
+ - lib/ioplus.rb
65
+ homepage: http://sourceforge.jp/projects/rutsubo/
66
+ licenses:
67
+ - 2-clause BSD License
68
+ metadata: {}
69
+ post_install_message:
70
+ rdoc_options: []
71
+ require_paths:
72
+ - lib
73
+ required_ruby_version: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - ">="
76
+ - !ruby/object:Gem::Version
77
+ version: '2.0'
78
+ required_rubygems_version: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">"
81
+ - !ruby/object:Gem::Version
82
+ version: 1.3.1
83
+ requirements: []
84
+ rubyforge_project:
85
+ rubygems_version: 2.4.5
86
+ signing_key:
87
+ specification_version: 4
88
+ summary: Append IO#aio_read/aio_write methods. These provide similar functionality
89
+ to aio_read(2)/aio_write(2).
90
+ test_files: []