sendfile 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -43,6 +43,7 @@
43
43
  #include <unistd.h>
44
44
  #include <fcntl.h>
45
45
  #include "config.h"
46
+ static VALUE sym_wait_writable;
46
47
 
47
48
  #ifndef HAVE_RB_THREAD_BLOCKING_REGION
48
49
  /*
@@ -185,7 +186,7 @@ static off_t sendfile_full(struct sendfile_args *args)
185
186
  return all - args->count;
186
187
  }
187
188
 
188
- static off_t sendfile_nonblock(struct sendfile_args *args)
189
+ static VALUE sendfile_nonblock(struct sendfile_args *args, int try)
189
190
  {
190
191
  ssize_t rv;
191
192
  off_t before = args->count;
@@ -201,12 +202,18 @@ static off_t sendfile_nonblock(struct sendfile_args *args)
201
202
 
202
203
  rv = (ssize_t)rb_thread_blocking_region(nogvl_sendfile, args,
203
204
  RUBY_UBF_IO, NULL);
204
- if (rv < 0)
205
+ if (rv < 0) {
206
+ if (try && errno == EAGAIN)
207
+ return sym_wait_writable;
205
208
  rb_sys_fail("sendfile");
206
- if (args->eof)
209
+ }
210
+ if (args->eof) {
211
+ if (try)
212
+ return Qnil;
207
213
  rb_eof_error();
214
+ }
208
215
 
209
- return before - args->count;
216
+ return OFFT2NUM(before - args->count);
210
217
  }
211
218
 
212
219
  static void convert_args(int argc, VALUE *argv, VALUE self,
@@ -287,7 +294,40 @@ static VALUE rb_io_sendfile_nonblock(int argc, VALUE *argv, VALUE self)
287
294
 
288
295
  convert_args(argc, argv, self, &args);
289
296
 
290
- return OFFT2NUM(sendfile_nonblock(&args));
297
+ return sendfile_nonblock(&args, 0);
298
+ }
299
+
300
+ /* call-seq:
301
+ * writeIO.trysendfile(readIO, offset=0, count=nil) => integer, nil, or
302
+ * :wait_writable
303
+ *
304
+ * Transfers count bytes starting at offset from readIO directly to writeIO
305
+ * without copying (i.e. invoking the kernel to do it for you).
306
+ *
307
+ * Unlike IO#sendfile, this will set the O_NONBLOCK flag on writeIO
308
+ * before calling sendfile(2) and will return :wait_writable instead
309
+ * of blocking. This method is intended for use with non-blocking
310
+ * event frameworks, including those that rely on Fibers.
311
+ *
312
+ * If offset is omitted, transfer starts at the beginning of the file.
313
+ *
314
+ * If count is omitted, the full length of the file will be sent.
315
+ *
316
+ * Returns the number of bytes sent on success, nil on EOF, and
317
+ * :wait_writable on EAGAIN. Will throw system error exception on error.
318
+ * (check man sendfile(2) on your platform for
319
+ * information on what errors could result and how to handle them)
320
+ *
321
+ * This method is a faster alternative to sendfile_nonblock as it does
322
+ * not raise exceptions on common EAGAIN errors.
323
+ */
324
+ static VALUE rb_io_trysendfile(int argc, VALUE *argv, VALUE self)
325
+ {
326
+ struct sendfile_args args;
327
+
328
+ convert_args(argc, argv, self, &args);
329
+
330
+ return sendfile_nonblock(&args, 1);
291
331
  }
292
332
 
293
333
  /* Interface to the UNIX sendfile(2) system call. Should work on FreeBSD,
@@ -295,8 +335,10 @@ static VALUE rb_io_sendfile_nonblock(int argc, VALUE *argv, VALUE self)
295
335
  */
296
336
  void Init_sendfile(void)
297
337
  {
338
+ sym_wait_writable = ID2SYM(rb_intern("wait_writable"));
298
339
  rb_define_method(rb_cIO, "sendfile", rb_io_sendfile, -1);
299
340
  rb_define_method(rb_cIO, "sendfile_nonblock",
300
341
  rb_io_sendfile_nonblock, -1);
342
+ rb_define_method(rb_cIO, "trysendfile", rb_io_trysendfile, -1);
301
343
  }
302
344
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  spec = Gem::Specification.new do |gs|
4
4
  gs.name = 'sendfile'
5
- gs.version = '1.0.0'
5
+ gs.version = '1.1.0'
6
6
  gs.summary = 'Ruby interface to sendfile(2) system call'
7
7
  gs.description = <<-EOF
8
8
  Allows Ruby programs to access sendfile(2) functionality on
@@ -185,6 +185,26 @@ class TestSendfile < Test::Unit::TestCase
185
185
  assert_equal nr_read, nr_sent
186
186
  end
187
187
 
188
+ def test_trysendfile
189
+ c, s = UNIXSocket.pair
190
+ nr_sent = 0
191
+ assert_nothing_raised do
192
+ case rv = c.trysendfile(@small)
193
+ when :wait_writable
194
+ break
195
+ when Integer
196
+ nr_sent += rv
197
+ else
198
+ raise "Unexpected return: #{rv.inspect}"
199
+ end while true
200
+ end
201
+ assert nr_sent > 0, "nr_sent: #{nr_sent} <= 0"
202
+ c.close
203
+ nr_read = s.read.size
204
+ s.close
205
+ assert_equal nr_read, nr_sent
206
+ end
207
+
188
208
  def test_tempfile
189
209
  tmp = Tempfile.new ''
190
210
  tmp.write @small_data
@@ -223,5 +243,12 @@ class TestSendfile < Test::Unit::TestCase
223
243
  end
224
244
  s.close
225
245
  end
246
+
247
+ def test_trysendfile_eof
248
+ s = TCPSocket.new @host, @port
249
+ off = @small_data.size
250
+ assert_nil s.trysendfile(@small, off, 1)
251
+ s.close
252
+ end
226
253
  end # class TestSendfile
227
254
 
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sendfile
3
3
  version: !ruby/object:Gem::Version
4
- hash: 23
4
+ hash: 19
5
5
  prerelease: false
6
6
  segments:
7
7
  - 1
8
+ - 1
8
9
  - 0
9
- - 0
10
- version: 1.0.0
10
+ version: 1.1.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Toby DiPasquale
@@ -15,7 +15,7 @@ autorequire: sendfile
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2010-06-26 00:00:00 -04:00
18
+ date: 2011-03-05 00:00:00 -05:00
19
19
  default_executable:
20
20
  dependencies: []
21
21