eio 0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. data/.gitignore +8 -0
  2. data/COPYING +502 -0
  3. data/LICENSE +16 -0
  4. data/README.rdoc +201 -0
  5. data/Rakefile +48 -0
  6. data/bench/eventmachine.rb +134 -0
  7. data/eio.gemspec +17 -0
  8. data/ext/eio/eio_ext.c +1447 -0
  9. data/ext/eio/extconf.rb +11 -0
  10. data/ext/libeio/CVS/Entries +13 -0
  11. data/ext/libeio/CVS/Repository +1 -0
  12. data/ext/libeio/CVS/Root +1 -0
  13. data/ext/libeio/Changes +40 -0
  14. data/ext/libeio/LICENSE +36 -0
  15. data/ext/libeio/Makefile +692 -0
  16. data/ext/libeio/Makefile.am +15 -0
  17. data/ext/libeio/Makefile.in +692 -0
  18. data/ext/libeio/aclocal.m4 +8937 -0
  19. data/ext/libeio/autogen.sh +3 -0
  20. data/ext/libeio/autom4te.cache/output.0 +13871 -0
  21. data/ext/libeio/autom4te.cache/output.1 +13867 -0
  22. data/ext/libeio/autom4te.cache/requests +275 -0
  23. data/ext/libeio/autom4te.cache/traces.0 +2384 -0
  24. data/ext/libeio/autom4te.cache/traces.1 +621 -0
  25. data/ext/libeio/config.guess +1501 -0
  26. data/ext/libeio/config.h +122 -0
  27. data/ext/libeio/config.h.in +121 -0
  28. data/ext/libeio/config.status +2035 -0
  29. data/ext/libeio/config.sub +1705 -0
  30. data/ext/libeio/configure +13867 -0
  31. data/ext/libeio/configure.ac +22 -0
  32. data/ext/libeio/demo.c +194 -0
  33. data/ext/libeio/eio.3 +3428 -0
  34. data/ext/libeio/eio.c +2075 -0
  35. data/ext/libeio/eio.h +336 -0
  36. data/ext/libeio/eio.pod +303 -0
  37. data/ext/libeio/install-sh +520 -0
  38. data/ext/libeio/libeio.m4 +156 -0
  39. data/ext/libeio/libtool +8890 -0
  40. data/ext/libeio/ltmain.sh +8406 -0
  41. data/ext/libeio/missing +376 -0
  42. data/ext/libeio/stamp-h1 +1 -0
  43. data/ext/libeio/xthread.h +168 -0
  44. data/lib/eio.rb +9 -0
  45. data/lib/eio/eventmachine.rb +24 -0
  46. data/lib/eio/middleware.rb +21 -0
  47. data/test/test_eio.rb +1161 -0
  48. data/test/test_eventmachine.rb +23 -0
  49. data/test/test_middleware.rb +20 -0
  50. metadata +148 -0
data/LICENSE ADDED
@@ -0,0 +1,16 @@
1
+ eio is copyrighted Free Software by all contributors, see logs in
2
+ revision control for names and email addresses of all of them.
3
+
4
+ You can redistribute it and/or modify it under the terms of the GNU
5
+ Lesser General Public License as published by the Free Software Foundation,
6
+ version 2.1 or later {LGPLv2.1}[http://www.gnu.org/licenses/lgpl-2.1.txt]
7
+ (see link:COPYING).
8
+
9
+ eio is distributed in the hope that it will be useful, but WITHOUT
10
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
12
+ License for more details.
13
+
14
+ You should have received a copy of the GNU Lesser General Public License
15
+ along with this library; if not, write to the Free Software
16
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
@@ -0,0 +1,201 @@
1
+ = eio - a libeio wrapper for Ruby
2
+
3
+ (c) 2011 Lourens Naudé (methodmissing)
4
+
5
+ http://github.com/methodmissing/eio
6
+
7
+ == Why you may need this
8
+
9
+ Changing a file descriptor that references a normal file to non-blocking mode (O_NONBLOCK flag) have
10
+ absolutely no effect. Regular files are always readable and always writable. Readability and writability
11
+ checks will also succeed immediately. Such operations can however still block for a undefined amount of time - busy disks, slow disks (EBS) etc. and usually also include a chunk of disk seek time as well
12
+ Manipulating directories, stat'ing files, changing permissions, ownership etc. is only defined through
13
+ synchronous APIs. This is a major drawback for environments where throughput is critical and a high
14
+ degree of interactivity is demanded at all times.
15
+
16
+ == How it works
17
+
18
+ A number of OS (pthread) threads is started to execute blocking I/O requests and signal their completion.
19
+ The I/O operations will still block, but you're application's able to do something else and will be
20
+ notified of completion status sometime in the future. This library wraps libeio
21
+ (http://software.schmorp.de/pkg/libeio.html), which also powers node.js's FS module and supports both
22
+ Ruby MRI 1.8 and 1.9.
23
+
24
+ It's designed to run in a single threaded environment and libeio will manage a pool of OS threads,
25
+ effectively scheduling out I/O ops across multiple cores. This is the same pattern very common in
26
+ implementations of the Reactor Pattern (http://en.wikipedia.org/wiki/Reactor_pattern, Eventmachine) where
27
+ I/O requests and callbacks are always submitted and handled on the reactor thread. This library thus
28
+ naturally fits into event driven applications and exposes a file descriptor that can wake up an event
29
+ loop when readable and execute callbacks for completed requests on the reactor thread as well.
30
+
31
+ Event loop integration is however much closer to the Proactor Pattern (http://en.wikipedia.org/wiki/Proactor_pattern).
32
+ The I/O multiplexer in a Reactor implementation merely notifies of file descriptor state changes - a
33
+ handler is still responsible for reading or writing data on the reactor thread. Callbacks for file system
34
+ and other blocking system calls wrapped by this library receive results as arguments - there's nothing
35
+ else to do. Nothing to read, nothing to write, no system calls or other context switches. In other words,
36
+ the Reactor pattern asynchronously notify of state changes, but act on those synchronously, on the
37
+ Reactor thread, which incurs some processing overhead.
38
+
39
+ In addition to wrapping known blocking system calls, libeio also expose several fallback implementations
40
+ such as readahead, sendfile etc. and is also very effective with system calls that incur a lot of CPU
41
+ overhead managing user space buffers, memcpy etc.
42
+
43
+ == Sweet Spot
44
+
45
+ This library solves a specific problem of avoiding blocking I/O work on POSIX APIs that traditionally
46
+ don't support the O_NONBLOCK flag or only have a synchronous interface defined. As with most event driven
47
+ I/O, the goal is increased throughput and not necessarily a faster per request guarantee. To serve more
48
+ clients with the same or less infrastructure without degrading quality of service.
49
+
50
+ == Requirements
51
+
52
+ * A POSIX compliant OS, known to work well on Linux, BSD variants and Mac OS X
53
+ * Ruby MRI 1.8 or 1.9
54
+ * Platform that supports the __VA_ARGS__ macro.
55
+ * It's recommended to use this library in conjunction with an event loop
56
+ * Best results with I/O bound work on disks / volumes with variable performance characteristics, such as
57
+ Amazon EBS.
58
+
59
+ == Installation
60
+
61
+ Rubygems installation
62
+
63
+ gem install eio
64
+
65
+ Building from source
66
+
67
+ git clone git@github.com:methodmissing/eio.git
68
+ rake compile:eio_ext
69
+
70
+ Running tests
71
+
72
+ rake test
73
+
74
+ == Documentation
75
+
76
+ See http://methodmissing.github.com/eio for RDOC documentation.
77
+
78
+ == How to - with an event loop
79
+
80
+ The Eventmachine handler watches the read end of a pipe which wakes up the event loop whenever there's
81
+ results to process. This is entirely driven from libeio which writes a char to the write end of the pipe
82
+ to wake up the loop. The EIO.poll callback will fire as many times as it needs to as we don't read data from the pipe through the reactor. A separate callback invoked by libeio will read the char and clear
83
+ it's readable state.
84
+
85
+ require 'eio/eventmachine'
86
+
87
+ EM.run do
88
+ EIO.eventmachine_handler # let libeio notify when there's result callbacks to invoke
89
+
90
+ EIO.open(__FILE__) do |fd|
91
+ EIO.read(fd) do |data|
92
+ p data
93
+ EIO.close{ EM.stop }
94
+ end
95
+ end
96
+ end
97
+
98
+ == How to - manually drain the result queue
99
+
100
+ This library ships with trivial Rack middleware that acts like a barrier at the end of each request. This
101
+ pattern can be applied to any other context as well.
102
+
103
+ module EIO
104
+ class Middleware
105
+ def initialize(app, opts = {})
106
+ @app = app
107
+ @options = opts
108
+ end
109
+
110
+ def call(env)
111
+ ret = @app.call(env)
112
+ EIO.wait # flush the libeio request queue
113
+ ret
114
+ end
115
+ end
116
+ end
117
+
118
+ use EIO::Middleware
119
+
120
+ The call to EIO.wait blocks until callbacks for all completed requests have been invoked. This workflow
121
+ is comparable to a 100m race with each line representing a libeio request and EIO.wait being the
122
+ finishing line / completion barrier. Each I/O operation may be scheduled on a different CPU core and will
123
+ complete in parallel, proportional to the slowest request, with some minor overhead to boot.
124
+
125
+ See the unit tests for further examples.
126
+
127
+ == Configuration
128
+
129
+ The thread pool can be configured for specific workloads. It's very important to schedule callback
130
+ processing in small batches when integrating with an event loop to not block other work. Use
131
+ EIO.max_poll_time and EIO.max_poll_request to restrict time spent or callbacks invoked per libeio
132
+ notification.
133
+
134
+ # Set the maximum amount of time spent in each eio_poll() invocation
135
+ EIO.max_poll_time = 0.1
136
+
137
+ # Set the maximum number of requests by each eio_poll() invocation
138
+ EIO.max_poll_reqs = x
139
+
140
+ Hundreds of threads can be spawned, however do note that stack sizes vary significantly between
141
+ platforms and this will most definitely affect your memory footprint. The default pool size is 8
142
+ threads.
143
+
144
+ # Set the minimum number of libeio threads to run in parallel. default: 8
145
+ EIO.min_parallel = x
146
+
147
+ # Set the maximum number of AIO threads to run in parallel. default: 8
148
+ EIO.max_parallel = x
149
+
150
+ # Limit the number of threads allowed to be idle
151
+ EIO.max_idle = x
152
+
153
+ # Set the minimum idle timeout before a thread is allowed to exit
154
+ EIO.idle_timeout = x
155
+
156
+ == Visibility
157
+
158
+ A simple API for integration with monitoring infrastructure's exposed as well. These stats may not be
159
+ very insightful or even accurate for a small number of in flight requests.
160
+
161
+ # Number of requests currently in the ready, execute or pending states
162
+ EIO.requests
163
+
164
+ # Number of requests currently in the ready state (not yet executed)
165
+ EIO.ready
166
+
167
+ # Number of requests currently in the pending state
168
+ EIO.pending
169
+
170
+ # Number of worker threads spawned
171
+ EIO.threads
172
+
173
+ == Managing requests
174
+
175
+ Use the following methods for manually scheduling request processing.
176
+
177
+ # Read end of the pipe an event loop can monitor for readability
178
+ EIO.fd
179
+
180
+ # Called when pending requests need finishing. The amount of work done is controlled by
181
+ # EIO.max_poll_time and EIO.max_poll_requests
182
+ EIO.poll
183
+
184
+ # Drain / flush all pending requests. This method blocks until all requests have been processed,
185
+ # regardless of configuration constraints imposed on requests per EIO.poll invocation.
186
+ EIO.wait
187
+
188
+ == Todo
189
+
190
+ * Finer grained priority support, especially stacked open, read etc.
191
+ * Grouped requests
192
+ * Richer EIO::Request API
193
+ * Implement and support all libeio wrapped syscalls
194
+ * Better guidelines for optimal configuration and tuning
195
+
196
+ == Contact, feedback and bugs
197
+
198
+ This project is very much work in progress and I'm looking for guidance on API design, use cases and
199
+ any outlier experiences. Please log bugs and suggestions at https://github.com/methodmissing/eio/issues
200
+
201
+ Thanks !
@@ -0,0 +1,48 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rake/extensiontask'
4
+ require 'rake/testtask'
5
+ require 'rdoc/task'
6
+
7
+ spec = eval(IO.read('eio.gemspec'))
8
+
9
+ task :compile => :build_libeio
10
+ task :clobber => :clobber_libeio
11
+
12
+ Rake::ExtensionTask.new('eio', spec) do |ext|
13
+ ext.name = 'eio_ext'
14
+ ext.ext_dir = 'ext/eio'
15
+ end
16
+
17
+ task :clobber_libeio do
18
+ Dir.chdir "ext/libeio" do
19
+ sh "make clean"
20
+ end
21
+ end
22
+
23
+ task :build_libeio do
24
+ Dir.chdir "ext/libeio" do
25
+ sh "./autogen.sh"
26
+ sh "./configure"
27
+ sh "make"
28
+ end unless File.exist?("ext/libeio/.libs/eio.o")
29
+ end
30
+
31
+ RDOC_FILES = FileList["README.rdoc", "ext/eio/eio_ext.c", "lib/eio.rb", "lib/eio/eventmachine.rb", "lib/eio/middleware.rb"]
32
+
33
+ Rake::RDocTask.new do |rd|
34
+ rd.title = "eio - a libeio wrapper for Ruby"
35
+ rd.main = "README.rdoc"
36
+ rd.rdoc_dir = "doc"
37
+ rd.rdoc_files.include(RDOC_FILES)
38
+ end
39
+
40
+ desc 'Run EIO tests'
41
+ Rake::TestTask.new(:test) do |t|
42
+ t.pattern = "test/test_*.rb"
43
+ t.verbose = true
44
+ t.warning = true
45
+ end
46
+ task :test => :compile
47
+
48
+ task :default => :test
@@ -0,0 +1,134 @@
1
+ # encoding: utf-8
2
+
3
+ $:.unshift File.expand_path(File.dirname(__FILE__) + '/../lib')
4
+ require 'eio'
5
+ require 'rubygems'
6
+ require 'eio/eventmachine'
7
+
8
+ EM.kqueue
9
+ EM.set_descriptor_table_size 10_000
10
+
11
+ class SyncIoConnection < EM::Connection
12
+ def self.stop
13
+ EM.stop
14
+ end
15
+
16
+ # Syncronous I/O during request lifetime
17
+ def receive_data(data)
18
+ start = EM.current_time
19
+ f = File.open(__FILE__)
20
+ f.read
21
+ Dir.entries(File.dirname(__FILE__))
22
+ f.close
23
+ send_data (Time.now - start).to_f
24
+ close_connection_after_writing
25
+ end
26
+ end
27
+
28
+ class AsyncIoConnection < EM::Connection
29
+ def self.stop
30
+ EIO.wait
31
+ EM.stop
32
+ end
33
+
34
+ # Asyncronous I/O during request lifetime - request finish before all work's complete
35
+ def receive_data(data)
36
+ start = EM.current_time
37
+ EIO.open(__FILE__) do |fd|
38
+ EIO.read(fd) do |buf|
39
+ EIO.readdir(File.dirname(__FILE__)) do |entries|
40
+ EIO.close(fd){ close_connection }
41
+ end
42
+ end
43
+ end
44
+ send_data (Time.now - start).to_f
45
+ end
46
+ end
47
+
48
+ class IoClient < EM::Connection
49
+ REQ_TIMINGS = []
50
+ WORK_TIMINGS = []
51
+
52
+ def self.stats(ctx, conns)
53
+ puts SEP_STR
54
+ puts FMT_STR % ["#{ctx} (#{conns} conns)"].concat(stats_for(REQ_TIMINGS)).concat(stats_for(WORK_TIMINGS))
55
+ EM.stop
56
+ end
57
+
58
+ def send_data(data)
59
+ @start = Time.now
60
+ super
61
+ end
62
+
63
+ def receive_data(data)
64
+ REQ_TIMINGS << data.to_f
65
+ end
66
+
67
+ def unbind
68
+ WORK_TIMINGS << (Time.now - @start).to_f
69
+ end
70
+
71
+ private
72
+ def self.stats_for(feature)
73
+ total = feature.inject(0){|a,i|a + i}
74
+ avg = (total / feature.size).to_f
75
+ feature.minmax.concat([avg, total])
76
+ ensure
77
+ feature.clear
78
+ end
79
+ end
80
+
81
+ def sync_io_server(conns)
82
+ conn = SyncIoConnection
83
+ EM.fork_reactor do
84
+ trap(:TERM){ conn.stop }
85
+ EM.start_server("0.0.0.0", 8000, conn)
86
+ end
87
+ end
88
+
89
+ def async_io_server(conns)
90
+ conn = AsyncIoConnection
91
+ EM.fork_reactor do
92
+ EIO.max_idle = conns / 10
93
+ EIO.min_parallel = conns / 10
94
+ EIO.max_parallel = conns / 20
95
+ EIO.max_poll_reqs = conns / 50
96
+
97
+ trap(:TERM){ conn.stop }
98
+ EIO.eventmachine_handler
99
+ EM.start_server("0.0.0.0", 8000, conn)
100
+ end
101
+ end
102
+
103
+ def run(server, conns = 300)
104
+ server_pid = send(server, conns)
105
+ EM.run do
106
+ EM.add_timer((conns / 50) + 0.5){ IoClient.stats(server, conns) }
107
+ conns.times do
108
+ c = EM.connect("0.0.0.0", 8000, IoClient)
109
+ c.send_data('*')
110
+ end
111
+ end
112
+ ensure
113
+ Process.kill(:TERM, server_pid)
114
+ sleep 1
115
+ end
116
+
117
+ HEADINGS = [ 'Context', 'Min req time', 'Max req time', 'Avg req time', 'Total req time',
118
+ 'Min work time', 'Max work time', 'Avg work time', 'Total work time']
119
+ FMT_STR = "| %-30s | %-12.5f | %-12.5f | %-12.5f | %-14.5f | %-13.5f | %-13.5f | %-13.5f | %-15.5f |"
120
+ SEP_STR = "|#{'-' * 160}|"
121
+
122
+ puts SEP_STR
123
+ puts "| %-30s | %-12s | %-12s | %-12s | %-14s | %-13s | %-13s | %-13s | %-15s |" % HEADINGS
124
+
125
+ run :sync_io_server, 100
126
+ run :async_io_server, 100
127
+
128
+ run :sync_io_server, 200
129
+ run :async_io_server, 200
130
+
131
+ run :sync_io_server, 300
132
+ run :async_io_server, 300
133
+
134
+ puts SEP_STR
@@ -0,0 +1,17 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = "eio"
3
+ s.version = defined?(EIO) ? EIO::VERSION : (ENV['VERSION'] || '0.1')
4
+ s.summary = "eio - a libeio wrapper for Ruby"
5
+ s.description = "eio - a libeio wrapper for Ruby"
6
+ s.authors = ["Lourens Naudé"]
7
+ s.email = ["lourens@methodmissing.com"]
8
+ s.homepage = "http://github.com/methodmissing/eio"
9
+ s.date = Time.now.utc.strftime('%Y-%m-%d')
10
+ s.platform = Gem::Platform::RUBY
11
+ s.extensions = Dir["ext/eio/extconf.rb"]
12
+ s.has_rdoc = true
13
+ s.files = `git ls-files`.split
14
+ s.test_files = Dir['test/test_*.rb']
15
+ s.add_development_dependency('rake-compiler', '~> 0.7.7')
16
+ s.add_development_dependency('eventmachine', '~> 0.12.3')
17
+ end
@@ -0,0 +1,1447 @@
1
+ #define EIO_REQ_MEMBERS short int complete;
2
+
3
+ #include "../libeio/config.h"
4
+ #include "../libeio/eio.h"
5
+ #include "../libeio/xthread.h"
6
+ #include "../libeio/eio.c"
7
+ #include "ruby.h"
8
+
9
+ /*
10
+ * Ruby 1.9 specific macros
11
+ */
12
+ #ifdef RUBY_VM
13
+ #include <ruby/encoding.h>
14
+ #include <ruby/io.h>
15
+ #define NO_CB_ARGS 0
16
+ #define EioEncode(str) rb_enc_associate(str, rb_default_internal_encoding())
17
+ #define TRAP_BEG
18
+ #define TRAP_END
19
+ static size_t
20
+ stat_memsize(const void *p)
21
+ {
22
+ return p ? sizeof(struct stat) : 0;
23
+ }
24
+
25
+ static const rb_data_type_t stat_data_type = {
26
+ "stat",
27
+ {NULL, RUBY_TYPED_DEFAULT_FREE, stat_memsize,},
28
+ };
29
+ #else
30
+ #ifndef RSTRING_PTR
31
+ #define RSTRING_PTR(str) RSTRING(str)->ptr
32
+ #endif
33
+ #ifndef RSTRING_LEN
34
+ #define RSTRING_LEN(s) (RSTRING(s)->len)
35
+ #endif
36
+ #include "rubyio.h"
37
+ #include "rubysig.h"
38
+ #define NO_CB_ARGS -1
39
+ #define EioEncode(str) str
40
+ #endif
41
+
42
+ /*
43
+ * Synchronous I/O fallback
44
+ */
45
+ #define s_fsync fsync
46
+ #define s_open open
47
+ #define s_close close
48
+ #define s_readahead(fd, len, off) read(fd, off, len)
49
+ #define s_sendfile eio_sendfile_sync
50
+ #define s_mkdir mkdir
51
+ #define s_rmdir rmdir
52
+ #define s_unlink unlink
53
+ #define s_rename rename
54
+ #define s_chmod chmod
55
+ #define s_fchmod fchmod
56
+ #define s_ftruncate ftruncate
57
+ #define s_truncate truncate
58
+ #define s_chown chown
59
+ #define s_fchown fchown
60
+ #define s_link link
61
+ #define s_symlink symlink
62
+
63
+ VALUE mEio;
64
+ VALUE cEioReq;
65
+
66
+ /*
67
+ * Symbols
68
+ */
69
+ static VALUE sym_call;
70
+ static VALUE sym_arity;
71
+ static VALUE sym_pipe;
72
+ static VALUE sym_readlink;
73
+ static VALUE sym_stat;
74
+ static VALUE sym_pipe_r_fd;
75
+ static VALUE sym_pipe_w_fd;
76
+
77
+ /*
78
+ * Common fixnums
79
+ */
80
+ static VALUE eio_zero;
81
+ static VALUE eio_default_bufsize;
82
+ static VALUE eio_default_mode;
83
+
84
+ /*
85
+ * Pipe r / w fds
86
+ */
87
+ static int eio_pipe_r_fd;
88
+ static int eio_pipe_w_fd;
89
+
90
+ static void rb_eio_recreate_pipe(void);
91
+ static VALUE rb_eio_wrap_request(eio_req *r);
92
+
93
+ /*
94
+ * Assert a valid Proc and arity as callback
95
+ */
96
+ #define AssertCallback(cb, arity) \
97
+ if (NIL_P(cb)) cb = proc; \
98
+ if (!NIL_P(cb)){ \
99
+ if (rb_class_of(cb) != rb_cProc) \
100
+ rb_raise(rb_eTypeError, "Expected a Proc callback"); \
101
+ if (rb_funcall(cb, sym_arity, 0) != INT2NUM(arity)) \
102
+ rb_raise(rb_eArgError, "Callback expects %d argument(s), got %d", arity, FIX2INT(rb_funcall(cb, sym_arity, 0))); \
103
+ }
104
+
105
+ /*
106
+ * Wrap the eio_req struct for a given object. Raises TypeError if the struct has been recycled by
107
+ * a libeio finish callback.
108
+ */
109
+ #define GetRequest(obj) \
110
+ eio_req * req; \
111
+ Data_Get_Struct(obj, eio_req, req); \
112
+ if (!req) rb_raise(rb_eTypeError, "uninitialized EIO::Request");
113
+
114
+ /*
115
+ * libeio callback handler. Respects cancelled requests and bubbles up any errors (-1 results).
116
+ */
117
+ #define EioCallback(req, statements) \
118
+ VALUE cb; \
119
+ assert(req); \
120
+ cb = (VALUE)req->data; \
121
+ if EIO_CANCELLED(req){ \
122
+ rb_gc_unregister_address(&cb); \
123
+ return 0; \
124
+ } \
125
+ if (req->result == -1){ \
126
+ rb_gc_unregister_address(&cb); \
127
+ errno = req->errorno; \
128
+ req->complete = 1; \
129
+ (!req->ptr1) ? rb_sys_fail(0) : rb_sys_fail(req->ptr1); \
130
+ return -1; \
131
+ } else { \
132
+ if (!NIL_P(cb)){ \
133
+ statements; \
134
+ req->complete = 1; \
135
+ rb_gc_unregister_address(&cb); \
136
+ } \
137
+ } \
138
+ return 0;
139
+
140
+ /*
141
+ * Synchronous I/O request
142
+ */
143
+ #define SyncRequest(statements) \
144
+ if (!rb_block_given_p()){ \
145
+ TRAP_BEG; \
146
+ statements; \
147
+ TRAP_END; \
148
+ }
149
+
150
+ /*
151
+ * Asynchronous I/O request
152
+ */
153
+ #define AsyncRequest(syscall, callback, ...) \
154
+ if (rb_thread_current() != rb_thread_main()) \
155
+ rb_raise(rb_eThreadError, "EIO requests can only be submitted on the main thread."); \
156
+ rb_gc_register_address(&cb); \
157
+ return rb_eio_wrap_request(eio_ ## syscall(__VA_ARGS__, EIO_PRI_DEFAULT, callback, (void*)cb)); \
158
+
159
+ /*
160
+ * Abstraction for conditional sync / async I/O
161
+ */
162
+ #define SubmitRequest(syscall, callback, ...) \
163
+ if (rb_block_given_p()){ \
164
+ AsyncRequest(syscall, callback, ##__VA_ARGS__); \
165
+ } else { \
166
+ TRAP_BEG; \
167
+ ret = s_ ## syscall(__VA_ARGS__); \
168
+ TRAP_END; \
169
+ if (ret == -1) rb_sys_fail(#syscall); \
170
+ return INT2NUM(ret); \
171
+ }
172
+
173
+ /*
174
+ * Callback for when libeio wants attention. Writes a char to pipe to wake up the event loop.
175
+ */
176
+ static void
177
+ want_poll(void)
178
+ {
179
+ char heartbeat;
180
+ assert(write(eio_pipe_w_fd, &heartbeat, 1) == 1);
181
+ }
182
+
183
+ /*
184
+ * Callback invoked when all pending work's been done. Reads a char from the pipe.
185
+ */
186
+ static void
187
+ done_poll(void)
188
+ {
189
+ char heartbeat;
190
+ assert(read(eio_pipe_r_fd, &heartbeat, 1) == 1);
191
+ }
192
+
193
+ /*
194
+ * Drain all pending libeio requests
195
+ */
196
+ static void
197
+ rb_eio_s_wait0()
198
+ {
199
+ fd_set rdset;
200
+ int fd, size;
201
+ fd = eio_pipe_r_fd;
202
+
203
+ while (eio_nreqs())
204
+ {
205
+ X_LOCK(reslock);
206
+ size = res_queue.size;
207
+ X_UNLOCK(reslock);
208
+
209
+ if (size) return;
210
+
211
+ etp_maybe_start_thread();
212
+
213
+ FD_ZERO(&rdset);
214
+ FD_SET(fd, &rdset);
215
+ if (rb_thread_select(fd + 1, &rdset, NULL, NULL, NULL) < 0) rb_sys_fail(0);
216
+ }
217
+ }
218
+
219
+ /*
220
+ * Generic callback, invoked with no args
221
+ */
222
+ int
223
+ rb_eio_generic_cb(eio_req *req)
224
+ {
225
+ EioCallback(req,{
226
+ rb_funcall(cb, sym_call, 0);
227
+ });
228
+ }
229
+
230
+ /*
231
+ * Open callback, invoked with a single Fixnum arg
232
+ */
233
+ int
234
+ rb_eio_open_cb(eio_req *req)
235
+ {
236
+ EioCallback(req,{
237
+ rb_funcall(cb, sym_call, 1, INT2NUM(EIO_RESULT(req)));
238
+ });
239
+ }
240
+
241
+ /*
242
+ * Read callback, invoked with a single String arg
243
+ */
244
+ int
245
+ rb_eio_read_cb(eio_req *req)
246
+ {
247
+ EioCallback(req,{
248
+ rb_funcall(cb, sym_call, 1, EioEncode(rb_str_new((const char*)EIO_BUF(req), EIO_RESULT(req))));
249
+ });
250
+ }
251
+
252
+ /*
253
+ * Readdir callback, invoked with a single Array arg
254
+ */
255
+ int
256
+ rb_eio_readdir_cb(eio_req *req)
257
+ {
258
+ int ret;
259
+ VALUE result;
260
+ char *entries;
261
+ EioCallback(req, {
262
+ result = rb_ary_new2(EIO_RESULT(req));
263
+ entries = (char *)EIO_BUF(req);
264
+ while (EIO_RESULT(req)--)
265
+ {
266
+ rb_ary_push(result, EioEncode(rb_str_new2(entries)));
267
+ entries += strlen(entries) + 1;
268
+ }
269
+ rb_funcall(cb, sym_call, 1, result);
270
+ });
271
+ }
272
+
273
+ /*
274
+ * Write callback, invoked with a single Fixnum arg
275
+ */
276
+ int
277
+ rb_eio_write_cb(eio_req *req)
278
+ {
279
+ EioCallback(req,{
280
+ rb_funcall(cb, sym_call, 1, INT2NUM(EIO_RESULT(req)));
281
+ });
282
+ }
283
+
284
+ /*
285
+ * Stat callback, invoked with a single File::Stat arg
286
+ */
287
+ int
288
+ rb_eio_stat_cb(eio_req *req)
289
+ {
290
+ EioCallback(req,{
291
+ #ifdef RUBY_VM
292
+ rb_funcall(cb, sym_call, 1, TypedData_Wrap_Struct(rb_cStat, &stat_data_type, EIO_BUF(req)));
293
+ #else
294
+ rb_funcall(cb, sym_call, 1, Data_Wrap_Struct(rb_cStat, NULL, NULL, EIO_BUF(req)));
295
+ #endif
296
+ });
297
+ }
298
+
299
+ /*
300
+ * call-seq:
301
+ * EIO.wait => nil
302
+ *
303
+ * Drain / flush all pending requests - BLOCKS
304
+ *
305
+ */
306
+ static VALUE
307
+ rb_eio_s_wait(VALUE eio)
308
+ {
309
+ int res;
310
+ eio_req *req;
311
+ req = eio_sync(EIO_PRI_DEFAULT, NULL, NULL);
312
+ assert(req);
313
+ while (eio_nreqs())
314
+ {
315
+ rb_eio_s_wait0();
316
+ res = eio_poll();
317
+ if (res > 0) rb_sys_fail("eio_poll");
318
+ }
319
+ return Qnil;
320
+ }
321
+
322
+ /*
323
+ * call-seq:
324
+ * EIO.poll => Fixnum
325
+ *
326
+ * Called when pending requests need finishing
327
+ *
328
+ */
329
+ static VALUE
330
+ rb_eio_s_poll(VALUE eio)
331
+ {
332
+ int res;
333
+ res = eio_poll();
334
+ if (res > 0) rb_sys_fail("eio_poll");
335
+ return INT2NUM(res);
336
+ }
337
+
338
+ /*
339
+ * call-seq:
340
+ * EIO.requests => Fixnum
341
+ *
342
+ * Number of requests currently in the ready, execute or pending states
343
+ *
344
+ */
345
+ static VALUE
346
+ rb_eio_s_requests(VALUE eio)
347
+ {
348
+ return INT2NUM(eio_nreqs());
349
+ }
350
+
351
+ /*
352
+ * call-seq:
353
+ * EIO.ready => Fixnum
354
+ *
355
+ * Number of requests currently in the ready state (not yet executed)
356
+ *
357
+ */
358
+ static VALUE
359
+ rb_eio_s_ready(VALUE eio)
360
+ {
361
+ return INT2NUM(eio_nready());
362
+ }
363
+
364
+ /*
365
+ * call-seq:
366
+ * EIO.pending => Fixnum
367
+ *
368
+ * Number of requests currently in the pending state
369
+ *
370
+ */
371
+ static VALUE
372
+ rb_eio_s_pending(VALUE eio)
373
+ {
374
+ return INT2NUM(eio_npending());
375
+ }
376
+
377
+ /*
378
+ * call-seq:
379
+ * EIO.threads => Fixnum
380
+ *
381
+ * Number of worker threads spawned
382
+ *
383
+ */
384
+ static VALUE
385
+ rb_eio_s_threads(VALUE eio)
386
+ {
387
+ return INT2NUM(eio_nthreads());
388
+ }
389
+
390
+ /*
391
+ * call-seq:
392
+ * EIO.fd => Fixnum
393
+ *
394
+ * Read end of the pipe an event loop can monitor for readability
395
+ *
396
+ */
397
+ static VALUE
398
+ rb_eio_s_fd(VALUE eio)
399
+ {
400
+ return INT2NUM(eio_pipe_r_fd);
401
+ }
402
+
403
+ /*
404
+ * call-seq:
405
+ * EIO.max_poll_time = x => Fixnum
406
+ *
407
+ * Set the maximum amount of time spent in each eio_poll() invocation
408
+ *
409
+ */
410
+ static VALUE
411
+ rb_eio_s_set_max_poll_time(VALUE eio, VALUE seconds)
412
+ {
413
+ eio_set_max_poll_time(FIX2LONG(seconds));
414
+ return seconds;
415
+ }
416
+
417
+ /*
418
+ * call-seq:
419
+ * EIO.max_poll_reqs = x => Fixnum
420
+ *
421
+ * Set the maximum number of requests by each eio_poll() invocation
422
+ *
423
+ */
424
+ static VALUE
425
+ rb_eio_s_set_max_poll_reqs(VALUE eio, VALUE requests)
426
+ {
427
+ eio_set_max_poll_reqs(FIX2INT(requests));
428
+ return requests;
429
+ }
430
+
431
+ /*
432
+ * call-seq:
433
+ * EIO.min_parallel = x => Fixnum
434
+ *
435
+ * Set the minimum number of libeio threads to run in parallel. default: 8
436
+ *
437
+ */
438
+ static VALUE
439
+ rb_eio_s_set_min_parallel(VALUE eio, VALUE threads)
440
+ {
441
+ eio_set_min_parallel(FIX2INT(threads));
442
+ return threads;
443
+ }
444
+
445
+ /*
446
+ * call-seq:
447
+ * EIO.max_parallel = x => Fixnum
448
+ *
449
+ * Set the maximum number of AIO threads to run in parallel. default: 8
450
+ *
451
+ */
452
+ static VALUE
453
+ rb_eio_s_set_max_parallel(VALUE eio, VALUE threads)
454
+ {
455
+ eio_set_max_parallel(FIX2INT(threads));
456
+ return threads;
457
+ }
458
+
459
+ /*
460
+ * call-seq:
461
+ * EIO.max_idle = x => Fixnum
462
+ *
463
+ * Limit the number of threads allowed to be idle
464
+ *
465
+ */
466
+ static VALUE
467
+ rb_eio_s_set_max_idle(VALUE eio, VALUE threads)
468
+ {
469
+ eio_set_max_idle(FIX2INT(threads));
470
+ return threads;
471
+ }
472
+
473
+ /*
474
+ * call-seq:
475
+ * EIO.idle_timeout = x => Fixnum
476
+ *
477
+ * Set the minimum idle timeout before a thread is allowed to exit
478
+ *
479
+ */
480
+ static VALUE
481
+ rb_eio_s_set_idle_timeout(VALUE eio, VALUE seconds)
482
+ {
483
+ eio_set_idle_timeout(FIX2INT(seconds));
484
+ return seconds;
485
+ }
486
+
487
+ /*
488
+ * call-seq:
489
+ * EIO.open('/path/file'){|fd| p fd } => EIO::Request
490
+ *
491
+ * Asynchronously open or create a file and call the callback with a newly created file handle
492
+ * for the file.
493
+ *
494
+ * === Examples
495
+ * EIO.open('/path/file', EIO::RDONLY){|fd| p fd } => EIO::Request
496
+ * EIO.open('/path/file', EIO::RDWR, 0777){|fd| p fd } => EIO::Request
497
+ * cb = Proc.new{|fd| p fd }
498
+ * EIO.open('/path/file', EIO::RDWR, 0777, cb) => EIO::Request
499
+ *
500
+ * EIO.open('/path/file') => Fixnum
501
+ * EIO.open('/path/file', EIO::RDWR) => Fixnum
502
+ * EIO.open('/path/file', EIO::RDWR, 0777) => Fixnum
503
+ *
504
+ */
505
+ static VALUE
506
+ rb_eio_s_open(int argc, VALUE *argv, VALUE eio)
507
+ {
508
+ int ret;
509
+ VALUE path, flags, mode, proc, cb;
510
+ rb_scan_args(argc, argv, "13&", &path, &flags, &mode, &proc, &cb);
511
+ AssertCallback(cb, 1);
512
+ Check_Type(path, T_STRING);
513
+ if (NIL_P(flags)) flags = INT2NUM(O_RDONLY);
514
+ Check_Type(flags, T_FIXNUM);
515
+ if (NIL_P(mode)) mode = eio_default_mode;
516
+ Check_Type(mode, T_FIXNUM);
517
+ SubmitRequest(open, rb_eio_open_cb, StringValueCStr(path), FIX2INT(flags), FIX2INT(mode));
518
+ }
519
+
520
+ /*
521
+ * call-seq:
522
+ * EIO.close(fd){ p :closed } => EIO::Request
523
+ *
524
+ * Asynchronously close a file and call the callback with the result code.
525
+ *
526
+ * === Examples
527
+ * cb = Proc.new{ p :closed }
528
+ * EIO.close(fd, cb) => EIO::Request
529
+ *
530
+ * EIO.close(fd) => Fixnum
531
+ *
532
+ */
533
+ static VALUE
534
+ rb_eio_s_close(int argc, VALUE *argv, VALUE eio)
535
+ {
536
+ int ret;
537
+ VALUE fd, proc, cb;
538
+ rb_scan_args(argc, argv, "11&", &fd, &proc, &cb);
539
+ AssertCallback(cb, NO_CB_ARGS);
540
+ Check_Type(fd, T_FIXNUM);
541
+ SubmitRequest(close, rb_eio_generic_cb, FIX2INT(fd));
542
+ }
543
+
544
+ /*
545
+ * call-seq:
546
+ * EIO.fsync(fd){ p :synced } => EIO::Request
547
+ *
548
+ * Asynchronously call fsync on the given filehandle and call the callback with the result
549
+ * code.
550
+ *
551
+ * === Examples
552
+ * cb = Proc.new{ p :synced }
553
+ * EIO.fsync(fd, cb) => EIO::Request
554
+ *
555
+ * EIO.fsync(fd) => Fixnum
556
+ *
557
+ */
558
+ static VALUE
559
+ rb_eio_s_fsync(int argc, VALUE *argv, VALUE eio)
560
+ {
561
+ int ret;
562
+ VALUE fd, proc, cb;
563
+ rb_scan_args(argc, argv, "11&", &fd, &proc, &cb);
564
+ AssertCallback(cb, NO_CB_ARGS);
565
+ Check_Type(fd, T_FIXNUM);
566
+ SubmitRequest(fsync, rb_eio_generic_cb, FIX2INT(fd));
567
+ }
568
+
569
+ /*
570
+ * call-seq:
571
+ * EIO.fdatasync(fd){ p :synced } => EIO::Request
572
+ *
573
+ * Asynchronously call fdatasync on the given filehandle and call the callback with the result
574
+ * code.
575
+ *
576
+ * === Examples
577
+ * cb = Proc.new{ p :synced }
578
+ * EIO.fdatasync(fd, cb) => EIO::Request
579
+ *
580
+ * EIO.fdatasync(fd) => Fixnum
581
+ *
582
+ */
583
+ static VALUE
584
+ rb_eio_s_fdatasync(int argc, VALUE *argv, VALUE eio)
585
+ {
586
+ int ret;
587
+ VALUE fd, proc, cb;
588
+ rb_scan_args(argc, argv, "11&", &fd, &proc, &cb);
589
+ AssertCallback(cb, NO_CB_ARGS);
590
+ Check_Type(fd, T_FIXNUM);
591
+ SyncRequest({
592
+ #if HAVE_FDATASYNC
593
+ ret = fdatasync(FIX2INT(fd));
594
+ #else
595
+ ret = fsync(FIX2INT(fd));
596
+ #endif
597
+ if (ret == -1) rb_sys_fail("fdatasync");
598
+ return INT2NUM(ret);
599
+ });
600
+ AsyncRequest(fdatasync, rb_eio_generic_cb, FIX2INT(fd));
601
+ }
602
+
603
+ /*
604
+ * call-seq:
605
+ * EIO.read(fd){|d| p d } => EIO::Request
606
+ *
607
+ * Asynchronously reads length bytes from a specified offset into a buffer.
608
+ *
609
+ * === Examples
610
+ * EIO.read(fd, 100){|d| p d } => EIO::Request
611
+ * EIO.read(fd, 100, 50){|d| p d } => EIO::Request
612
+ * cb = Proc.new{|d| p d }
613
+ * EIO.read(fd, 100, 50, cb) => EIO::Request
614
+ *
615
+ * EIO.read(fd) => String
616
+ * EIO.read(fd, 100) => String
617
+ * EIO.read(fd, 100, 50) => String
618
+ *
619
+ */
620
+ static VALUE
621
+ rb_eio_s_read(int argc, VALUE *argv, VALUE eio)
622
+ {
623
+ int ret;
624
+ VALUE fd, len, offset, proc, cb;
625
+ VALUE buf;
626
+ rb_scan_args(argc, argv, "13&", &fd, &len, &offset, &proc, &cb);
627
+ AssertCallback(cb, 1);
628
+ Check_Type(fd, T_FIXNUM);
629
+ if (NIL_P(len)) len = eio_default_bufsize;
630
+ Check_Type(len, T_FIXNUM);
631
+ if (len == eio_zero) len = eio_default_bufsize;
632
+ if (NIL_P(offset)) offset = eio_zero;
633
+ Check_Type(offset, T_FIXNUM);
634
+ SyncRequest({
635
+ buf = rb_str_new(0, FIX2INT(len));
636
+ if (offset == eio_zero){
637
+ ret = read(FIX2INT(fd), RSTRING_PTR(buf), FIX2INT(len));
638
+ } else {
639
+ ret = pread(FIX2INT(fd), RSTRING_PTR(buf), FIX2INT(len), FIX2INT(offset));
640
+ }
641
+ if (ret == -1) rb_sys_fail("read");
642
+ return buf;
643
+ });
644
+ AsyncRequest(read, rb_eio_read_cb, FIX2INT(fd), 0, FIX2INT(len), FIX2INT(offset));
645
+ }
646
+
647
+ /*
648
+ * call-seq:
649
+ * EIO.readahead(fd){|d| p :read } => EIO::Request
650
+ *
651
+ * Populates the page cache with data from a file so that subsequent reads from that file will
652
+ * not block on disk I/O.
653
+ *
654
+ * === Examples
655
+ * EIO.readahead(fd, 100){|d| p :read } => EIO::Request
656
+ * EIO.readahead(fd, 100, 50){ p :read } => EIO::Request
657
+ * cb = Proc.new{ p :read }
658
+ * EIO.readahead(fd, 100, 50, cb) => EIO::Request
659
+ *
660
+ * EIO.readahead(fd) => Fixnum
661
+ * EIO.readahead(fd, 100) => Fixnum
662
+ * EIO.readahead(fd, 100, 50) => Fixnum
663
+ *
664
+ */
665
+ static VALUE
666
+ rb_eio_s_readahead(int argc, VALUE *argv, VALUE eio)
667
+ {
668
+ int ret;
669
+ VALUE fd, len, offset, proc, cb;
670
+ rb_scan_args(argc, argv, "13&", &fd, &len, &offset, &proc, &cb);
671
+ AssertCallback(cb, NO_CB_ARGS);
672
+ Check_Type(fd, T_FIXNUM);
673
+ if (NIL_P(len)) len = eio_default_bufsize;
674
+ Check_Type(len, T_FIXNUM);
675
+ if (len == eio_zero) len = eio_default_bufsize;
676
+ if (NIL_P(offset)) offset = eio_zero;
677
+ Check_Type(offset, T_FIXNUM);
678
+ SubmitRequest(readahead, rb_eio_generic_cb, FIX2INT(fd), FIX2INT(offset), FIX2INT(len));
679
+ }
680
+
681
+ /*
682
+ * call-seq:
683
+ * EIO.write(fd, buf){|b| p b } => EIO::Request
684
+ *
685
+ * Asynchronously writes length bytes from a specified offset into a buffer.
686
+ *
687
+ * === Examples
688
+ * EIO.write(fd, buf, 100){|b| p b } => EIO::Request
689
+ * EIO.write(fd, buf, 100, 50){|b| p b } => EIO::Request
690
+ * cb = Proc.new{|b| p b }
691
+ * EIO.write(fd, buf, 100, 50, cb) => EIO::Request
692
+ *
693
+ * EIO.write(fd, buf) => Fixnum
694
+ * EIO.write(fd, buf, 100) => Fixnum
695
+ * EIO.write(fd, buf, 100, 50) => Fixnum
696
+ *
697
+ */
698
+ static VALUE
699
+ rb_eio_s_write(int argc, VALUE *argv, VALUE eio)
700
+ {
701
+ int ret, i_len, i_offset;
702
+ VALUE fd, buf, len, offset, proc, cb, buf_len;
703
+ rb_scan_args(argc, argv, "23&", &fd, &buf, &len, &offset, &proc, &cb);
704
+ AssertCallback(cb, 1);
705
+ Check_Type(fd, T_FIXNUM);
706
+ Check_Type(buf, T_STRING);
707
+ if (NIL_P(len)) len = INT2NUM(RSTRING_LEN(buf));
708
+ Check_Type(len, T_FIXNUM);
709
+ if (NIL_P(offset)) offset = eio_zero;
710
+ Check_Type(offset, T_FIXNUM);
711
+ i_offset = FIX2INT(offset);
712
+ i_len = FIX2INT(len);
713
+ if (i_offset >= RSTRING_LEN(buf)) rb_raise(rb_eArgError, "out of bounds offset");
714
+ if ((i_offset + i_len) > RSTRING_LEN(buf)) rb_raise(rb_eArgError, "length extends beyond buffer");
715
+ SyncRequest({
716
+ if (offset == eio_zero){
717
+ ret = write(FIX2INT(fd), StringValueCStr(buf), i_len);
718
+ } else {
719
+ ret = pwrite(FIX2INT(fd), StringValueCStr(buf), i_len, i_offset);
720
+ }
721
+ if (ret == -1) rb_sys_fail("write");
722
+ return INT2NUM(ret);
723
+ });
724
+ AsyncRequest(write, rb_eio_write_cb, FIX2INT(fd), StringValueCStr(buf), i_len, i_offset);
725
+ }
726
+
727
+ /*
728
+ * call-seq:
729
+ * EIO.sendfile(in_fd, out_fd){|b| p b } => EIO::Request
730
+ *
731
+ * Tries to copy length bytes from in fd to out fd, starting at a given offset.
732
+ *
733
+ * === Examples
734
+ * EIO.sendfile(in_fd, out_fd, 100){|b| p b } => EIO::Request
735
+ * EIO.sendfile(in_fd, out_fd, 100, 50){|b| p b } => EIO::Request
736
+ * cb = Proc.new{|b| p b }
737
+ * EIO.sendfile(in_fd, out_fd, 100, 50, cb) => EIO::Request
738
+ *
739
+ * EIO.sendfile(in_fd, out_fd) => Fixnum
740
+ * EIO.sendfile(in_fd, out_fd, 100) => Fixnum
741
+ * EIO.sendfile(in_fd, out_fd, 100, 50) => Fixnum
742
+ *
743
+ */
744
+ static VALUE
745
+ rb_eio_s_sendfile(int argc, VALUE *argv, VALUE eio)
746
+ {
747
+ int ret;
748
+ VALUE out_fd, in_fd, offset, len, proc, cb;
749
+ rb_scan_args(argc, argv, "23&", &out_fd, &in_fd, &offset, &len, &proc, &cb);
750
+ AssertCallback(cb, 1);
751
+ Check_Type(in_fd, T_FIXNUM);
752
+ Check_Type(out_fd, T_FIXNUM);
753
+ if (NIL_P(len)) len = eio_default_bufsize;
754
+ Check_Type(len, T_FIXNUM);
755
+ if (len == eio_zero) len = eio_default_bufsize;
756
+ if (NIL_P(offset)) offset = eio_zero;
757
+ Check_Type(offset, T_FIXNUM);
758
+ SubmitRequest(sendfile, rb_eio_write_cb, FIX2INT(out_fd), FIX2INT(in_fd), FIX2INT(offset), FIX2INT(len));
759
+ }
760
+
761
+ /*
762
+ * call-seq:
763
+ * EIO.readdir('/path'){|fs| p fs } => EIO::Request
764
+ *
765
+ * Unlike the POSIX call of the same name, aio_readdir reads an entire directory (i.e.
766
+ * opendir + readdir + closedir). The entries will not be sorted, and will NOT include the
767
+ * . and .. entries.
768
+ *
769
+ * === Examples
770
+ * cb = Proc.new{|b| p b }
771
+ * EIO.readdir('/path', cb) => EIO::Request
772
+ *
773
+ * EIO.readdir('/path') => Array
774
+ *
775
+ */
776
+ static VALUE
777
+ rb_eio_s_readdir(int argc, VALUE *argv, VALUE eio)
778
+ {
779
+ int ret;
780
+ VALUE path, proc, cb;
781
+ VALUE files;
782
+ char *name;
783
+ struct dirent *ent;
784
+ rb_scan_args(argc, argv, "11&", &path, &proc, &cb);
785
+ AssertCallback(cb, 1);
786
+ Check_Type(path, T_STRING);
787
+ SyncRequest({
788
+ DIR *dir = opendir(StringValueCStr(path));
789
+ if (!dir) rb_sys_fail(StringValueCStr(path));
790
+
791
+ files = rb_ary_new();
792
+
793
+ while ((ent = readdir(dir))) {
794
+ name = ent->d_name;
795
+ if (name[0] != '.' || (name[1] && (name[1] != '.' || name[2]))) {
796
+ rb_ary_push(files, rb_str_new2(name));
797
+ }
798
+ }
799
+ ret = closedir(dir);
800
+ if (ret == -1) rb_sys_fail("closedir");
801
+ return(files);
802
+ });
803
+ AsyncRequest(readdir, rb_eio_readdir_cb, StringValueCStr(path), EIO_READDIR_STAT_ORDER);
804
+ }
805
+
806
+ /*
807
+ * call-seq:
808
+ * EIO.mkdir('/path'){ p :created } => EIO::Request
809
+ *
810
+ * Asynchronously mkdir (create) a directory and call the callback with the result code.
811
+ *
812
+ * === Examples
813
+ * EIO.mkdir('/path', 0777){ p :created } => EIO::Request
814
+ * cb = Proc.new{ p :created }
815
+ * EIO.mkdir('/path', 0777, cb) => EIO::Request
816
+ *
817
+ * EIO.mkdir('/path') => Fixnum
818
+ * EIO.mkdir('/path', 0777) => Fixnum
819
+ *
820
+ */
821
+ static VALUE
822
+ rb_eio_s_mkdir(int argc, VALUE *argv, VALUE eio)
823
+ {
824
+ int ret;
825
+ VALUE path, mode, proc, cb;
826
+ rb_scan_args(argc, argv, "12&", &path, &mode, &proc, &cb);
827
+ AssertCallback(cb, NO_CB_ARGS);
828
+ Check_Type(path, T_STRING);
829
+ if (NIL_P(mode)) mode = eio_default_mode;
830
+ Check_Type(mode, T_FIXNUM);
831
+ SubmitRequest(mkdir, rb_eio_generic_cb, StringValueCStr(path), FIX2INT(mode));
832
+ }
833
+
834
+ /*
835
+ * call-seq:
836
+ * EIO.rmdir('/path'){ p :removed } => EIO::Request
837
+ *
838
+ * Asynchronously rmdir (delete) a directory and call the callback with the result code.
839
+ *
840
+ * === Examples
841
+ * cb = Proc.new{ p :removed }
842
+ * EIO.rmdir('/path', cb) => EIO::Request
843
+ *
844
+ * EIO.rmdir('/path') => Fixnum
845
+ *
846
+ */
847
+ static VALUE
848
+ rb_eio_s_rmdir(int argc, VALUE *argv, VALUE eio)
849
+ {
850
+ int ret;
851
+ VALUE path, proc, cb;
852
+ rb_scan_args(argc, argv, "11&", &path, &proc, &cb);
853
+ AssertCallback(cb, NO_CB_ARGS);
854
+ Check_Type(path, T_STRING);
855
+ SubmitRequest(rmdir, rb_eio_generic_cb, StringValueCStr(path));
856
+ }
857
+
858
+ /*
859
+ * call-seq:
860
+ * EIO.unlink('/path/file'){ p :removed } => EIO::Request
861
+ *
862
+ * Asynchronously unlink (delete) a file and call the callback with the result code.
863
+ *
864
+ * === Examples
865
+ * cb = Proc.new{ p :removed }
866
+ * EIO.unlink('/path/file', cb) => EIO::Request
867
+ *
868
+ * EIO.unlink('/path/file') => Fixnum
869
+ *
870
+ */
871
+ static VALUE
872
+ rb_eio_s_unlink(int argc, VALUE *argv, VALUE eio)
873
+ {
874
+ int ret;
875
+ VALUE path, proc, cb;
876
+ rb_scan_args(argc, argv, "11&", &path, &proc, &cb);
877
+ AssertCallback(cb, NO_CB_ARGS);
878
+ Check_Type(path, T_STRING);
879
+ SubmitRequest(unlink, rb_eio_generic_cb, StringValueCStr(path));
880
+ }
881
+
882
+ /*
883
+ * call-seq:
884
+ * EIO.readlink('/path/link'){|l| p l } => EIO::Request
885
+ *
886
+ * Asynchronously read the symlink specified by path and pass it to the callback.
887
+ *
888
+ * === Examples
889
+ * cb = Proc.new{|l| p l }
890
+ * EIO.readlink('/path/link', cb) => EIO::Request
891
+ *
892
+ * EIO.readlink('/path/link') => Fixnum
893
+ *
894
+ */
895
+ static VALUE
896
+ rb_eio_s_readlink(int argc, VALUE *argv, VALUE eio)
897
+ {
898
+ int ret;
899
+ VALUE path, proc, cb;
900
+ rb_scan_args(argc, argv, "11&", &path, &proc, &cb);
901
+ AssertCallback(cb, 1);
902
+ Check_Type(path, T_STRING);
903
+ SyncRequest({
904
+ return rb_funcall(rb_cFile, sym_readlink, 1, path);
905
+ });
906
+ AsyncRequest(readlink, rb_eio_read_cb, StringValueCStr(path));
907
+ }
908
+
909
+ /*
910
+ * call-seq:
911
+ * EIO.stat('/path/file'){|s| p s } => EIO::Request
912
+ *
913
+ * Works like Ruby's stat. The callback will be called after the stat.
914
+ *
915
+ * === Examples
916
+ * cb = Proc.new{|s| p s }
917
+ * EIO.stat('/path/file', cb) => EIO::Request
918
+ *
919
+ * EIO.stat('/path/file') => File::Stat
920
+ *
921
+ */
922
+ static VALUE
923
+ rb_eio_s_stat(int argc, VALUE *argv, VALUE eio)
924
+ {
925
+ int ret;
926
+ VALUE path, proc, cb;
927
+ rb_scan_args(argc, argv, "11&", &path, &proc, &cb);
928
+ AssertCallback(cb, 1);
929
+ Check_Type(path, T_STRING);
930
+ SyncRequest({
931
+ return rb_funcall(rb_cFile, sym_stat, 1, path);
932
+ });
933
+ AsyncRequest(stat, rb_eio_stat_cb, StringValueCStr(path));
934
+ }
935
+
936
+ /*
937
+ * call-seq:
938
+ * EIO.rename('/path/a', '/path/b'){ p :renamed } => EIO::Request
939
+ *
940
+ * Asynchronously rename the object at source path to destination path
941
+ *
942
+ * === Examples
943
+ * cb = Proc.new{ p :renamed }
944
+ * EIO.rename('/path/a', '/path/b', cb) => EIO::Request
945
+ *
946
+ * EIO.rename('/path/a', '/path/b') => Fixnum
947
+ *
948
+ */
949
+ static VALUE
950
+ rb_eio_s_rename(int argc, VALUE *argv, VALUE eio)
951
+ {
952
+ int ret;
953
+ VALUE path, new_path, proc, cb;
954
+ rb_scan_args(argc, argv, "21&", &path, &new_path, &proc, &cb);
955
+ AssertCallback(cb, NO_CB_ARGS);
956
+ Check_Type(path, T_STRING);
957
+ Check_Type(new_path, T_STRING);
958
+ SubmitRequest(rename, rb_eio_generic_cb, StringValueCStr(path), StringValueCStr(new_path));
959
+ }
960
+
961
+ /*
962
+ * call-seq:
963
+ * EIO.chmod('/path/file'){ p :chmodded } => EIO::Request
964
+ *
965
+ * Asynchronously change permissions for a given file path.
966
+ *
967
+ * === Examples
968
+ * EIO.chmod('/path/file', 0777){ p :chmodded } => EIO::Request
969
+ * cb = Proc.new{ p :chmodded }
970
+ * EIO.chmod('/path/file', 0777, cb) => EIO::Request
971
+ *
972
+ * EIO.chmod('/path/file') => Fixnum
973
+ * EIO.chmod('/path/file', 0777) => Fixnum
974
+ *
975
+ */
976
+ static VALUE
977
+ rb_eio_s_chmod(int argc, VALUE *argv, VALUE eio)
978
+ {
979
+ int ret;
980
+ VALUE path, mode, proc, cb;
981
+ rb_scan_args(argc, argv, "12&", &path, &mode, &proc, &cb);
982
+ AssertCallback(cb, NO_CB_ARGS);
983
+ Check_Type(path, T_STRING);
984
+ if (NIL_P(mode)) mode = eio_default_mode;
985
+ Check_Type(mode, T_FIXNUM);
986
+ SubmitRequest(chmod, rb_eio_generic_cb, StringValueCStr(path), FIX2INT(mode));
987
+ }
988
+
989
+ /*
990
+ * call-seq:
991
+ * EIO.fchmod(fd){ p :chmodded } => EIO::Request
992
+ *
993
+ * Asynchronously change ownership for a given file descriptor.
994
+ *
995
+ * === Examples
996
+ * EIO.fchmod(fd, 0777){ p :chmodded } => EIO::Request
997
+ * cb = Proc.new{ p :chmodded }
998
+ * EIO.fchmod(fd, 0777, cb) => EIO::Request
999
+ *
1000
+ * EIO.fchmod(fd) => Fixnum
1001
+ * EIO.fchmod(fd, 0777) => Fixnum
1002
+ *
1003
+ */
1004
+ static VALUE
1005
+ rb_eio_s_fchmod(int argc, VALUE *argv, VALUE eio)
1006
+ {
1007
+ int ret;
1008
+ VALUE fd, mode, proc, cb;
1009
+ rb_scan_args(argc, argv, "12&", &fd, &mode, &proc, &cb);
1010
+ AssertCallback(cb, NO_CB_ARGS);
1011
+ Check_Type(fd, T_FIXNUM);
1012
+ if (NIL_P(mode)) mode = eio_default_mode;
1013
+ Check_Type(mode, T_FIXNUM);
1014
+ SubmitRequest(fchmod, rb_eio_generic_cb, FIX2INT(fd), FIX2INT(mode));
1015
+ }
1016
+
1017
+ /*
1018
+ * call-seq:
1019
+ * EIO.truncate('/path/file'){ p :truncated } => EIO::Request
1020
+ *
1021
+ * Asynchronously truncates a given file path.
1022
+ *
1023
+ * === Examples
1024
+ * EIO.truncate('/path/file', 100){ p :truncated } => EIO::Request
1025
+ * cb = Proc.new{ p :truncated }
1026
+ * EIO.truncate('/path/file', 100, cb) => EIO::Request
1027
+ *
1028
+ * EIO.truncate('/path/file') => Fixnum
1029
+ * EIO.truncate('/path/file', 100) => Fixnum
1030
+ *
1031
+ */
1032
+ static VALUE
1033
+ rb_eio_s_truncate(int argc, VALUE *argv, VALUE eio)
1034
+ {
1035
+ int ret;
1036
+ VALUE path, offset, proc, cb;
1037
+ rb_scan_args(argc, argv, "12&", &path, &offset, &proc, &cb);
1038
+ AssertCallback(cb, NO_CB_ARGS);
1039
+ Check_Type(path, T_STRING);
1040
+ if (NIL_P(offset)) offset = eio_zero;
1041
+ Check_Type(offset, T_FIXNUM);
1042
+ SubmitRequest(truncate, rb_eio_generic_cb, StringValueCStr(path), FIX2INT(offset));
1043
+ }
1044
+
1045
+ /*
1046
+ * call-seq:
1047
+ * EIO.ftruncate(fd){ p :truncated } => EIO::Request
1048
+ *
1049
+ * Asynchronously truncates a given file descriptor.
1050
+ *
1051
+ * === Examples
1052
+ * EIO.ftruncate(fd, 100){ p :truncated } => EIO::Request
1053
+ * cb = Proc.new{ p :truncated }
1054
+ * EIO.ftruncate(fd, 100, cb) => EIO::Request
1055
+ *
1056
+ * EIO.ftruncate(fd) => Fixnum
1057
+ * EIO.ftruncate(fd, 100) => Fixnum
1058
+ *
1059
+ */
1060
+ static VALUE
1061
+ rb_eio_s_ftruncate(int argc, VALUE *argv, VALUE eio)
1062
+ {
1063
+ int ret;
1064
+ VALUE fd, offset, proc, cb;
1065
+ rb_scan_args(argc, argv, "12&", &fd, &offset, &proc, &cb);
1066
+ AssertCallback(cb, NO_CB_ARGS);
1067
+ Check_Type(fd, T_FIXNUM);
1068
+ if (NIL_P(offset)) offset = eio_zero;
1069
+ Check_Type(offset, T_FIXNUM);
1070
+ SubmitRequest(ftruncate, rb_eio_generic_cb, FIX2INT(fd), FIX2INT(offset));
1071
+ }
1072
+
1073
+ /*
1074
+ * call-seq:
1075
+ * EIO.chown('/path/file'){ p :chowned } => EIO::Request
1076
+ *
1077
+ * Asynchronously changes ownership for a given file path.
1078
+ *
1079
+ * === Examples
1080
+ * EIO.chown('/path/file', 500){ p :chowned } => EIO::Request
1081
+ * EIO.chown('/path/file', 500, 500){ p :chowned } => EIO::Request
1082
+ * cb = Proc.new{ p :chowned }
1083
+ * EIO.chown('/path/file', 500, 500, cb) => EIO::Request
1084
+ *
1085
+ * EIO.chown('/path/file', 500) => Fixnum
1086
+ * EIO.chown('/path/file', 500, 500) => Fixnum
1087
+ *
1088
+ */
1089
+ static VALUE
1090
+ rb_eio_s_chown(int argc, VALUE *argv, VALUE eio)
1091
+ {
1092
+ int ret;
1093
+ VALUE path, uid, gid, proc, cb;
1094
+ rb_scan_args(argc, argv, "13&", &path, &uid, &gid, &proc, &cb);
1095
+ AssertCallback(cb, NO_CB_ARGS);
1096
+ Check_Type(path, T_STRING);
1097
+ if (NIL_P(uid)) uid = INT2NUM(getuid());
1098
+ Check_Type(uid, T_FIXNUM);
1099
+ if (NIL_P(gid)) gid = INT2NUM(getgid());
1100
+ Check_Type(gid, T_FIXNUM);
1101
+ SubmitRequest(chown, rb_eio_generic_cb, StringValueCStr(path), FIX2INT(uid), FIX2INT(gid));
1102
+ }
1103
+
1104
+ /*
1105
+ * call-seq:
1106
+ * EIO.fchown(fd){ p :chowned } => EIO::Request
1107
+ *
1108
+ * Asynchronously changes ownership for a given file descriptor.
1109
+ *
1110
+ * === Examples
1111
+ * EIO.fchown(fd, 500){ p :chowned } => EIO::Request
1112
+ * EIO.fchown(fd, 500, 500){ p :chowned } => EIO::Request
1113
+ * cb = Proc.new{ p :chowned }
1114
+ * EIO.fchown(fd, 500, 500, cb) => EIO::Request
1115
+ *
1116
+ * EIO.fchown(fd, 500) => Fixnum
1117
+ * EIO.fchown(fd, 500, 500) => Fixnum
1118
+ *
1119
+ */
1120
+ static VALUE
1121
+ rb_eio_s_fchown(int argc, VALUE *argv, VALUE eio)
1122
+ {
1123
+ int ret;
1124
+ VALUE fd, uid, gid, proc, cb;
1125
+ rb_scan_args(argc, argv, "13&", &fd, &uid, &gid, &proc, &cb);
1126
+ AssertCallback(cb, NO_CB_ARGS);
1127
+ Check_Type(fd, T_FIXNUM);
1128
+ if (NIL_P(uid)) uid = INT2NUM(getuid());
1129
+ Check_Type(uid, T_FIXNUM);
1130
+ if (NIL_P(gid)) gid = INT2NUM(getgid());
1131
+ Check_Type(gid, T_FIXNUM);
1132
+ SubmitRequest(fchown, rb_eio_generic_cb, FIX2INT(fd), FIX2INT(uid), FIX2INT(gid));
1133
+ }
1134
+
1135
+ /*
1136
+ * call-seq:
1137
+ * EIO.link('/path/a', '/path/b'){ p :linked } => EIO::Request
1138
+ *
1139
+ * Asynchronously create a new link to the existing object at source path at the destination
1140
+ * path and call the callback with the result code.
1141
+ *
1142
+ * === Examples
1143
+ * cb = Proc.new{ p :linked }
1144
+ * EIO.link('/path/a', '/path/b', cb) => EIO::Request
1145
+ *
1146
+ * EIO.link('/path/a', '/path/b') => Fixnum
1147
+ *
1148
+ */
1149
+ static VALUE
1150
+ rb_eio_s_link(int argc, VALUE *argv, VALUE eio)
1151
+ {
1152
+ int ret;
1153
+ VALUE path, new_path, proc, cb;
1154
+ rb_scan_args(argc, argv, "21&", &path, &new_path, &proc, &cb);
1155
+ AssertCallback(cb, NO_CB_ARGS);
1156
+ Check_Type(path, T_STRING);
1157
+ Check_Type(new_path, T_STRING);
1158
+ SubmitRequest(link, rb_eio_generic_cb, StringValueCStr(path), StringValueCStr(new_path));
1159
+ }
1160
+
1161
+ /*
1162
+ * call-seq:
1163
+ * EIO.symlink('/path/a', '/path/b'){ p :linked } => EIO::Request
1164
+ *
1165
+ * Asynchronously create a new symbolic link to the existing object at sourc path at the
1166
+ * destination path and call the callback with the result code.
1167
+ *
1168
+ * === Examples
1169
+ * cb = Proc.new{ p :linked }
1170
+ * EIO.symlink('/path/a', '/path/b', cb) => EIO::Request
1171
+ *
1172
+ * EIO.symlink('/path/a', '/path/b') => Fixnum
1173
+ *
1174
+ */
1175
+ static VALUE
1176
+ rb_eio_s_symlink(int argc, VALUE *argv, VALUE eio)
1177
+ {
1178
+ int ret;
1179
+ VALUE path, new_path, proc, cb;
1180
+ rb_scan_args(argc, argv, "21&", &path, &new_path, &proc, &cb);
1181
+ AssertCallback(cb, NO_CB_ARGS);
1182
+ Check_Type(path, T_STRING);
1183
+ Check_Type(new_path, T_STRING);
1184
+ SubmitRequest(symlink, rb_eio_generic_cb, StringValueCStr(path), StringValueCStr(new_path));
1185
+ }
1186
+
1187
+ /*
1188
+ * Mark an EIO::Request instance
1189
+ */
1190
+ static void
1191
+ rb_eio_mark_request(eio_req *r)
1192
+ {
1193
+ }
1194
+
1195
+ /*
1196
+ * Free an EIO::Request instance
1197
+ */
1198
+ static void
1199
+ rb_eio_free_request(eio_req *r)
1200
+ {
1201
+ }
1202
+
1203
+ /*
1204
+ * Wraps an eio_req struct
1205
+ */
1206
+ static VALUE
1207
+ rb_eio_wrap_request(eio_req *r)
1208
+ {
1209
+ VALUE obj;
1210
+ obj = Data_Wrap_Struct(cEioReq, rb_eio_mark_request, rb_eio_free_request, r);
1211
+ rb_obj_call_init(obj, 0, NULL);
1212
+ return obj;
1213
+ }
1214
+
1215
+ /*
1216
+ * call-seq:
1217
+ * req.errno => Fixnum
1218
+ *
1219
+ * Request error number, if any.
1220
+ *
1221
+ */
1222
+ static VALUE
1223
+ rb_eio_req_errno(VALUE obj)
1224
+ {
1225
+ GetRequest(obj);
1226
+ return INT2NUM(req->errorno);
1227
+ }
1228
+
1229
+ /*
1230
+ * call-seq:
1231
+ * req.type => Fixnum
1232
+ *
1233
+ * Request type
1234
+ *
1235
+ */
1236
+ static VALUE
1237
+ rb_eio_req_type(VALUE obj)
1238
+ {
1239
+ GetRequest(obj);
1240
+ return INT2NUM(req->type);
1241
+ }
1242
+
1243
+ /*
1244
+ * call-seq:
1245
+ * req.priority => Fixnum
1246
+ *
1247
+ * Request priority
1248
+ *
1249
+ */
1250
+ static VALUE
1251
+ rb_eio_req_priority(VALUE obj)
1252
+ {
1253
+ GetRequest(obj);
1254
+ return INT2NUM(req->pri);
1255
+ }
1256
+
1257
+ /*
1258
+ * call-seq:
1259
+ * req.cancel => nil
1260
+ *
1261
+ * Attempt to cancel an in flight libeio request - no guarantees.
1262
+ *
1263
+ */
1264
+ static VALUE
1265
+ rb_eio_req_cancel(VALUE obj)
1266
+ {
1267
+ GetRequest(obj);
1268
+ eio_cancel(req);
1269
+ return Qnil;
1270
+ }
1271
+
1272
+ /*
1273
+ * call-seq:
1274
+ * req.complete? => Boolean
1275
+ *
1276
+ * True if the Ruby callback for this request already fired.
1277
+ *
1278
+ */
1279
+ static VALUE
1280
+ rb_eio_req_complete_p(VALUE obj)
1281
+ {
1282
+ GetRequest(obj);
1283
+ return (req->complete == 1) ? Qtrue : Qfalse;
1284
+ }
1285
+
1286
+ /*
1287
+ * Get the fd from a given I/O instance
1288
+ */
1289
+ static int
1290
+ rb_eio_pipe_fd(VALUE io)
1291
+ {
1292
+ rb_io_t *fptr;
1293
+ GetOpenFile(io, fptr);
1294
+ #ifdef RUBY_VM
1295
+ return fptr->fd;
1296
+ #else
1297
+ return fileno(fptr->f);
1298
+ #endif
1299
+ }
1300
+
1301
+ /*
1302
+ * create the libeio notify pipe
1303
+ */
1304
+ static void
1305
+ rb_eio_create_pipe(void)
1306
+ {
1307
+ VALUE pipe, pipe_r_fd, pipe_w_fd;
1308
+ pipe = rb_funcall(rb_cIO, sym_pipe, 0);
1309
+
1310
+ rb_gc_register_address(&pipe_r_fd);
1311
+ pipe_r_fd = rb_ary_shift(pipe);
1312
+
1313
+ rb_gc_register_address(&pipe_w_fd);
1314
+ pipe_w_fd = rb_ary_shift(pipe);
1315
+
1316
+ rb_ivar_set(mEio, sym_pipe_r_fd, pipe_r_fd);
1317
+ rb_ivar_set(mEio, sym_pipe_w_fd, pipe_w_fd);
1318
+
1319
+ eio_pipe_r_fd = rb_eio_pipe_fd(pipe_r_fd);
1320
+ eio_pipe_w_fd = rb_eio_pipe_fd(pipe_w_fd);
1321
+ }
1322
+
1323
+ /* recreate the libeio notify pipe */
1324
+ static void
1325
+ rb_eio_recreate_pipe(void)
1326
+ {
1327
+ rb_eio_create_pipe();
1328
+ }
1329
+
1330
+ /* recreate the libeio notify pipe on fork */
1331
+ static void
1332
+ rb_eio_atfork(void)
1333
+ {
1334
+ rb_eio_recreate_pipe();
1335
+ }
1336
+
1337
+ void
1338
+ Init_eio_ext()
1339
+ {
1340
+ /* Initializes libeio */
1341
+ if (eio_init(want_poll, done_poll) < 0) rb_sys_fail("EIO init failed!");
1342
+
1343
+ mEio = rb_define_module("EIO");
1344
+
1345
+ /* Init symbols ahead of time */
1346
+ sym_call = rb_intern("call");
1347
+ sym_arity = rb_intern("arity");
1348
+ sym_pipe = rb_intern("pipe");
1349
+ sym_readlink = rb_intern("readlink");
1350
+ sym_stat = rb_intern("stat");
1351
+ sym_pipe_r_fd = rb_intern("pipe_r_fd");
1352
+ sym_pipe_w_fd = rb_intern("pipe_w_fd");
1353
+
1354
+ /* Common fixnum defaults */
1355
+ eio_default_mode = INT2NUM(0777);
1356
+ eio_zero = INT2NUM(0);
1357
+ eio_default_bufsize = INT2NUM(BUFSIZ);
1358
+
1359
+ /* Setup a communication pipe between libeio and other I/O frameworks */
1360
+ rb_eio_create_pipe();
1361
+
1362
+ /* Recreate pipe on fork */
1363
+ X_THREAD_ATFORK(0, 0, rb_eio_atfork);
1364
+
1365
+ rb_define_const(mEio, "PRI_MIN", INT2NUM(EIO_PRI_MIN));
1366
+ rb_define_const(mEio, "PRI_MAX", INT2NUM(EIO_PRI_MAX));
1367
+ rb_define_const(mEio, "PRI_DEFAULT", INT2NUM(EIO_PRI_DEFAULT));
1368
+
1369
+ rb_define_const(mEio, "RDONLY", INT2NUM(O_RDONLY));
1370
+ rb_define_const(mEio, "WRONLY", INT2NUM(O_WRONLY));
1371
+ rb_define_const(mEio, "RDWR", INT2NUM(O_RDWR));
1372
+ rb_define_const(mEio, "APPEND", INT2NUM(O_APPEND));
1373
+ rb_define_const(mEio, "CREAT", INT2NUM(O_CREAT));
1374
+ rb_define_const(mEio, "EXCL", INT2NUM(O_EXCL));
1375
+
1376
+ rb_define_module_function(mEio, "poll", rb_eio_s_poll, 0);
1377
+ rb_define_module_function(mEio, "wait", rb_eio_s_wait, 0);
1378
+ rb_define_module_function(mEio, "requests", rb_eio_s_requests, 0);
1379
+ rb_define_module_function(mEio, "ready", rb_eio_s_ready, 0);
1380
+ rb_define_module_function(mEio, "pending", rb_eio_s_pending, 0);
1381
+ rb_define_module_function(mEio, "threads", rb_eio_s_threads, 0);
1382
+ rb_define_module_function(mEio, "fd", rb_eio_s_fd, 0);
1383
+
1384
+ rb_define_module_function(mEio, "max_poll_time=", rb_eio_s_set_max_poll_time, 1);
1385
+ rb_define_module_function(mEio, "max_poll_reqs=", rb_eio_s_set_max_poll_reqs, 1);
1386
+ rb_define_module_function(mEio, "min_parallel=", rb_eio_s_set_min_parallel, 1);
1387
+ rb_define_module_function(mEio, "max_parallel=", rb_eio_s_set_max_parallel, 1);
1388
+ rb_define_module_function(mEio, "max_idle=", rb_eio_s_set_max_idle, 1);
1389
+ rb_define_module_function(mEio, "idle_timeout=", rb_eio_s_set_idle_timeout, 1);
1390
+
1391
+ rb_define_module_function(mEio, "fsync", rb_eio_s_fsync, -1);
1392
+ rb_define_module_function(mEio, "fdatasync", rb_eio_s_fdatasync, -1);
1393
+ rb_define_module_function(mEio, "open", rb_eio_s_open, -1);
1394
+ rb_define_module_function(mEio, "close", rb_eio_s_close, -1);
1395
+ rb_define_module_function(mEio, "read", rb_eio_s_read, -1);
1396
+ rb_define_module_function(mEio, "readahead", rb_eio_s_readahead, -1);
1397
+ rb_define_module_function(mEio, "write", rb_eio_s_write, -1);
1398
+ rb_define_module_function(mEio, "sendfile", rb_eio_s_sendfile, -1);
1399
+ rb_define_module_function(mEio, "mkdir", rb_eio_s_mkdir, -1);
1400
+ rb_define_module_function(mEio, "rmdir", rb_eio_s_rmdir, -1);
1401
+ rb_define_module_function(mEio, "unlink", rb_eio_s_unlink, -1);
1402
+ rb_define_module_function(mEio, "rename", rb_eio_s_rename, -1);
1403
+ rb_define_module_function(mEio, "chmod", rb_eio_s_chmod, -1);
1404
+ rb_define_module_function(mEio, "fchmod", rb_eio_s_fchmod, -1);
1405
+ rb_define_module_function(mEio, "truncate", rb_eio_s_truncate, -1);
1406
+ rb_define_module_function(mEio, "ftruncate", rb_eio_s_ftruncate, -1);
1407
+ rb_define_module_function(mEio, "chown", rb_eio_s_chown, -1);
1408
+ rb_define_module_function(mEio, "fchown", rb_eio_s_fchown, -1);
1409
+ rb_define_module_function(mEio, "link", rb_eio_s_link, -1);
1410
+ rb_define_module_function(mEio, "readlink", rb_eio_s_readlink, -1);
1411
+ rb_define_module_function(mEio, "symlink", rb_eio_s_symlink, -1);
1412
+ rb_define_module_function(mEio, "readdir", rb_eio_s_readdir, -1);
1413
+ rb_define_module_function(mEio, "stat", rb_eio_s_stat, -1);
1414
+
1415
+ cEioReq = rb_define_class_under(mEio, "Request", rb_cObject);
1416
+
1417
+ rb_define_method(cEioReq, "errno", rb_eio_req_errno, 0);
1418
+ rb_define_method(cEioReq, "type", rb_eio_req_type, 0);
1419
+ rb_define_method(cEioReq, "priority", rb_eio_req_priority, 0);
1420
+ rb_define_method(cEioReq, "cancel", rb_eio_req_cancel, 0);
1421
+ rb_define_method(cEioReq, "complete?", rb_eio_req_complete_p, 0);
1422
+
1423
+ rb_define_const(cEioReq, "OPEN", INT2NUM(EIO_OPEN));
1424
+ rb_define_const(cEioReq, "CLOSE", INT2NUM(EIO_CLOSE));
1425
+ rb_define_const(cEioReq, "READ", INT2NUM(EIO_READ));
1426
+ rb_define_const(cEioReq, "WRITE", INT2NUM(EIO_WRITE));
1427
+ rb_define_const(cEioReq, "READAHEAD", INT2NUM(EIO_READAHEAD));
1428
+ rb_define_const(cEioReq, "SENDFILE", INT2NUM(EIO_SENDFILE));
1429
+ rb_define_const(cEioReq, "STAT", INT2NUM(EIO_STAT));
1430
+ rb_define_const(cEioReq, "TRUNCATE", INT2NUM(EIO_TRUNCATE));
1431
+ rb_define_const(cEioReq, "FTRUNCATE", INT2NUM(EIO_FTRUNCATE));
1432
+ rb_define_const(cEioReq, "CHMOD", INT2NUM(EIO_CHMOD));
1433
+ rb_define_const(cEioReq, "FCHMOD", INT2NUM(EIO_FCHMOD));
1434
+ rb_define_const(cEioReq, "CHOWN", INT2NUM(EIO_CHOWN));
1435
+ rb_define_const(cEioReq, "FCHOWN", INT2NUM(EIO_FCHOWN));
1436
+ rb_define_const(cEioReq, "SYNC", INT2NUM(EIO_SYNC));
1437
+ rb_define_const(cEioReq, "FSYNC", INT2NUM(EIO_FSYNC));
1438
+ rb_define_const(cEioReq, "FDATASYNC", INT2NUM(EIO_FDATASYNC));
1439
+ rb_define_const(cEioReq, "UNLINK", INT2NUM(EIO_UNLINK));
1440
+ rb_define_const(cEioReq, "RMDIR", INT2NUM(EIO_RMDIR));
1441
+ rb_define_const(cEioReq, "MKDIR", INT2NUM(EIO_MKDIR));
1442
+ rb_define_const(cEioReq, "RENAME", INT2NUM(EIO_RENAME));
1443
+ rb_define_const(cEioReq, "READDIR", INT2NUM(EIO_READDIR));
1444
+ rb_define_const(cEioReq, "LINK", INT2NUM(EIO_LINK));
1445
+ rb_define_const(cEioReq, "SYMLINK", INT2NUM(EIO_SYMLINK));
1446
+ rb_define_const(cEioReq, "READLINK", INT2NUM(EIO_READLINK));
1447
+ }