io_splice 2.2.0 → 2.2.0.18.g3025

Sign up to get free protection for your applications and to get access to all the features.
data/.document CHANGED
@@ -4,3 +4,4 @@ NEWS
4
4
  ChangeLog
5
5
  lib
6
6
  ext/io_splice/io_splice_ext.c
7
+ LATEST
data/.gitignore CHANGED
@@ -13,3 +13,5 @@ Makefile
13
13
  /pkg
14
14
  /doc
15
15
  /coverage
16
+ LATEST
17
+ /tmp
data/.wrongdoc.yml ADDED
@@ -0,0 +1,5 @@
1
+ ---
2
+ cgit_url: http://bogomips.org/ruby_io_splice.git
3
+ git_url: git://bogomips.org/ruby_io_splice.git
4
+ rdoc_url: http://bogomips.org/ruby_io_splice/
5
+ changelog_start: v1.0.0
data/GNUmakefile CHANGED
@@ -1,200 +1,14 @@
1
- # use GNU Make to run tests in parallel, and without depending on RubyGems
2
1
  all::
3
- RUBY = ruby
4
- RAKE = rake
5
- GIT_URL = git://git.bogomips.org/ruby_io_splice.git
6
- RSYNC = rsync
7
2
  RCOV = rcov
8
-
9
- GIT-VERSION-FILE: .FORCE-GIT-VERSION-FILE
10
- @./GIT-VERSION-GEN
11
- -include GIT-VERSION-FILE
12
- -include local.mk
13
- ifeq ($(DLEXT),) # "so" for Linux
14
- DLEXT := $(shell $(RUBY) -rrbconfig -e 'puts Config::CONFIG["DLEXT"]')
15
- endif
16
- ifeq ($(RUBY_VERSION),)
17
- RUBY_VERSION := $(shell $(RUBY) -e 'puts RUBY_VERSION')
18
- endif
19
-
20
- install: $(bins)
21
- $(prep_setup_rb)
22
- $(RM) -r .install-tmp
23
- mkdir .install-tmp
24
- cp -p bin/* .install-tmp
25
- $(RUBY) setup.rb all
26
- $(RM) $^
27
- mv .install-tmp/* bin/
28
- $(RM) -r .install-tmp
29
- $(prep_setup_rb)
30
-
31
- setup_rb_files := .config InstalledFiles
32
- prep_setup_rb := @-$(RM) $(setup_rb_files);$(MAKE) -C $(ext) clean
33
-
34
- clean:
35
- -$(MAKE) -C ext/io_splice clean
36
- $(RM) $(setup_rb_files) ext/io_splice/Makefile
37
-
38
- pkg_extra := GIT-VERSION-FILE NEWS ChangeLog
39
- manifest: $(pkg_extra)
40
- $(RM) .manifest
41
- $(MAKE) .manifest
42
-
43
- .manifest:
44
- (git ls-files && \
45
- for i in $@ $(pkg_extra); \
46
- do echo $$i; done) | LC_ALL=C sort > $@+
47
- cmp $@+ $@ || mv $@+ $@
48
- $(RM) $@+
49
-
50
- NEWS: GIT-VERSION-FILE
51
- $(RAKE) -s news_rdoc > $@+
52
- mv $@+ $@
53
-
54
- SINCE = 1.0.0
55
- ChangeLog: LOG_VERSION = \
56
- $(shell git rev-parse -q "$(GIT_VERSION)" >/dev/null 2>&1 && \
57
- echo $(GIT_VERSION) || git describe)
58
- ifneq ($(SINCE),)
59
- ChangeLog: log_range = v$(SINCE)..$(LOG_VERSION)
60
- endif
61
- ChangeLog: GIT-VERSION-FILE
62
- @echo "ChangeLog from $(GIT_URL) ($(log_range))" > $@+
63
- @echo >> $@+
64
- git log $(log_range) | sed -e 's/^/ /' >> $@+
65
- mv $@+ $@
66
-
67
- news_atom := http://bogomips.org/ruby_io_splice/NEWS.atom.xml
68
- cgit_atom := http://git.bogomips.org/cgit/ruby_io_splice.git/atom/?h=master
69
- atom = <link rel="alternate" title="Atom feed" href="$(1)" \
70
- type="application/atom+xml"/>
71
-
72
- # using rdoc 2.5.x + workaround patch here:
73
- # rubyforge.org/tracker/index.php?func=detail&aid=28230&group_id=627&atid=2472
74
- doc: .document NEWS ChangeLog
75
- rdoc -a -t "$(shell sed -ne '1s/^= //p' README)"
76
- install -m644 COPYING doc/COPYING
77
- install -m644 $(shell grep '^[A-Z]' .document) doc/
78
- cd doc && for i in $(base_bins); do \
79
- html=$$(echo $$i | sed 's/\.rb/_rb/')_1.html; \
80
- sed -e '/"documentation">/r man1/'$$i'.1.html' \
81
- < $$html > tmp && mv tmp $$html; done
82
- $(RUBY) -i -p -e \
83
- '$$_.gsub!("</title>",%q{\&$(call atom,$(cgit_atom))})' \
84
- doc/ChangeLog.html
85
- $(RUBY) -i -p -e \
86
- '$$_.gsub!("</title>",%q{\&$(call atom,$(news_atom))})' \
87
- doc/NEWS.html doc/README.html
88
- $(RAKE) -s news_atom > doc/NEWS.atom.xml
89
- cd doc && ln README.html tmp && mv tmp index.html
90
-
91
- latest: NEWS
92
- @awk 'BEGIN{RS="=== ";ORS=""}NR==2{sub(/\n$$/,"");print RS""$$0 }' $<
93
-
94
- # publishes docs to http://bogomips.org/ruby_io_splice/,
95
- publish_doc:
96
- -git set-file-times
97
- $(RM) -r doc ChangeLog NEWS
98
- $(MAKE) doc LOG_VERSION=$(shell git tag -l | tail -1)
99
- awk 'BEGIN{RS="=== ";ORS=""}NR==2{sub(/\n$$/,"");print RS""$$0 }' \
100
- NEWS > doc/LATEST
101
- find doc/images doc/js -type f | \
102
- TZ=UTC xargs touch -d '1970-01-01 00:00:01' doc/rdoc.css
103
- $(MAKE) doc_gz
104
- chmod 644 $$(find doc -type f)
105
- $(RSYNC) -av doc/ bogomips.org:/srv/bogomips/ruby_io_splice/
106
- git ls-files | xargs touch
107
-
108
- # Create gzip variants of the same timestamp as the original so nginx
109
- # "gzip_static on" can serve the gzipped versions directly.
110
- doc_gz: docs = $(shell find doc -type f ! -regex '^.*\.\(gif\|jpg\|png\|gz\)$$')
111
- doc_gz:
112
- touch doc/NEWS.atom.xml -d "$$(awk 'NR==1{print $$4,$$5,$$6}' NEWS)"
113
- for i in $(docs); do \
114
- gzip --rsyncable -9 < $$i > $$i.gz; touch -r $$i $$i.gz; done
115
-
116
- ifneq ($(VERSION),)
3
+ RSYNC_DEST := bogomips.org:/srv/bogomips/ruby_io_splice
117
4
  rfproject := qrp
118
5
  rfpackage := io_splice
119
- pkggem := pkg/$(rfpackage)-$(VERSION).gem
120
- pkgtgz := pkg/$(rfpackage)-$(VERSION).tgz
121
- release_notes := release_notes-$(VERSION)
122
- release_changes := release_changes-$(VERSION)
123
-
124
- release-notes: $(release_notes)
125
- release-changes: $(release_changes)
126
- $(release_changes):
127
- $(RAKE) -s release_changes > $@+
128
- $(VISUAL) $@+ && test -s $@+ && mv $@+ $@
129
- $(release_notes):
130
- GIT_URL=$(GIT_URL) $(RAKE) -s release_notes > $@+
131
- $(VISUAL) $@+ && test -s $@+ && mv $@+ $@
132
-
133
- # ensures we're actually on the tagged $(VERSION), only used for release
134
- verify:
135
- test x"$(shell umask)" = x0022
136
- git rev-parse --verify refs/tags/v$(VERSION)^{}
137
- git diff-index --quiet HEAD^0
138
- test `git rev-parse --verify HEAD^0` = \
139
- `git rev-parse --verify refs/tags/v$(VERSION)^{}`
140
-
141
- fix-perms:
142
- -git ls-tree -r HEAD | awk '/^100644 / {print $$NF}' | xargs chmod 644
143
- -git ls-tree -r HEAD | awk '/^100755 / {print $$NF}' | xargs chmod 755
144
-
145
- gem: $(pkggem)
146
-
147
- install-gem: $(pkggem)
148
- gem install $(CURDIR)/$<
149
-
150
- $(pkggem): manifest fix-perms
151
- gem build $(rfpackage).gemspec
152
- mkdir -p pkg
153
- mv $(@F) $@
154
-
155
- $(pkgtgz): distdir = $(basename $@)
156
- $(pkgtgz): HEAD = v$(VERSION)
157
- $(pkgtgz): manifest fix-perms
158
- @test -n "$(distdir)"
159
- $(RM) -r $(distdir)
160
- mkdir -p $(distdir)
161
- tar c `cat .manifest` | (cd $(distdir) && tar x)
162
- cd pkg && tar c $(basename $(@F)) | gzip -9 > $(@F)+
163
- mv $@+ $@
164
-
165
- package: $(pkgtgz) $(pkggem)
166
-
167
- test-release: verify package $(release_notes) $(release_changes)
168
- release: verify package $(release_notes) $(release_changes)
169
- # make tgz release on RubyForge
170
- rubyforge add_release -f -n $(release_notes) -a $(release_changes) \
171
- $(rfproject) $(rfpackage) $(VERSION) $(pkgtgz)
172
- # push gem to Gemcutter
173
- gem push $(pkggem)
174
- # in case of gem downloads from RubyForge releases page
175
- -rubyforge add_file \
176
- $(rfproject) $(rfpackage) $(VERSION) $(pkggem)
177
- else
178
- gem install-gem: GIT-VERSION-FILE
179
- $(MAKE) $@ VERSION=$(GIT_VERSION)
6
+ include pkg.mk
7
+ ifneq ($(VERSION),)
8
+ release::
9
+ $(RAKE) raa_update VERSION=$(VERSION)
10
+ $(RAKE) publish_news VERSION=$(VERSION)
180
11
  endif
181
-
182
- ext := ext/io_splice/io_splice_ext.$(DLEXT)
183
- ext/io_splice/Makefile: ext/io_splice/extconf.rb
184
- cd $(@D) && $(RUBY) extconf.rb
185
- $(ext): $(wildcard $(addprefix ext/io_splice/,*.c *.h)) ext/io_splice/Makefile
186
- $(MAKE) -C $(@D)
187
-
188
- all:: test
189
-
190
- build: $(ext)
191
- test: test-unit
192
12
  rcov: build
193
13
  $(RCOV) -I lib:ext/io_splice $(test_unit)
194
-
195
- test_unit := $(wildcard test/test_*.rb)
196
- $(test_unit): build
197
- $(RUBY) -I lib:ext/io_splice $@ $(TEST_UNIT_OPTS)
198
- test-unit: $(test_unit)
199
-
200
- .PHONY: .FORCE-GIT-VERSION-FILE doc manifest man test $(test_unit)
14
+ .PHONY: rcov
data/README CHANGED
@@ -66,13 +66,13 @@ standardized and available in non-Linux kernels some day.
66
66
 
67
67
  You can get the latest source via git from the following locations:
68
68
 
69
- git://git.bogomips.org/ruby_io_splice.git
69
+ git://bogomips.org/ruby_io_splice.git
70
70
  git://repo.or.cz/ruby_io_splice.git (mirror)
71
71
 
72
72
  You may browse the code from the web and download the latest snapshot
73
73
  tarballs here:
74
74
 
75
- * http://git.bogomips.org/cgit/ruby_io_splice.git (cgit)
75
+ * http://bogomips.org/cgit/ruby_io_splice.git (cgit)
76
76
  * http://repo.or.cz/w/ruby_io_splice.git (gitweb)
77
77
 
78
78
  Inline patches (from "git format-patch") to the mailing list are
data/Rakefile CHANGED
@@ -1,112 +1,26 @@
1
1
  # -*- encoding: binary -*-
2
+ require 'wrongdoc'
3
+ cgit_url = Wrongdoc.config[:cgit_url]
4
+ git_url = Wrongdoc.config[:git_url]
2
5
 
3
- # most tasks are in the GNUmakefile which offers better parallelism
4
-
5
- def tags
6
- timefmt = '%Y-%m-%dT%H:%M:%SZ'
7
- @tags ||= `git tag -l`.split(/\n/).map do |tag|
8
- if %r{\Av[\d\.]+\z} =~ tag
9
- header, subject, body = `git cat-file tag #{tag}`.split(/\n\n/, 3)
10
- header = header.split(/\n/)
11
- tagger = header.grep(/\Atagger /).first
12
- body ||= "initial"
13
- {
14
- :time => Time.at(tagger.split(/ /)[-2].to_i).utc.strftime(timefmt),
15
- :tagger_name => %r{^tagger ([^<]+)}.match(tagger)[1].strip,
16
- :tagger_email => %r{<([^>]+)>}.match(tagger)[1].strip,
17
- :id => `git rev-parse refs/tags/#{tag}`.chomp!,
18
- :tag => tag,
19
- :subject => subject,
20
- :body => body,
21
- }
22
- end
23
- end.compact.sort { |a,b| b[:time] <=> a[:time] }
24
- end
25
-
26
- cgit_url = "http://git.bogomips.org/cgit/ruby_io_splice.git"
27
- git_url = ENV['GIT_URL'] || 'git://git.bogomips.org/ruby_io_splice.git'
28
- web_url = "http://bogomips.org/ruby_io_splice/"
29
-
30
- desc 'prints news as an Atom feed'
31
- task :news_atom do
32
- require 'nokogiri'
33
- new_tags = tags[0,10]
34
- puts(Nokogiri::XML::Builder.new do
35
- feed :xmlns => "http://www.w3.org/2005/Atom" do
36
- id! "#{web_url}NEWS.atom.xml"
37
- title "Ruby io_splice news"
38
- subtitle "splice and tee Linux syscalls from Ruby"
39
- link! :rel => "alternate", :type => "text/html",
40
- :href => "#{web_url}NEWS.html"
41
- updated(new_tags.empty? ? "1970-01-01T00:00:00Z" : new_tags.first[:time])
42
- new_tags.each do |tag|
43
- entry do
44
- title tag[:subject]
45
- updated tag[:time]
46
- published tag[:time]
47
- author {
48
- name tag[:tagger_name]
49
- email tag[:tagger_email]
50
- }
51
- url = "#{cgit_url}/tag/?id=#{tag[:tag]}"
52
- link! :rel => "alternate", :type => "text/html", :href =>url
53
- id! url
54
- message_only = tag[:body].split(/\n.+\(\d+\):\n {6}/s).first.strip
55
- content({:type =>:text}, message_only)
56
- content(:type =>:xhtml) { pre tag[:body] }
57
- end
58
- end
59
- end
60
- end.to_xml)
61
- end
62
-
63
- desc 'prints RDoc-formatted news'
64
- task :news_rdoc do
65
- tags.each do |tag|
66
- time = tag[:time].tr!('T', ' ').gsub!(/:\d\dZ/, ' UTC')
67
- puts "=== #{tag[:tag].sub(/^v/, '')} / #{time}"
68
- puts ""
69
-
70
- body = tag[:body]
71
- puts tag[:body].gsub(/^/sm, " ").gsub(/[ \t]+$/sm, "")
72
- puts ""
73
- end
74
- end
75
-
76
- desc "print release changelog for Rubyforge"
77
- task :release_changes do
78
- version = ENV['VERSION'] or abort "VERSION= needed"
79
- version = "v#{version}"
80
- vtags = tags.map { |tag| tag[:tag] =~ /\Av/ and tag[:tag] }.sort
81
- prev = vtags[vtags.index(version) - 1]
82
- if prev
83
- system('git', 'diff', '--stat', prev, version) or abort $?
84
- puts ""
85
- system('git', 'log', "#{prev}..#{version}") or abort $?
86
- else
87
- system('git', 'log', version) or abort $?
88
- end
89
- end
90
-
91
- desc "print release notes for Rubyforge"
92
- task :release_notes do
93
- spec = Gem::Specification.load('io_splice.gemspec')
94
- puts spec.description.strip
95
- puts ""
96
- puts "* #{spec.homepage}"
97
- puts "* #{spec.email}"
98
- puts "* #{git_url}"
99
-
100
- _, _, body = `git cat-file tag v#{spec.version}`.split(/\n\n/, 3)
101
- print "\nChanges:\n\n"
102
- puts body
103
- end
104
-
105
- desc "read news article from STDIN and post to rubyforge"
6
+ desc "post news article to rubyforge"
106
7
  task :publish_news do
107
8
  require 'rubyforge'
108
- IO.select([STDIN], nil, nil, 1) or abort "E: news must be read from stdin"
109
- msg = STDIN.readlines
9
+ spec = Gem::Specification.load('io_splice.gemspec')
10
+ tmp = Tempfile.new('rf-news')
11
+ _, subject, body = `git cat-file tag v#{spec.version}`.split(/\n\n/, 3)
12
+ tmp.puts subject
13
+ tmp.puts
14
+ tmp.puts spec.description.strip
15
+ tmp.puts ""
16
+ tmp.puts "* #{spec.homepage}"
17
+ tmp.puts "* #{spec.email}"
18
+ tmp.puts "* #{git_url}"
19
+ tmp.print "\nChanges:\n\n"
20
+ tmp.puts body
21
+ tmp.flush
22
+ system(ENV["VISUAL"], tmp.path) or abort "#{ENV["VISUAL"]} failed: #$?"
23
+ msg = File.readlines(tmp.path)
110
24
  subject = msg.shift
111
25
  blank = msg.shift
112
26
  blank == "\n" or abort "no newline after subject!"
@@ -144,7 +58,7 @@ task :raa_update do
144
58
  :category_minor => 'System',
145
59
  :url => s.homepage,
146
60
  :download => 'http://rubyforge.org/frs/?group_id=5626',
147
- :license => 'LGPL',
61
+ :license => "LGPL",
148
62
  :description_style => 'Plain',
149
63
  :description => desc,
150
64
  :pass => password,
@@ -12,6 +12,8 @@
12
12
  #include <alloca.h>
13
13
  #include <sys/utsname.h>
14
14
 
15
+ static VALUE sym_EAGAIN;
16
+
15
17
  #ifndef F_LINUX_SPECIFIC_BASE
16
18
  # define F_LINUX_SPECIFIC_BASE 1024
17
19
  #endif
@@ -88,8 +90,14 @@ rb_thread_blocking_region(
88
90
  # define RARRAY_LEN(s) (RARRAY(s)->len)
89
91
  #endif
90
92
 
93
+ static VALUE io_run(rb_blocking_function_t *fn, void *data)
94
+ {
95
+ return rb_thread_blocking_region(fn, data, RUBY_UBF_IO, 0);
96
+ }
97
+
91
98
  /*
92
99
  * Releases GVL only iff blocking I/O is used.
100
+ * Only use this if all file descriptors in data are pipes.
93
101
  * We'll trust programmers who use non-blocking I/O explicitly to
94
102
  * want the fastest possible performance without resorting to threads,
95
103
  * so releasing and them immediately reacquiring the GVL would be
@@ -99,7 +107,7 @@ static VALUE nb_io_run(rb_blocking_function_t *fn, void *data, unsigned flags)
99
107
  {
100
108
  if (flags & SPLICE_F_NONBLOCK)
101
109
  return fn(data);
102
- return rb_thread_blocking_region(fn, data, RUBY_UBF_IO, 0);
110
+ return io_run(fn, data);
103
111
  }
104
112
 
105
113
  struct splice_args {
@@ -119,8 +127,28 @@ static VALUE nogvl_splice(void *ptr)
119
127
  a->len, a->flags);
120
128
  }
121
129
 
130
+ static long do_splice(int argc, VALUE *argv, unsigned dflags)
131
+ {
132
+ off_t i, o;
133
+ VALUE fd_in, off_in, fd_out, off_out, len, flags;
134
+ struct splice_args a;
135
+
136
+ rb_scan_args(argc, argv, "51",
137
+ &fd_in, &off_in, &fd_out, &off_out, &len, &flags);
138
+
139
+ a.off_in = NIL_P(off_in) ? NULL : (i = NUM2OFFT(off_in), &i);
140
+ a.off_out = NIL_P(off_out) ? NULL : (o = NUM2OFFT(off_out), &o);
141
+ a.fd_in = my_fileno(fd_in);
142
+ a.fd_out = my_fileno(fd_out);
143
+ a.len = (size_t)NUM2ULONG(len);
144
+ a.flags = NIL_P(flags) ? dflags : NUM2UINT(flags) | dflags;
145
+
146
+ return (long)io_run(nogvl_splice, &a);
147
+ }
148
+
122
149
  /*
123
150
  * call-seq:
151
+ * IO.splice(fd_in, off_in, fd_out, off_out, len) => integer
124
152
  * IO.splice(fd_in, off_in, fd_out, off_out, len, flags) => integer
125
153
  *
126
154
  * Splice +len+ bytes from/to a pipe. Either +fd_in+ or +fd_out+
@@ -130,6 +158,7 @@ static VALUE nogvl_splice(void *ptr)
130
158
  * +off_in+ and +off_out+ if non-nil may be used to
131
159
  * specify an offset for the non-pipe file descriptor.
132
160
  *
161
+ * +flags+ defaults to zero if unspecified.
133
162
  * +flags+ may be a bitmask of the following flags:
134
163
  *
135
164
  * IO::Splice::F_MOVE, IO::Splice::F_NONBLOCK, IO::Splice::F_MORE
@@ -152,26 +181,17 @@ static VALUE nogvl_splice(void *ptr)
152
181
  * into account userspace buffering done by Ruby or stdio. It is
153
182
  * also not subject to encoding/decoding filters under Ruby 1.9.
154
183
  *
184
+ * Consider using IO.trysplice if you are using non-blocking I/O on
185
+ * both descriptors as it avoids the cost of raising common Errno::EAGAIN
186
+ * exceptions.
187
+ *
155
188
  * See manpage for full documentation:
156
189
  * http://kernel.org/doc/man-pages/online/pages/man2/splice.2.html
157
190
  */
158
- static VALUE my_splice(VALUE self,
159
- VALUE fd_in, VALUE off_in,
160
- VALUE fd_out, VALUE off_out,
161
- VALUE len, VALUE flags)
191
+ static VALUE my_splice(int argc, VALUE *argv, VALUE self)
162
192
  {
163
- off_t i, o;
164
- long n;
165
- struct splice_args a = {
166
- .off_in = NIL_P(off_in) ? NULL : (i = NUM2OFFT(off_in), &i),
167
- .off_out = NIL_P(off_out) ? NULL : (o = NUM2OFFT(off_out), &o),
168
- .fd_in = my_fileno(fd_in),
169
- .fd_out = my_fileno(fd_out),
170
- .len = (size_t)NUM2ULONG(len),
171
- .flags = NUM2UINT(flags),
172
- };
173
-
174
- n = (long)rb_thread_blocking_region(nogvl_splice, &a, RUBY_UBF_IO, 0);
193
+ long n = do_splice(argc, argv, 0);
194
+
175
195
  if (n == 0)
176
196
  rb_eof_error();
177
197
  if (n < 0)
@@ -179,6 +199,33 @@ static VALUE my_splice(VALUE self,
179
199
  return LONG2NUM(n);
180
200
  }
181
201
 
202
+ /*
203
+ * call-seq:
204
+ * IO.trysplice(fd_in, off_in, fd_out, off_out, len) => integer
205
+ * IO.trysplice(fd_in, off_in, fd_out, off_out, len, flags) => integer
206
+ *
207
+ * Exactly like IO.splice, except +:EAGAIN+ is returned when either
208
+ * the read or write end would block instead of raising Errno::EAGAIN.
209
+ *
210
+ * IO::Splice::F_NONBLOCK is always passed for the pipe descriptor,
211
+ * but this can still block if the non-pipe descriptor is blocking.
212
+ *
213
+ * See IO.splice documentation for more details.
214
+ */
215
+ static VALUE trysplice(int argc, VALUE *argv, VALUE self)
216
+ {
217
+ long n = do_splice(argc, argv, SPLICE_F_NONBLOCK);
218
+
219
+ if (n == 0)
220
+ return Qnil;
221
+ if (n < 0) {
222
+ if (errno == EAGAIN)
223
+ return sym_EAGAIN;
224
+ rb_sys_fail("splice");
225
+ }
226
+ return LONG2NUM(n);
227
+ }
228
+
182
229
  struct tee_args {
183
230
  int fd_in;
184
231
  int fd_out;
@@ -194,15 +241,30 @@ static VALUE nogvl_tee(void *ptr)
194
241
  return (VALUE)tee(a->fd_in, a->fd_out, a->len, a->flags);
195
242
  }
196
243
 
244
+ static long do_tee(int argc, VALUE *argv, unsigned dflags)
245
+ {
246
+ VALUE fd_in, fd_out, len, flags;
247
+ struct tee_args a;
248
+
249
+ rb_scan_args(argc, argv, "31", &fd_in, &fd_out, &len, &flags);
250
+ a.fd_in = my_fileno(fd_in);
251
+ a.fd_out = my_fileno(fd_out);
252
+ a.len = (size_t)NUM2ULONG(len);
253
+ a.flags = NIL_P(flags) ? dflags : NUM2UINT(flags) | dflags;
254
+
255
+ return (long)nb_io_run(nogvl_tee, &a, a.flags);
256
+ }
257
+
197
258
  /*
198
259
  * call-seq:
260
+ * IO.tee(fd_in, fd_out, len) => integer
199
261
  * IO.tee(fd_in, fd_out, len, flags) => integer
200
262
  *
201
263
  * Copies up to +len+ bytes of data from +fd_in+ to +fd_out+. +fd_in+
202
264
  * and +fd_out+ must both refer to pipe descriptors. +fd_in+ and +fd_out+
203
265
  * may not be endpoints of the same pipe.
204
266
  *
205
- * +flags+ may be zero or IO::Splice::F_NONBLOCK
267
+ * +flags+ may be zero (the default) or IO::Splice::F_NONBLOCK
206
268
  * Other IO::Splice flags are currently unimplemented or have no effect.
207
269
  *
208
270
  * Returns the number of bytes duplicated if successful.
@@ -210,22 +272,16 @@ static VALUE nogvl_tee(void *ptr)
210
272
  * Raises Errno::EAGAIN when +fd_in+ is empty and/or +fd_out+ is full
211
273
  * and +flags+ contains IO::Splice::F_NONBLOCK
212
274
  *
275
+ * Consider using IO.trytee if you are using IO::Splice::F_NONBLOCK
276
+ * as it avoids the cost of raising common Errno::EAGAIN exceptions.
277
+ *
213
278
  * See manpage for full documentation:
214
279
  * http://kernel.org/doc/man-pages/online/pages/man2/tee.2.html
215
280
  */
216
- static VALUE my_tee(VALUE self,
217
- VALUE fd_in, VALUE fd_out,
218
- VALUE len, VALUE flags)
281
+ static VALUE my_tee(int argc, VALUE *argv, VALUE self)
219
282
  {
220
- long n;
221
- struct tee_args a = {
222
- .fd_in = my_fileno(fd_in),
223
- .fd_out = my_fileno(fd_out),
224
- .len = (size_t)NUM2ULONG(len),
225
- .flags = NUM2UINT(flags),
226
- };
227
-
228
- n = (long)nb_io_run(nogvl_tee, &a, a.flags);
283
+ long n = do_tee(argc, argv, 0);
284
+
229
285
  if (n == 0)
230
286
  rb_eof_error();
231
287
  if (n < 0)
@@ -234,6 +290,34 @@ static VALUE my_tee(VALUE self,
234
290
  return LONG2NUM(n);
235
291
  }
236
292
 
293
+ /*
294
+ * call-seq:
295
+ * IO.trytee(fd_in, fd_out, len) => integer
296
+ * IO.trytee(fd_in, fd_out, len, flags) => integer
297
+ *
298
+ * Exactly like IO.tee, except +:EAGAIN+ is returned when either
299
+ * the read or write end would block instead of raising Errno::EAGAIN.
300
+ *
301
+ * IO::Splice::F_NONBLOCK is always passed for the pipe descriptor,
302
+ * but this can still block if the non-pipe descriptor is blocking.
303
+ *
304
+ * See IO.tee documentation for more details.
305
+ */
306
+ static VALUE trytee(int argc, VALUE *argv, VALUE self)
307
+ {
308
+ long n = do_tee(argc, argv, SPLICE_F_NONBLOCK);
309
+
310
+ if (n == 0)
311
+ return Qnil;
312
+ if (n < 0) {
313
+ if (errno == EAGAIN)
314
+ return sym_EAGAIN;
315
+ rb_sys_fail("tee");
316
+ }
317
+
318
+ return LONG2NUM(n);
319
+ }
320
+
237
321
  struct vmsplice_args {
238
322
  int fd;
239
323
  struct iovec *iov;
@@ -272,7 +356,7 @@ do { \
272
356
  static void advance_vmsplice_args(struct vmsplice_args *a, long n)
273
357
  {
274
358
  struct iovec *new_iov = a->iov;
275
- int i;
359
+ unsigned long i;
276
360
 
277
361
  /* skip over iovecs we've already written completely */
278
362
  for (i = 0; i < a->nr_segs; i++, new_iov++) {
@@ -301,7 +385,9 @@ static void advance_vmsplice_args(struct vmsplice_args *a, long n)
301
385
 
302
386
  /*
303
387
  * call-seq:
388
+ * IO.vmsplice(fd, string_array) => integer
304
389
  * IO.vmsplice(fd, string_array, flags) => integer
390
+ * IO.vmsplice(fd, string) => integer
305
391
  * IO.vmsplice(fd, string, flags) => integer
306
392
  *
307
393
  * Transfers an array of strings into the pipe descriptor given by fd.
@@ -313,11 +399,14 @@ static void advance_vmsplice_args(struct vmsplice_args *a, long n)
313
399
  * See manpage for full documentation:
314
400
  * http://kernel.org/doc/man-pages/online/pages/man2/vmsplice.2.html
315
401
  */
316
- static VALUE my_vmsplice(VALUE self, VALUE fd, VALUE data, VALUE flags)
402
+ static VALUE my_vmsplice(int argc, VALUE * argv, VALUE self)
317
403
  {
318
404
  long rv = 0;
319
405
  ssize_t left;
320
406
  struct vmsplice_args a;
407
+ VALUE fd, data, flags;
408
+
409
+ rb_scan_args(argc, argv, "21", &fd, &data, &flags);
321
410
 
322
411
  switch (TYPE(data)) {
323
412
  case T_STRING: {
@@ -338,7 +427,7 @@ static VALUE my_vmsplice(VALUE self, VALUE fd, VALUE data, VALUE flags)
338
427
  rb_obj_classname(data));
339
428
  }
340
429
  a.fd = my_fileno(fd);
341
- a.flags = NUM2UINT(flags);
430
+ a.flags = NIL_P(flags) ? 0 : NUM2UINT(flags);
342
431
 
343
432
  for (;;) {
344
433
  long n = (long)nb_io_run(nogvl_vmsplice, &a, a.flags);
@@ -442,9 +531,11 @@ void Init_io_splice_ext(void)
442
531
  VALUE mSplice = rb_define_module_under(rb_cIO, "Splice");
443
532
  struct utsname utsname;
444
533
 
445
- rb_define_singleton_method(rb_cIO, "splice", my_splice, 6);
446
- rb_define_singleton_method(rb_cIO, "tee", my_tee, 4);
447
- rb_define_singleton_method(rb_cIO, "vmsplice", my_vmsplice, 3);
534
+ rb_define_singleton_method(rb_cIO, "splice", my_splice, -1);
535
+ rb_define_singleton_method(rb_cIO, "trysplice", trysplice, -1);
536
+ rb_define_singleton_method(rb_cIO, "tee", my_tee, -1);
537
+ rb_define_singleton_method(rb_cIO, "trytee", trytee, -1);
538
+ rb_define_singleton_method(rb_cIO, "vmsplice", my_vmsplice, -1);
448
539
 
449
540
  /*
450
541
  * Attempt to move pages instead of copying. This is only a hint
@@ -510,4 +601,6 @@ void Init_io_splice_ext(void)
510
601
  rb_define_const(mSplice, "F_SETPIPE_SZ",
511
602
  UINT2NUM(F_SETPIPE_SZ));
512
603
  }
604
+
605
+ sym_EAGAIN = ID2SYM(rb_intern("EAGAIN"));
513
606
  }