ioplus 0.0.1.PROTOTYPE
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE.md +35 -0
- data/README.ja.md +20 -0
- data/README.md +24 -0
- data/Rakefile +143 -0
- data/examples/aio_read.rb +28 -0
- data/ext/extconf.rb +38 -0
- data/ext/extio.c +865 -0
- data/gemstub.rb +18 -0
- data/lib/ioplus.rb +47 -0
- metadata +90 -0
checksums.yaml
ADDED
@@ -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
|
data/LICENSE.md
ADDED
@@ -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
|
data/README.ja.md
ADDED
@@ -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`` などのメソッドを用いて下さい。
|
data/README.md
ADDED
@@ -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
|
+
```
|
data/Rakefile
ADDED
@@ -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
|
data/ext/extconf.rb
ADDED
@@ -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
|
data/ext/extio.c
ADDED
@@ -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
|
+
}
|
data/gemstub.rb
ADDED
@@ -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
|
data/lib/ioplus.rb
ADDED
@@ -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: []
|