eio 0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+ }