sleepy_penguin 3.4.1 → 3.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/.document +1 -0
  3. data/.olddoc.yml +3 -4
  4. data/GIT-VERSION-GEN +1 -1
  5. data/LICENSE +3 -3
  6. data/README +7 -4
  7. data/TODO +1 -0
  8. data/ext/sleepy_penguin/cfr.c +62 -0
  9. data/ext/sleepy_penguin/epoll.c +34 -24
  10. data/ext/sleepy_penguin/eventfd.c +6 -5
  11. data/ext/sleepy_penguin/extconf.rb +6 -0
  12. data/ext/sleepy_penguin/init.c +83 -12
  13. data/ext/sleepy_penguin/inotify.c +48 -36
  14. data/ext/sleepy_penguin/kqueue.c +22 -21
  15. data/ext/sleepy_penguin/sendfile.c +120 -0
  16. data/ext/sleepy_penguin/sleepy_penguin.h +15 -28
  17. data/ext/sleepy_penguin/sp_copy.h +33 -0
  18. data/ext/sleepy_penguin/splice.c +174 -0
  19. data/ext/sleepy_penguin/timerfd.c +1 -5
  20. data/ext/sleepy_penguin/util.c +12 -0
  21. data/lib/sleepy_penguin.rb +28 -0
  22. data/lib/sleepy_penguin/cfr.rb +29 -0
  23. data/lib/sleepy_penguin/epoll.rb +13 -10
  24. data/lib/sleepy_penguin/kqueue.rb +6 -6
  25. data/lib/sleepy_penguin/sp.rb +1 -1
  26. data/lib/sleepy_penguin/splice.rb +125 -0
  27. data/pkg.mk +5 -12
  28. data/sleepy_penguin.gemspec +13 -15
  29. data/test/helper.rb +2 -7
  30. data/test/test_cfr.rb +35 -0
  31. data/test/test_constants.rb +2 -4
  32. data/test/test_epoll.rb +35 -6
  33. data/test/test_epoll_gc.rb +2 -5
  34. data/test/test_epoll_io.rb +3 -6
  35. data/test/test_epoll_optimizations.rb +2 -2
  36. data/test/test_eventfd.rb +2 -5
  37. data/test/test_inotify.rb +2 -4
  38. data/test/test_kqueue.rb +35 -7
  39. data/test/test_kqueue_io.rb +2 -5
  40. data/test/test_pipesize.rb +22 -0
  41. data/test/test_sendfile.rb +26 -0
  42. data/test/test_splice.rb +250 -0
  43. data/test/test_splice_eintr.rb +31 -0
  44. data/test/test_timerfd.rb +2 -5
  45. metadata +27 -34
  46. data/lib/sleepy_penguin/epoll/io.rb +0 -28
  47. data/lib/sleepy_penguin/kqueue/io.rb +0 -30
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 65ea0df5a538075f28711e1d2c220f4810eff81a
4
- data.tar.gz: 5f1566a8d5ddaf86c955189d0c5b730c1f95ef31
3
+ metadata.gz: dff861d3873bfcd1a028f7cdc0ae46a1065d4092
4
+ data.tar.gz: 19244c89ef9f4a8719e31eaed7354ce88640ff5c
5
5
  SHA512:
6
- metadata.gz: 58068d4e4f28ad4b5d02049dd3c87b1de8a9a4f20b6884fad942a020c45ebd97c6f96257a8060fa9b66b6a5c003446de832dad5e0e466c18ef52a5705a70ae2d
7
- data.tar.gz: ba28da9192001381d99f4a8b1ae9823d76cebb0e97e7d2836ec1b9bbe4146da4da733cdaafc5cc440b6d4e15d501c3d406b6bb82b829a7ee261b6ebf7795d847
6
+ metadata.gz: 7a82315acdb00dead8609707ed1fe9c5d649c58997170b0fbd0d600a0531bc100f6d57b09fc1fe9ebe4e3f1b1d0e0a967dd0627b26e87e1928f4439aa17b454c
7
+ data.tar.gz: 48ff2a95beb3a28150504ddcf7d5e3f475a92b4379aad96db219dd8556872cd0e4351392f72e91ab5fd608cc597e80374134f4340227d31a4f6a5f5a6840eaae
data/.document CHANGED
@@ -9,3 +9,4 @@ ext/sleepy_penguin/init.c
9
9
  ext/sleepy_penguin/inotify.c
10
10
  ext/sleepy_penguin/timerfd.c
11
11
  ext/sleepy_penguin/kqueue.c
12
+ ext/sleepy_penguin/splice.c
@@ -1,7 +1,6 @@
1
1
  ---
2
- cgit_url: http://bogomips.org/sleepy_penguin.git
2
+ cgit_url: https://bogomips.org/sleepy_penguin.git
3
3
  git_url: git://bogomips.org/sleepy_penguin.git
4
- rdoc_url: http://bogomips.org/sleepy_penguin/
5
- ml_url: http://bogomips.org/sleepy-penguin/
6
- private_email: bofh@bogomips.org
4
+ rdoc_url: https://bogomips.org/sleepy_penguin/
5
+ ml_url: https://bogomips.org/sleepy-penguin/
7
6
  public_email: sleepy-penguin@bogomips.org
@@ -1,7 +1,7 @@
1
1
  #!/bin/sh
2
2
 
3
3
  GVF=GIT-VERSION-FILE
4
- DEF_VER=v3.4.1
4
+ DEF_VER=v3.5.0
5
5
  GVH=ext/sleepy_penguin/git_version.h
6
6
 
7
7
  LF='
data/LICENSE CHANGED
@@ -3,8 +3,8 @@ logs in revision control for names and email addresses of all of them.
3
3
 
4
4
  You can redistribute it and/or modify it under the terms of the GNU
5
5
  Lesser General Public License (LGPL) as published by the Free Software
6
- Foundation, version {2.1}[http://www.gnu.org/licenses/lgpl-2.1.txt] or
7
- (at your option) any later version.
6
+ Foundation, version {2.1}[https://www.gnu.org/licenses/lgpl-2.1.txt]
7
+ or (at your option) any later version.
8
8
 
9
9
  sleepy_penguin is distributed in the hope that it will be useful, but
10
10
  WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -12,4 +12,4 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
12
12
  General Public License for more details.
13
13
 
14
14
  You should have received a copy of the GNU Lesser General Public License
15
- along with this library; if not, see http://www.gnu.org/licenses/
15
+ along with this library; if not, see https://www.gnu.org/licenses/
data/README CHANGED
@@ -3,7 +3,7 @@
3
3
  sleepy_penguin provides access to newer, Linux-only system calls to wait
4
4
  on events from traditionally non-I/O sources. Bindings to the eventfd,
5
5
  timerfd, inotify, and epoll interfaces are provided. Experimental support
6
- for kqueue on FreeBSD (and likely OpenBSD/NetBSD) are also provided.
6
+ for kqueue on FreeBSD (and likely OpenBSD/NetBSD) is also provided.
7
7
 
8
8
  == Features
9
9
 
@@ -31,7 +31,7 @@ If you use RubyGems:
31
31
 
32
32
  Otherwise grab the latest tarball from:
33
33
 
34
- http://bogomips.org/sleepy_penguin/files/
34
+ https://bogomips.org/sleepy_penguin/files/
35
35
 
36
36
  Unpack it, and run "ruby setup.rb"
37
37
 
@@ -45,7 +45,7 @@ You can get the latest source via git from the following locations:
45
45
  You may browse the code from the web and download the latest snapshot
46
46
  tarballs here:
47
47
 
48
- * http://bogomips.org/sleepy_penguin.git (cgit)
48
+ * https://bogomips.org/sleepy_penguin.git
49
49
  * http://repo.or.cz/w/sleepy_penguin.git (gitweb)
50
50
 
51
51
  Inline patches (from "git format-patch") to the mailing list are
@@ -62,4 +62,7 @@ don't email the git mailing list or maintainer with sleepy_penguin patches.
62
62
  All feedback (bug reports, user/development discussion, patches, pull
63
63
  requests) go to the mailing list: mailto:sleepy-penguin@bogomips.org
64
64
 
65
- * Mailing list archives: http://bogomips.org/sleepy-penguin/
65
+ * Mailing list archives: https://bogomips.org/sleepy-penguin/
66
+
67
+ * Also available over NNTP:
68
+ nntp://news.public-inbox.org/inbox.comp.lang.ruby.sleepy-penguin
data/TODO CHANGED
@@ -1 +1,2 @@
1
1
  * FANotify interface
2
+ * memfd interface (awaiting glibc support)
@@ -0,0 +1,62 @@
1
+ #include "sleepy_penguin.h"
2
+ #include "sp_copy.h"
3
+ #include <unistd.h>
4
+
5
+ #ifdef __NR_copy_file_range
6
+ static ssize_t my_cfr(int fd_in, off_t *off_in, int fd_out, off_t *off_out,
7
+ size_t len, unsigned int flags)
8
+ {
9
+ long n = syscall(__NR_copy_file_range,
10
+ fd_in, off_in, fd_out, off_out, len, flags);
11
+
12
+ return (ssize_t)n;
13
+ }
14
+ # define copy_file_range(fd_in,off_in,fd_out,off_out,len,flags) \
15
+ my_cfr((fd_in),(off_in),(fd_out),(off_out),(len),(flags))
16
+ #endif
17
+
18
+ #if defined(HAVE_COPY_FILE_RANGE) || \
19
+ (defined(__linux__) && defined(__NR_copy_file_range))
20
+ static void *nogvl_cfr(void *ptr)
21
+ {
22
+ struct copy_args *a = ptr;
23
+
24
+ return (void *)copy_file_range(a->fd_in, a->off_in,
25
+ a->fd_out, a->off_out, a->len, a->flags);
26
+ }
27
+
28
+ /* :nodoc: */
29
+ static VALUE rb_sp_cfr(VALUE mod, VALUE io_in, VALUE off_in,
30
+ VALUE io_out, VALUE off_out,
31
+ VALUE len, VALUE flags)
32
+ {
33
+ off_t i = 0, o = 0;
34
+ struct copy_args a;
35
+ ssize_t bytes;
36
+
37
+ a.off_in = NIL_P(off_in) ? NULL : (i = NUM2OFFT(off_in), &i);
38
+ a.off_out = NIL_P(off_out) ? NULL : (o = NUM2OFFT(off_out), &o);
39
+ a.len = NUM2SIZET(len);
40
+ a.flags = NUM2UINT(flags);
41
+
42
+ for (;;) {
43
+ a.fd_in = rb_sp_fileno(io_in);
44
+ a.fd_out = rb_sp_fileno(io_out);
45
+ bytes = (ssize_t)IO_RUN(nogvl_cfr, &a);
46
+ if (bytes < 0) {
47
+ switch (errno) {
48
+ case EINTR: continue;
49
+ default: rb_sys_fail("copy_file_range");
50
+ }
51
+ }
52
+ return SSIZET2NUM(bytes);
53
+ }
54
+ }
55
+
56
+ void sleepy_penguin_init_cfr(void)
57
+ {
58
+ VALUE mod = rb_define_module("SleepyPenguin");
59
+
60
+ rb_define_singleton_method(mod, "__cfr", rb_sp_cfr, 6);
61
+ }
62
+ #endif /* !HAVE_COPY_FILE_RANGE */
@@ -49,7 +49,7 @@ static int ep_fd_check(struct ep_per_thread *ept)
49
49
  return 1;
50
50
  }
51
51
 
52
- static struct ep_per_thread *ept_get(VALUE self, int maxevents)
52
+ static struct ep_per_thread *ept_get(int maxevents)
53
53
  {
54
54
  struct ep_per_thread *ept;
55
55
  size_t size;
@@ -66,8 +66,6 @@ static struct ep_per_thread *ept_get(VALUE self, int maxevents)
66
66
  ept = rb_sp_gettlsbuf(&size);
67
67
  ept->capa = maxevents;
68
68
  ept->maxevents = maxevents;
69
- ept->io = self;
70
- ept->fd = rb_sp_fileno(ept->io);
71
69
 
72
70
  return ept;
73
71
  }
@@ -82,15 +80,13 @@ static struct ep_per_thread *ept_get(VALUE self, int maxevents)
82
80
  static VALUE s_new(VALUE klass, VALUE _flags)
83
81
  {
84
82
  int default_flags = RB_SP_CLOEXEC(EPOLL_CLOEXEC);
85
- int flags = rb_sp_get_flags(klass, _flags, default_flags);
83
+ int flags = rb_sp_get_flags(cEpoll, _flags, default_flags);
86
84
  int fd = epoll_create1(flags);
87
85
  VALUE rv;
88
86
 
89
87
  if (fd < 0) {
90
- if (errno == EMFILE || errno == ENFILE || errno == ENOMEM) {
91
- rb_gc();
88
+ if (rb_sp_gc_for_fd(errno))
92
89
  fd = epoll_create1(flags);
93
- }
94
90
  if (fd < 0)
95
91
  rb_sys_fail("epoll_create1");
96
92
  }
@@ -106,7 +102,7 @@ static VALUE s_new(VALUE klass, VALUE _flags)
106
102
  * Register, modify, or register a watch for a given +io+ for events.
107
103
  *
108
104
  * +op+ may be one of +EPOLL_CTL_ADD+, +EPOLL_CTL_MOD+, or +EPOLL_CTL_DEL+
109
- * +io+ is an IO object or one which proxies via the +to_io+ method.
105
+ * +io+ is an +IO+ object or one which proxies via the +to_io+ method.
110
106
  * +events+ is an integer mask of events to watch for.
111
107
  *
112
108
  * Returns nil on success.
@@ -179,6 +175,7 @@ static VALUE real_epwait(struct ep_per_thread *ept)
179
175
  long n;
180
176
  uint64_t expire_at = ept->timeout > 0 ? now_ms() + ept->timeout : 0;
181
177
 
178
+ ept->fd = rb_sp_fileno(ept->io);
182
179
  do {
183
180
  n = (long)rb_sp_fd_region(nogvl_wait, ept, ept->fd);
184
181
  } while (n < 0 && epoll_resume_p(expire_at, ept));
@@ -190,7 +187,7 @@ static VALUE real_epwait(struct ep_per_thread *ept)
190
187
  * call-seq:
191
188
  * ep_io.epoll_wait([maxevents[, timeout]]) { |events, io| ... }
192
189
  *
193
- * Calls epoll_wait(2) and yields Integer +events+ and IO objects watched
190
+ * Calls epoll_wait(2) and yields Integer +events+ and +IO+ objects watched
194
191
  * for. +maxevents+ is the maximum number of events to process at once,
195
192
  * lower numbers may prevent starvation when used by epoll_wait in multiple
196
193
  * threads. Larger +maxevents+ reduces syscall overhead for
@@ -202,14 +199,17 @@ static VALUE epwait(int argc, VALUE *argv, VALUE self)
202
199
  {
203
200
  VALUE timeout, maxevents;
204
201
  struct ep_per_thread *ept;
202
+ int t;
205
203
 
206
204
  rb_need_block();
207
205
  rb_scan_args(argc, argv, "02", &maxevents, &timeout);
206
+ t = NIL_P(timeout) ? -1 : NUM2INT(timeout);
208
207
 
209
- ept = ept_get(self, NIL_P(maxevents) ? 64 : NUM2INT(maxevents));
210
- ept->timeout = NIL_P(timeout) ? -1 : NUM2INT(timeout);
208
+ ept = ept_get(NIL_P(maxevents) ? 64 : NUM2INT(maxevents));
209
+ ept->timeout = t;
210
+ ept->io = self;
211
211
 
212
- return real_epwait(ept);
212
+ return rb_ensure(real_epwait, (VALUE)ept, rb_sp_puttlsbuf, (VALUE)ept);
213
213
  }
214
214
 
215
215
  /* :nodoc: */
@@ -251,7 +251,7 @@ void sleepy_penguin_init_epoll(void)
251
251
  *
252
252
  * The Epoll class provides high-level access to epoll(7)
253
253
  * functionality in the Linux 2.6 and later kernels. It provides
254
- * fork and GC-safety for Ruby objects stored within the IO object
254
+ * fork and GC-safety for Ruby objects stored within the +IO+ object
255
255
  * and may be passed as an argument to IO.select.
256
256
  */
257
257
  cEpoll = rb_define_class_under(mSleepyPenguin, "Epoll", rb_cObject);
@@ -260,7 +260,7 @@ void sleepy_penguin_init_epoll(void)
260
260
  * Document-class: SleepyPenguin::Epoll::IO
261
261
  *
262
262
  * Epoll::IO is a low-level class. It does not provide fork nor
263
- * GC-safety, so Ruby IO objects added via epoll_ctl must be retained
263
+ * GC-safety, so Ruby +IO+ objects added via epoll_ctl must be retained
264
264
  * by the application until IO#close is called.
265
265
  */
266
266
  cEpoll_IO = rb_define_class_under(cEpoll, "IO", rb_cIO);
@@ -271,13 +271,13 @@ void sleepy_penguin_init_epoll(void)
271
271
 
272
272
  rb_define_method(cEpoll, "__event_flags", event_flags, 1);
273
273
 
274
- /* registers an IO object via epoll_ctl */
274
+ /* registers a target +IO+ object via epoll_ctl */
275
275
  rb_define_const(cEpoll, "CTL_ADD", INT2NUM(EPOLL_CTL_ADD));
276
276
 
277
- /* unregisters an IO object via epoll_ctl */
277
+ /* unregisters a target +IO+ object via epoll_ctl */
278
278
  rb_define_const(cEpoll, "CTL_DEL", INT2NUM(EPOLL_CTL_DEL));
279
279
 
280
- /* modifies the registration of an IO object via epoll_ctl */
280
+ /* modifies the registration of a target +IO+ object via epoll_ctl */
281
281
  rb_define_const(cEpoll, "CTL_MOD", INT2NUM(EPOLL_CTL_MOD));
282
282
 
283
283
  /* specifies whether close-on-exec flag is set for Epoll.new */
@@ -306,18 +306,29 @@ void sleepy_penguin_init_epoll(void)
306
306
  rb_define_const(cEpoll, "WAKEUP", UINT2NUM(EPOLLWAKEUP));
307
307
  #endif
308
308
 
309
+ #ifdef EPOLLEXCLUSIVE
310
+ /*
311
+ * Sets an exclusive wakeup mode for the epoll object
312
+ * that is being attached to the target +IO+. This
313
+ * avoids thundering herd scenarios when the same
314
+ * target +IO+ is shared among multiple epoll objects.
315
+ * Available since Linux 4.5
316
+ */
317
+ rb_define_const(cEpoll, "EXCLUSIVE", UINT2NUM(EPOLLEXCLUSIVE));
318
+ #endif
319
+
309
320
  /* watch for urgent read(2) data */
310
321
  rb_define_const(cEpoll, "PRI", UINT2NUM(EPOLLPRI));
311
322
 
312
323
  /*
313
324
  * watch for errors, there is no need to specify this,
314
- * it is always monitored when an IO is watched
325
+ * it is always monitored when an +IO+ is watched
315
326
  */
316
327
  rb_define_const(cEpoll, "ERR", UINT2NUM(EPOLLERR));
317
328
 
318
329
  /*
319
330
  * watch for hangups, there is no need to specify this,
320
- * it is always monitored when an IO is watched
331
+ * it is always monitored when an +IO+ is watched
321
332
  */
322
333
  rb_define_const(cEpoll, "HUP", UINT2NUM(EPOLLHUP));
323
334
 
@@ -329,10 +340,9 @@ void sleepy_penguin_init_epoll(void)
329
340
 
330
341
  id_for_fd = rb_intern("for_fd");
331
342
 
332
- if (RB_SP_GREEN_THREAD)
333
- rb_require("sleepy_penguin/epoll/io");
334
-
335
- /* the high-level interface is implemented in Ruby: */
336
- rb_require("sleepy_penguin/epoll");
343
+ /*
344
+ * the high-level interface is implemented in Ruby,
345
+ * see lib/sleepy_penguin/epoll.rb
346
+ */
337
347
  }
338
348
  #endif /* HAVE_SYS_EPOLL_H */
@@ -31,10 +31,8 @@ static VALUE s_new(int argc, VALUE *argv, VALUE klass)
31
31
 
32
32
  fd = eventfd(initval, flags);
33
33
  if (fd < 0) {
34
- if (errno == EMFILE || errno == ENFILE || errno == ENOMEM) {
35
- rb_gc();
34
+ if (rb_sp_gc_for_fd(errno))
36
35
  fd = eventfd(initval, flags);
37
- }
38
36
  if (fd < 0)
39
37
  rb_sys_fail("eventfd");
40
38
  }
@@ -84,7 +82,9 @@ static VALUE incr(int argc, VALUE *argv, VALUE self)
84
82
 
85
83
  rb_scan_args(argc, argv, "11", &value, &nonblock);
86
84
  x.fd = rb_sp_fileno(self);
87
- RTEST(nonblock) ? rb_sp_set_nonblock(x.fd) : blocking_io_prepare(x.fd);
85
+ if (RTEST(nonblock))
86
+ rb_sp_set_nonblock(x.fd);
87
+
88
88
  x.val = (uint64_t)NUM2ULL(value);
89
89
  retry:
90
90
  w = (ssize_t)rb_sp_fd_region(efd_write, &x, x.fd);
@@ -121,7 +121,8 @@ static VALUE getvalue(int argc, VALUE *argv, VALUE self)
121
121
 
122
122
  rb_scan_args(argc, argv, "01", &nonblock);
123
123
  x.fd = rb_sp_fileno(self);
124
- RTEST(nonblock) ? rb_sp_set_nonblock(x.fd) : blocking_io_prepare(x.fd);
124
+ if (RTEST(nonblock))
125
+ rb_sp_set_nonblock(x.fd);
125
126
  retry:
126
127
  w = (ssize_t)rb_sp_fd_region(efd_read, &x, x.fd);
127
128
  if (w < 0) {
@@ -6,6 +6,7 @@ if have_header('sys/event.h')
6
6
  end
7
7
  have_header('sys/mount.h')
8
8
  have_header('sys/eventfd.h')
9
+ have_header('sys/sendfile.h')
9
10
 
10
11
  # it's impossible to use signalfd reliably with Ruby since Ruby currently
11
12
  # manages # (and overrides) all signal handling
@@ -19,8 +20,13 @@ unless have_macro('CLOCK_MONOTONIC', 'time.h')
19
20
  end
20
21
  have_type('clockid_t', 'time.h')
21
22
  have_func('clock_gettime', 'time.h')
23
+ have_func('copy_file_range')
22
24
  have_func('epoll_create1', %w(sys/epoll.h))
23
25
  have_func('inotify_init1', %w(sys/inotify.h))
26
+ have_func('splice', %w(fcntl.h))
27
+ have_func('tee', %w(fcntl.h))
28
+ have_macro('F_GETPIPE_SZ', %w(fcntl.h))
29
+ have_macro('F_SETPIPE_SZ', %w(fcntl.h))
24
30
  have_func('rb_thread_call_without_gvl')
25
31
  have_func('rb_thread_blocking_region')
26
32
  have_func('rb_thread_io_blocking_region')
@@ -11,8 +11,15 @@
11
11
  #define L1_CACHE_LINE_MAX 128 /* largest I've seen (Pentium 4) */
12
12
  size_t rb_sp_l1_cache_line_size;
13
13
  static pthread_key_t rb_sp_key;
14
+ enum rb_sp_tls_buf_type {
15
+ RB_SP_TLS_INUSE = -1,
16
+ RB_SP_TLS_READY = 0,
17
+ RB_SP_TLS_MALLOCED = 1
18
+ };
19
+
14
20
  struct rb_sp_tlsbuf {
15
- size_t capa;
21
+ uint32_t capa;
22
+ enum rb_sp_tls_buf_type buf_type;
16
23
  unsigned char ptr[FLEX_ARRAY];
17
24
  };
18
25
 
@@ -52,6 +59,22 @@ void sleepy_penguin_init_signalfd(void);
52
59
  # define sleepy_penguin_init_signalfd() for(;0;)
53
60
  #endif
54
61
 
62
+ #ifdef HAVE_SPLICE
63
+ void sleepy_penguin_init_splice(void);
64
+ #else
65
+ # define sleepy_penguin_init_splice() for(;0;)
66
+ #endif
67
+
68
+ #if defined(HAVE_COPY_FILE_RANGE) || \
69
+ (defined(__linux__) && defined(__NR_copy_file_range))
70
+ void sleepy_penguin_init_cfr(void);
71
+ #else
72
+ # define sleepy_penguin_init_cfr() for (;0;)
73
+ #endif
74
+
75
+ /* everyone */
76
+ void sleepy_penguin_init_sendfile(void);
77
+
55
78
  static size_t l1_cache_line_size_detect(void)
56
79
  {
57
80
  #ifdef _SC_LEVEL1_DCACHE_LINESIZE
@@ -73,12 +96,36 @@ static void sp_once(void)
73
96
  }
74
97
  }
75
98
 
99
+ static struct rb_sp_tlsbuf *alloc_tlsbuf(size_t size)
100
+ {
101
+ size_t bytes = size + sizeof(struct rb_sp_tlsbuf);
102
+ struct rb_sp_tlsbuf *buf;
103
+ void *ptr;
104
+ int err = posix_memalign(&ptr, rb_sp_l1_cache_line_size, bytes);
105
+
106
+ if (err) {
107
+ errno = err;
108
+ rb_memerror(); /* fatal */
109
+ }
110
+
111
+ buf = ptr;
112
+ buf->capa = size;
113
+
114
+ return buf;
115
+ }
116
+
76
117
  void *rb_sp_gettlsbuf(size_t *size)
77
118
  {
78
119
  struct rb_sp_tlsbuf *buf = pthread_getspecific(rb_sp_key);
79
- void *ptr;
80
120
  int err;
81
- size_t bytes;
121
+
122
+ assert(buf ? buf->buf_type != RB_SP_TLS_MALLOCED : 1);
123
+
124
+ if (buf && buf->buf_type != RB_SP_TLS_READY) {
125
+ buf = alloc_tlsbuf(*size);
126
+ buf->buf_type = RB_SP_TLS_MALLOCED;
127
+ return buf->ptr;
128
+ }
82
129
 
83
130
  if (buf && buf->capa >= *size) {
84
131
  *size = buf->capa;
@@ -86,24 +133,45 @@ void *rb_sp_gettlsbuf(size_t *size)
86
133
  }
87
134
 
88
135
  free(buf);
89
- bytes = *size + sizeof(struct rb_sp_tlsbuf);
90
- err = posix_memalign(&ptr, rb_sp_l1_cache_line_size, bytes);
91
- if (err) {
92
- errno = err;
93
- rb_memerror(); /* fatal */
94
- }
95
-
96
- buf = ptr;
97
- buf->capa = *size;
136
+ buf = alloc_tlsbuf(*size);
98
137
  err = pthread_setspecific(rb_sp_key, buf);
99
138
  if (err != 0) {
139
+ free(buf);
100
140
  errno = err;
101
141
  rb_sys_fail("BUG: pthread_setspecific");
102
142
  }
103
143
  out:
144
+ buf->buf_type = RB_SP_TLS_INUSE;
104
145
  return buf->ptr;
105
146
  }
106
147
 
148
+ #define container_of(ptr, type, member) \
149
+ (type *)((uintptr_t)(ptr) - offsetof(type, member))
150
+
151
+ VALUE rb_sp_puttlsbuf(VALUE p)
152
+ {
153
+ struct rb_sp_tlsbuf *tls = pthread_getspecific(rb_sp_key);
154
+ void *ptr = (void *)p;
155
+ struct rb_sp_tlsbuf *buf;
156
+
157
+ if (!ptr)
158
+ return Qfalse;
159
+
160
+ buf = container_of(ptr, struct rb_sp_tlsbuf, ptr);
161
+
162
+ switch (buf->buf_type) {
163
+ case RB_SP_TLS_INUSE:
164
+ assert(tls == buf && "rb_sp_puttlsbuf mismatch");
165
+ buf->buf_type = RB_SP_TLS_READY;
166
+ break;
167
+ case RB_SP_TLS_READY:
168
+ assert(0 && "rb_sp_gettlsbuf not called");
169
+ case RB_SP_TLS_MALLOCED:
170
+ free(buf);
171
+ }
172
+ return Qfalse;
173
+ }
174
+
107
175
  void Init_sleepy_penguin_ext(void)
108
176
  {
109
177
  VALUE mSleepyPenguin;
@@ -127,4 +195,7 @@ void Init_sleepy_penguin_ext(void)
127
195
  sleepy_penguin_init_eventfd();
128
196
  sleepy_penguin_init_inotify();
129
197
  sleepy_penguin_init_signalfd();
198
+ sleepy_penguin_init_splice();
199
+ sleepy_penguin_init_cfr();
200
+ sleepy_penguin_init_sendfile();
130
201
  }