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.
- data/.gitignore +8 -0
- data/COPYING +502 -0
- data/LICENSE +16 -0
- data/README.rdoc +201 -0
- data/Rakefile +48 -0
- data/bench/eventmachine.rb +134 -0
- data/eio.gemspec +17 -0
- data/ext/eio/eio_ext.c +1447 -0
- data/ext/eio/extconf.rb +11 -0
- data/ext/libeio/CVS/Entries +13 -0
- data/ext/libeio/CVS/Repository +1 -0
- data/ext/libeio/CVS/Root +1 -0
- data/ext/libeio/Changes +40 -0
- data/ext/libeio/LICENSE +36 -0
- data/ext/libeio/Makefile +692 -0
- data/ext/libeio/Makefile.am +15 -0
- data/ext/libeio/Makefile.in +692 -0
- data/ext/libeio/aclocal.m4 +8937 -0
- data/ext/libeio/autogen.sh +3 -0
- data/ext/libeio/autom4te.cache/output.0 +13871 -0
- data/ext/libeio/autom4te.cache/output.1 +13867 -0
- data/ext/libeio/autom4te.cache/requests +275 -0
- data/ext/libeio/autom4te.cache/traces.0 +2384 -0
- data/ext/libeio/autom4te.cache/traces.1 +621 -0
- data/ext/libeio/config.guess +1501 -0
- data/ext/libeio/config.h +122 -0
- data/ext/libeio/config.h.in +121 -0
- data/ext/libeio/config.status +2035 -0
- data/ext/libeio/config.sub +1705 -0
- data/ext/libeio/configure +13867 -0
- data/ext/libeio/configure.ac +22 -0
- data/ext/libeio/demo.c +194 -0
- data/ext/libeio/eio.3 +3428 -0
- data/ext/libeio/eio.c +2075 -0
- data/ext/libeio/eio.h +336 -0
- data/ext/libeio/eio.pod +303 -0
- data/ext/libeio/install-sh +520 -0
- data/ext/libeio/libeio.m4 +156 -0
- data/ext/libeio/libtool +8890 -0
- data/ext/libeio/ltmain.sh +8406 -0
- data/ext/libeio/missing +376 -0
- data/ext/libeio/stamp-h1 +1 -0
- data/ext/libeio/xthread.h +168 -0
- data/lib/eio.rb +9 -0
- data/lib/eio/eventmachine.rb +24 -0
- data/lib/eio/middleware.rb +21 -0
- data/test/test_eio.rb +1161 -0
- data/test/test_eventmachine.rb +23 -0
- data/test/test_middleware.rb +20 -0
- 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
|
data/README.rdoc
ADDED
@@ -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 !
|
data/Rakefile
ADDED
@@ -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
|
data/eio.gemspec
ADDED
@@ -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
|
data/ext/eio/eio_ext.c
ADDED
@@ -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
|
+
}
|