sleepy_penguin 3.4.1 → 3.5.0

Sign up to get free protection for your applications and to get access to all the features.
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
  }