polyphony 0.84 → 0.86

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 (241) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +14 -0
  3. data/Gemfile.lock +1 -1
  4. data/Rakefile +1 -1
  5. data/examples/core/multi_suspend.rb +39 -0
  6. data/examples/core/shutdown_all_children.rb +41 -0
  7. data/examples/io/gzip.rb +8 -0
  8. data/examples/io/splice_echo_server.rb +15 -0
  9. data/ext/polyphony/backend_io_uring.c +57 -31
  10. data/ext/polyphony/io_extensions.c +137 -26
  11. data/lib/polyphony/extensions/fiber.rb +3 -1
  12. data/lib/polyphony/extensions/io.rb +4 -0
  13. data/lib/polyphony/extensions/pipe.rb +4 -0
  14. data/lib/polyphony/extensions/socket.rb +4 -0
  15. data/lib/polyphony/version.rb +1 -1
  16. data/polyphony.gemspec +1 -1
  17. data/test/test_backend.rb +1 -1
  18. data/test/test_fiber.rb +5 -2
  19. data/test/test_signal.rb +3 -3
  20. data/vendor/liburing/.github/pull_request_template.md +86 -0
  21. data/vendor/liburing/.github/workflows/build.yml +85 -0
  22. data/vendor/liburing/.github/workflows/shellcheck.yml +20 -0
  23. data/vendor/liburing/.gitignore +149 -0
  24. data/vendor/liburing/COPYING +502 -0
  25. data/vendor/liburing/COPYING.GPL +339 -0
  26. data/vendor/liburing/LICENSE +7 -0
  27. data/vendor/liburing/Makefile +82 -0
  28. data/vendor/liburing/Makefile.common +5 -0
  29. data/vendor/liburing/Makefile.quiet +11 -0
  30. data/vendor/liburing/README +46 -0
  31. data/vendor/liburing/configure +486 -0
  32. data/vendor/liburing/debian/README.Debian +7 -0
  33. data/vendor/liburing/debian/changelog +27 -0
  34. data/vendor/liburing/debian/compat +1 -0
  35. data/vendor/liburing/debian/control +48 -0
  36. data/vendor/liburing/debian/copyright +49 -0
  37. data/vendor/liburing/debian/liburing-dev.install +4 -0
  38. data/vendor/liburing/debian/liburing-dev.manpages +6 -0
  39. data/vendor/liburing/debian/liburing1-udeb.install +1 -0
  40. data/vendor/liburing/debian/liburing1.install +1 -0
  41. data/vendor/liburing/debian/liburing1.symbols +32 -0
  42. data/vendor/liburing/debian/patches/series +1 -0
  43. data/vendor/liburing/debian/rules +81 -0
  44. data/vendor/liburing/debian/source/format +1 -0
  45. data/vendor/liburing/debian/source/local-options +2 -0
  46. data/vendor/liburing/debian/source/options +1 -0
  47. data/vendor/liburing/debian/watch +3 -0
  48. data/vendor/liburing/examples/Makefile +38 -0
  49. data/vendor/liburing/examples/io_uring-cp.c +282 -0
  50. data/vendor/liburing/examples/io_uring-test.c +112 -0
  51. data/vendor/liburing/examples/link-cp.c +193 -0
  52. data/vendor/liburing/examples/ucontext-cp.c +273 -0
  53. data/vendor/liburing/liburing.pc.in +12 -0
  54. data/vendor/liburing/liburing.spec +66 -0
  55. data/vendor/liburing/make-debs.sh +53 -0
  56. data/vendor/liburing/man/io_uring.7 +754 -0
  57. data/vendor/liburing/man/io_uring_cq_advance.3 +35 -0
  58. data/vendor/liburing/man/io_uring_cq_ready.3 +25 -0
  59. data/vendor/liburing/man/io_uring_cqe_get_data.3 +34 -0
  60. data/vendor/liburing/man/io_uring_cqe_seen.3 +32 -0
  61. data/vendor/liburing/man/io_uring_enter.2 +1483 -0
  62. data/vendor/liburing/man/io_uring_free_probe.3 +24 -0
  63. data/vendor/liburing/man/io_uring_get_probe.3 +29 -0
  64. data/vendor/liburing/man/io_uring_get_sqe.3 +38 -0
  65. data/vendor/liburing/man/io_uring_opcode_supported.3 +29 -0
  66. data/vendor/liburing/man/io_uring_prep_msg_ring.3 +58 -0
  67. data/vendor/liburing/man/io_uring_prep_read.3 +50 -0
  68. data/vendor/liburing/man/io_uring_prep_read_fixed.3 +54 -0
  69. data/vendor/liburing/man/io_uring_prep_readv.3 +51 -0
  70. data/vendor/liburing/man/io_uring_prep_readv2.3 +79 -0
  71. data/vendor/liburing/man/io_uring_prep_write.3 +50 -0
  72. data/vendor/liburing/man/io_uring_prep_write_fixed.3 +54 -0
  73. data/vendor/liburing/man/io_uring_prep_writev.3 +51 -0
  74. data/vendor/liburing/man/io_uring_prep_writev2.3 +78 -0
  75. data/vendor/liburing/man/io_uring_queue_exit.3 +27 -0
  76. data/vendor/liburing/man/io_uring_queue_init.3 +44 -0
  77. data/vendor/liburing/man/io_uring_register.2 +688 -0
  78. data/vendor/liburing/man/io_uring_register_buffers.3 +41 -0
  79. data/vendor/liburing/man/io_uring_register_files.3 +35 -0
  80. data/vendor/liburing/man/io_uring_setup.2 +534 -0
  81. data/vendor/liburing/man/io_uring_sq_ready.3 +25 -0
  82. data/vendor/liburing/man/io_uring_sq_space_left.3 +25 -0
  83. data/vendor/liburing/man/io_uring_sqe_set_data.3 +30 -0
  84. data/vendor/liburing/man/io_uring_sqe_set_flags.3 +60 -0
  85. data/vendor/liburing/man/io_uring_sqring_wait.3 +30 -0
  86. data/vendor/liburing/man/io_uring_submit.3 +29 -0
  87. data/vendor/liburing/man/io_uring_submit_and_wait.3 +34 -0
  88. data/vendor/liburing/man/io_uring_submit_and_wait_timeout.3 +49 -0
  89. data/vendor/liburing/man/io_uring_unregister_buffers.3 +26 -0
  90. data/vendor/liburing/man/io_uring_unregister_files.3 +26 -0
  91. data/vendor/liburing/man/io_uring_wait_cqe.3 +33 -0
  92. data/vendor/liburing/man/io_uring_wait_cqe_nr.3 +36 -0
  93. data/vendor/liburing/man/io_uring_wait_cqe_timeout.3 +39 -0
  94. data/vendor/liburing/man/io_uring_wait_cqes.3 +46 -0
  95. data/vendor/liburing/src/Makefile +89 -0
  96. data/vendor/liburing/src/arch/aarch64/syscall.h +95 -0
  97. data/vendor/liburing/src/arch/generic/lib.h +21 -0
  98. data/vendor/liburing/src/arch/generic/syscall.h +87 -0
  99. data/vendor/liburing/src/arch/syscall-defs.h +67 -0
  100. data/vendor/liburing/src/arch/x86/lib.h +32 -0
  101. data/vendor/liburing/src/arch/x86/syscall.h +160 -0
  102. data/vendor/liburing/src/include/liburing/barrier.h +81 -0
  103. data/vendor/liburing/src/include/liburing/io_uring.h +442 -0
  104. data/vendor/liburing/src/include/liburing.h +921 -0
  105. data/vendor/liburing/src/int_flags.h +8 -0
  106. data/vendor/liburing/src/lib.h +57 -0
  107. data/vendor/liburing/src/liburing.map +53 -0
  108. data/vendor/liburing/src/nolibc.c +48 -0
  109. data/vendor/liburing/src/queue.c +403 -0
  110. data/vendor/liburing/src/register.c +293 -0
  111. data/vendor/liburing/src/setup.c +332 -0
  112. data/vendor/liburing/src/syscall.c +47 -0
  113. data/vendor/liburing/src/syscall.h +103 -0
  114. data/vendor/liburing/test/232c93d07b74-test.c +306 -0
  115. data/vendor/liburing/test/35fa71a030ca-test.c +329 -0
  116. data/vendor/liburing/test/500f9fbadef8-test.c +89 -0
  117. data/vendor/liburing/test/7ad0e4b2f83c-test.c +93 -0
  118. data/vendor/liburing/test/8a9973408177-test.c +106 -0
  119. data/vendor/liburing/test/917257daa0fe-test.c +53 -0
  120. data/vendor/liburing/test/Makefile +244 -0
  121. data/vendor/liburing/test/a0908ae19763-test.c +58 -0
  122. data/vendor/liburing/test/a4c0b3decb33-test.c +180 -0
  123. data/vendor/liburing/test/accept-link.c +254 -0
  124. data/vendor/liburing/test/accept-reuse.c +164 -0
  125. data/vendor/liburing/test/accept-test.c +79 -0
  126. data/vendor/liburing/test/accept.c +477 -0
  127. data/vendor/liburing/test/across-fork.c +283 -0
  128. data/vendor/liburing/test/b19062a56726-test.c +53 -0
  129. data/vendor/liburing/test/b5837bd5311d-test.c +77 -0
  130. data/vendor/liburing/test/ce593a6c480a-test.c +136 -0
  131. data/vendor/liburing/test/close-opath.c +122 -0
  132. data/vendor/liburing/test/config +10 -0
  133. data/vendor/liburing/test/connect.c +398 -0
  134. data/vendor/liburing/test/cq-full.c +96 -0
  135. data/vendor/liburing/test/cq-overflow.c +294 -0
  136. data/vendor/liburing/test/cq-peek-batch.c +102 -0
  137. data/vendor/liburing/test/cq-ready.c +94 -0
  138. data/vendor/liburing/test/cq-size.c +64 -0
  139. data/vendor/liburing/test/d4ae271dfaae-test.c +96 -0
  140. data/vendor/liburing/test/d77a67ed5f27-test.c +65 -0
  141. data/vendor/liburing/test/defer.c +307 -0
  142. data/vendor/liburing/test/double-poll-crash.c +185 -0
  143. data/vendor/liburing/test/drop-submit.c +92 -0
  144. data/vendor/liburing/test/eeed8b54e0df-test.c +114 -0
  145. data/vendor/liburing/test/empty-eownerdead.c +45 -0
  146. data/vendor/liburing/test/eventfd-disable.c +151 -0
  147. data/vendor/liburing/test/eventfd-reg.c +76 -0
  148. data/vendor/liburing/test/eventfd-ring.c +97 -0
  149. data/vendor/liburing/test/eventfd.c +112 -0
  150. data/vendor/liburing/test/exec-target.c +6 -0
  151. data/vendor/liburing/test/exit-no-cleanup.c +117 -0
  152. data/vendor/liburing/test/fadvise.c +202 -0
  153. data/vendor/liburing/test/fallocate.c +249 -0
  154. data/vendor/liburing/test/fc2a85cb02ef-test.c +131 -0
  155. data/vendor/liburing/test/file-register.c +858 -0
  156. data/vendor/liburing/test/file-update.c +173 -0
  157. data/vendor/liburing/test/file-verify.c +629 -0
  158. data/vendor/liburing/test/files-exit-hang-poll.c +128 -0
  159. data/vendor/liburing/test/files-exit-hang-timeout.c +134 -0
  160. data/vendor/liburing/test/fixed-link.c +90 -0
  161. data/vendor/liburing/test/fpos.c +252 -0
  162. data/vendor/liburing/test/fsync.c +224 -0
  163. data/vendor/liburing/test/hardlink.c +136 -0
  164. data/vendor/liburing/test/helpers.c +135 -0
  165. data/vendor/liburing/test/helpers.h +67 -0
  166. data/vendor/liburing/test/io-cancel.c +550 -0
  167. data/vendor/liburing/test/io_uring_enter.c +296 -0
  168. data/vendor/liburing/test/io_uring_register.c +676 -0
  169. data/vendor/liburing/test/io_uring_setup.c +192 -0
  170. data/vendor/liburing/test/iopoll.c +372 -0
  171. data/vendor/liburing/test/lfs-openat-write.c +119 -0
  172. data/vendor/liburing/test/lfs-openat.c +275 -0
  173. data/vendor/liburing/test/link-timeout.c +1107 -0
  174. data/vendor/liburing/test/link.c +496 -0
  175. data/vendor/liburing/test/link_drain.c +229 -0
  176. data/vendor/liburing/test/madvise.c +195 -0
  177. data/vendor/liburing/test/mkdir.c +108 -0
  178. data/vendor/liburing/test/msg-ring.c +234 -0
  179. data/vendor/liburing/test/multicqes_drain.c +387 -0
  180. data/vendor/liburing/test/nop-all-sizes.c +99 -0
  181. data/vendor/liburing/test/nop.c +115 -0
  182. data/vendor/liburing/test/open-close.c +261 -0
  183. data/vendor/liburing/test/openat2.c +308 -0
  184. data/vendor/liburing/test/personality.c +204 -0
  185. data/vendor/liburing/test/pipe-eof.c +83 -0
  186. data/vendor/liburing/test/pipe-reuse.c +105 -0
  187. data/vendor/liburing/test/poll-cancel-ton.c +135 -0
  188. data/vendor/liburing/test/poll-cancel.c +228 -0
  189. data/vendor/liburing/test/poll-link.c +230 -0
  190. data/vendor/liburing/test/poll-many.c +208 -0
  191. data/vendor/liburing/test/poll-mshot-update.c +273 -0
  192. data/vendor/liburing/test/poll-ring.c +48 -0
  193. data/vendor/liburing/test/poll-v-poll.c +353 -0
  194. data/vendor/liburing/test/poll.c +109 -0
  195. data/vendor/liburing/test/pollfree.c +426 -0
  196. data/vendor/liburing/test/probe.c +135 -0
  197. data/vendor/liburing/test/read-write.c +876 -0
  198. data/vendor/liburing/test/register-restrictions.c +633 -0
  199. data/vendor/liburing/test/rename.c +135 -0
  200. data/vendor/liburing/test/ring-leak.c +173 -0
  201. data/vendor/liburing/test/ring-leak2.c +249 -0
  202. data/vendor/liburing/test/rsrc_tags.c +449 -0
  203. data/vendor/liburing/test/runtests-loop.sh +16 -0
  204. data/vendor/liburing/test/runtests.sh +170 -0
  205. data/vendor/liburing/test/rw_merge_test.c +97 -0
  206. data/vendor/liburing/test/self.c +91 -0
  207. data/vendor/liburing/test/send_recv.c +286 -0
  208. data/vendor/liburing/test/send_recvmsg.c +345 -0
  209. data/vendor/liburing/test/sendmsg_fs_cve.c +200 -0
  210. data/vendor/liburing/test/shared-wq.c +84 -0
  211. data/vendor/liburing/test/short-read.c +75 -0
  212. data/vendor/liburing/test/shutdown.c +165 -0
  213. data/vendor/liburing/test/sigfd-deadlock.c +74 -0
  214. data/vendor/liburing/test/skip-cqe.c +429 -0
  215. data/vendor/liburing/test/socket-rw-eagain.c +158 -0
  216. data/vendor/liburing/test/socket-rw-offset.c +157 -0
  217. data/vendor/liburing/test/socket-rw.c +145 -0
  218. data/vendor/liburing/test/splice.c +512 -0
  219. data/vendor/liburing/test/sq-full-cpp.cc +45 -0
  220. data/vendor/liburing/test/sq-full.c +45 -0
  221. data/vendor/liburing/test/sq-poll-dup.c +204 -0
  222. data/vendor/liburing/test/sq-poll-kthread.c +169 -0
  223. data/vendor/liburing/test/sq-poll-share.c +137 -0
  224. data/vendor/liburing/test/sq-space_left.c +159 -0
  225. data/vendor/liburing/test/sqpoll-cancel-hang.c +157 -0
  226. data/vendor/liburing/test/sqpoll-disable-exit.c +196 -0
  227. data/vendor/liburing/test/sqpoll-exit-hang.c +78 -0
  228. data/vendor/liburing/test/sqpoll-sleep.c +69 -0
  229. data/vendor/liburing/test/statx.c +172 -0
  230. data/vendor/liburing/test/stdout.c +232 -0
  231. data/vendor/liburing/test/submit-link-fail.c +154 -0
  232. data/vendor/liburing/test/submit-reuse.c +239 -0
  233. data/vendor/liburing/test/symlink.c +116 -0
  234. data/vendor/liburing/test/teardowns.c +58 -0
  235. data/vendor/liburing/test/thread-exit.c +143 -0
  236. data/vendor/liburing/test/timeout-new.c +252 -0
  237. data/vendor/liburing/test/timeout-overflow.c +204 -0
  238. data/vendor/liburing/test/timeout.c +1523 -0
  239. data/vendor/liburing/test/unlink.c +112 -0
  240. data/vendor/liburing/test/wakeup-hang.c +162 -0
  241. metadata +227 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ce9742f5b50581c5569df4ad3e2232c7c32392a92e7ab23960373c8a4e3b0de4
4
- data.tar.gz: a695f3191cc7cefb7d720565c3564b24784beb836528342e8d4fd9e8d0231dda
3
+ metadata.gz: b87dae4e2c17055e8206c820a4281774cdf3127195173948c97df4eb09a2bd13
4
+ data.tar.gz: 2e7bd958ec7435569423398069223eda41e9c2d89f580627bc22e9054276dd1c
5
5
  SHA512:
6
- metadata.gz: 041cb949e600ef328ed6d1c92badcaa9ef502e4776043d6a7786a811d5935de450f933b8eff4dd6100db6e55ff19878186bd9ef6bb840ac1cdcdb8c53341f51c
7
- data.tar.gz: fc0542606c3ee5ab044cc5e8175a60fb3fa6a8466422643ff1446fb0271a6db09236b5c0dbc7a632fa47853ae0ac3a2007fa4644a85283b4225f453f6155dbce
6
+ metadata.gz: 48f6489939da965463735c83acb4372263eb44b48b5a16da1e704a15e3af971a5d43abd7da5e44f2455b40d880413c6fa8159794e663176c83a11eddebdebc7d
7
+ data.tar.gz: 3ada5f9041041e91b6b2a7f31ec609352752317e87c51df090517c9e2b556d9ef69ef8842babd15f25b2257c4226431520ff15df505748cb38bb4a1d5a19063c
data/CHANGELOG.md CHANGED
@@ -1,3 +1,17 @@
1
+ ## 0.86 2022-03-14
2
+
3
+ - Fix gemspec
4
+
5
+ ## 0.85 2022-03-13
6
+
7
+ - Reduce write ops in `IO.gzip` (#77)
8
+ - Add support for read/write method detection for compression/decompression
9
+ methods (#77)
10
+ - Improve `Fiber#shutdown_all_children`
11
+ - Improve io_uring `wait_event` implementation (share single I/O poll across
12
+ multiple fibers)
13
+ - Fix io_uring `write` file offset
14
+
1
15
  ## 0.84 2022-03-11
2
16
 
3
17
  - Implement `IO.tee` (#82)
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- polyphony (0.84)
4
+ polyphony (0.86)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
data/Rakefile CHANGED
@@ -33,7 +33,7 @@ task :release do
33
33
  `gem build polyphony.gemspec`
34
34
 
35
35
  puts "Pushing polyphony #{version}..."
36
- `gem push extralite-#{version}.gem`
36
+ `gem push polyphony-#{version}.gem`
37
37
 
38
38
  puts "Cleaning up..."
39
39
  `rm *.gem`
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/setup'
4
+ require 'polyphony'
5
+
6
+ module ::Kernel
7
+ def trace(*args)
8
+ STDOUT.orig_write(format_trace(args))
9
+ end
10
+
11
+ def format_trace(args)
12
+ if args.first.is_a?(String)
13
+ if args.size > 1
14
+ format("%s: %p\n", args.shift, args)
15
+ else
16
+ format("%s\n", args.first)
17
+ end
18
+ else
19
+ format("%p\n", args.size == 1 ? args.first : args)
20
+ end
21
+ end
22
+
23
+ def monotonic_clock
24
+ ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
25
+ end
26
+ end
27
+
28
+ count = 10000
29
+
30
+ count.times do
31
+ spin { Polyphony.backend_wait_event(true) }
32
+ end
33
+
34
+ trace 'sleeping...'
35
+ sleep 1
36
+
37
+ trace 'shutting down children...'
38
+ Fiber.current.shutdown_all_children
39
+ trace 'done'
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/setup'
4
+ require 'polyphony'
5
+
6
+ module ::Kernel
7
+ def trace(*args)
8
+ STDOUT.orig_write(format_trace(args))
9
+ end
10
+
11
+ def format_trace(args)
12
+ if args.first.is_a?(String)
13
+ if args.size > 1
14
+ format("%s: %p\n", args.shift, args)
15
+ else
16
+ format("%s\n", args.first)
17
+ end
18
+ else
19
+ format("%p\n", args.size == 1 ? args.first : args)
20
+ end
21
+ end
22
+
23
+ def monotonic_clock
24
+ ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
25
+ end
26
+ end
27
+
28
+ count = 10
29
+
30
+ f = spin do
31
+ count.times { spin { suspend } }
32
+ suspend
33
+ end
34
+
35
+ snooze
36
+ trace children_alive: f.children.size
37
+
38
+ trace 'shutting down children...'
39
+ f.shutdown_all_children
40
+
41
+ trace children_alive: f.children.size
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/setup'
4
+ require 'polyphony'
5
+
6
+ require 'polyphony'
7
+
8
+ IO.gzip(STDIN, STDOUT)
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/setup'
4
+ require 'polyphony'
5
+
6
+ require 'polyphony'
7
+
8
+ def handle_echo_client(conn)
9
+ buffer = Polyphony.pipe
10
+ spin { buffer.splice_to_eof_from(conn) }
11
+ spin { conn.splice_to_eof_from(buffer) }
12
+ end
13
+
14
+ puts "Serving echo on port 1234..."
15
+ TCPServer.new('127.0.0.1', 1234).accept_loop { |c| handle_echo_client(c) }
@@ -41,8 +41,10 @@ typedef struct Backend_t {
41
41
  op_context_store_t store;
42
42
  unsigned int pending_sqes;
43
43
  unsigned int prepared_limit;
44
- int event_fd;
45
44
  int ring_initialized;
45
+
46
+ int event_fd;
47
+ op_context_t *event_fd_ctx;
46
48
  } Backend_t;
47
49
 
48
50
  static void Backend_mark(void *ptr) {
@@ -83,6 +85,7 @@ static VALUE Backend_initialize(VALUE self) {
83
85
  backend->pending_sqes = 0;
84
86
  backend->ring_initialized = 0;
85
87
  backend->event_fd = -1;
88
+ backend->event_fd_ctx = NULL;
86
89
 
87
90
  context_store_initialize(&backend->store);
88
91
 
@@ -183,13 +186,21 @@ done:
183
186
  return;
184
187
  }
185
188
 
189
+ inline void io_uring_backend_immediate_submit(Backend_t *backend) {
190
+ backend->pending_sqes = 0;
191
+ io_uring_submit(&backend->ring);
192
+ }
193
+
194
+ inline void io_uring_backend_defer_submit(Backend_t *backend) {
195
+ backend->pending_sqes += 1;
196
+ if (backend->pending_sqes >= backend->prepared_limit)
197
+ io_uring_backend_immediate_submit(backend);
198
+ }
199
+
186
200
  void io_uring_backend_poll(Backend_t *backend) {
187
201
  poll_context_t poll_ctx;
188
202
  poll_ctx.ring = &backend->ring;
189
- if (backend->pending_sqes) {
190
- backend->pending_sqes = 0;
191
- io_uring_submit(&backend->ring);
192
- }
203
+ if (backend->pending_sqes) io_uring_backend_immediate_submit(backend);
193
204
 
194
205
  wait_cqe:
195
206
  backend->base.currently_polling = 1;
@@ -211,10 +222,7 @@ inline VALUE Backend_poll(VALUE self, VALUE blocking) {
211
222
 
212
223
  backend->base.poll_count++;
213
224
 
214
- if (!is_blocking && backend->pending_sqes) {
215
- backend->pending_sqes = 0;
216
- io_uring_submit(&backend->ring);
217
- }
225
+ if (!is_blocking && backend->pending_sqes) io_uring_backend_immediate_submit(backend);
218
226
 
219
227
  COND_TRACE(&backend->base, 2, SYM_enter_poll, rb_fiber_current());
220
228
 
@@ -263,8 +271,7 @@ VALUE Backend_wakeup(VALUE self) {
263
271
  // NOP which would cause the io_uring_enter syscall to return
264
272
  struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
265
273
  io_uring_prep_nop(sqe);
266
- backend->pending_sqes = 0;
267
- io_uring_submit(&backend->ring);
274
+ io_uring_backend_immediate_submit(backend);
268
275
 
269
276
  return Qtrue;
270
277
  }
@@ -272,14 +279,6 @@ VALUE Backend_wakeup(VALUE self) {
272
279
  return Qnil;
273
280
  }
274
281
 
275
- inline void io_uring_backend_defer_submit(Backend_t *backend) {
276
- backend->pending_sqes += 1;
277
- if (backend->pending_sqes >= backend->prepared_limit) {
278
- backend->pending_sqes = 0;
279
- io_uring_submit(&backend->ring);
280
- }
281
- }
282
-
283
282
  int io_uring_backend_defer_submit_and_await(
284
283
  Backend_t *backend,
285
284
  struct io_uring_sqe *sqe,
@@ -305,8 +304,7 @@ int io_uring_backend_defer_submit_and_await(
305
304
  ctx->result = -ECANCELED;
306
305
  sqe = io_uring_get_sqe(&backend->ring);
307
306
  io_uring_prep_cancel(sqe, (__u64)ctx, 0);
308
- backend->pending_sqes = 0;
309
- io_uring_submit(&backend->ring);
307
+ io_uring_backend_immediate_submit(backend);
310
308
  }
311
309
 
312
310
  if (value_ptr) (*value_ptr) = switchpoint_result;
@@ -552,7 +550,7 @@ VALUE Backend_write(VALUE self, VALUE io, VALUE str) {
552
550
  int result;
553
551
  int completed;
554
552
 
555
- io_uring_prep_write(sqe, fd, buffer.ptr, left, 0);
553
+ io_uring_prep_write(sqe, fd, buffer.ptr, left, -1);
556
554
 
557
555
  result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
558
556
  completed = context_store_release(&backend->store, ctx);
@@ -1161,8 +1159,7 @@ VALUE Backend_timeout_ensure(VALUE arg) {
1161
1159
  // op was not completed, so we need to cancel it
1162
1160
  sqe = io_uring_get_sqe(&timeout_ctx->backend->ring);
1163
1161
  io_uring_prep_cancel(sqe, (__u64)timeout_ctx->ctx, 0);
1164
- timeout_ctx->backend->pending_sqes = 0;
1165
- io_uring_submit(&timeout_ctx->backend->ring);
1162
+ io_uring_backend_immediate_submit(timeout_ctx->backend);
1166
1163
  }
1167
1164
  context_store_release(&timeout_ctx->backend->store, timeout_ctx->ctx);
1168
1165
  return Qnil;
@@ -1237,6 +1234,13 @@ VALUE Backend_waitpid(VALUE self, VALUE pid) {
1237
1234
  return rb_ary_new_from_args(2, INT2NUM(ret), INT2NUM(WEXITSTATUS(status)));
1238
1235
  }
1239
1236
 
1237
+ /*
1238
+ Blocks a fiber indefinitely. This is accomplished by using an eventfd that will
1239
+ never be signalled. The eventfd is needed so we could do a blocking polling for
1240
+ completions even when no other I/O operations are pending.
1241
+
1242
+ The eventfd is refcounted in order to allow multiple fibers to be blocked.
1243
+ */
1240
1244
  VALUE Backend_wait_event(VALUE self, VALUE raise) {
1241
1245
  Backend_t *backend;
1242
1246
  VALUE resume_value;
@@ -1251,7 +1255,32 @@ VALUE Backend_wait_event(VALUE self, VALUE raise) {
1251
1255
  }
1252
1256
  }
1253
1257
 
1254
- resume_value = io_uring_backend_wait_fd(backend, backend->event_fd, 0);
1258
+ if (!backend->event_fd_ctx) {
1259
+ struct io_uring_sqe *sqe;
1260
+
1261
+ backend->event_fd_ctx = context_store_acquire(&backend->store, OP_POLL);
1262
+ sqe = io_uring_get_sqe(&backend->ring);
1263
+ io_uring_prep_poll_add(sqe, backend->event_fd, POLLIN);
1264
+ backend->base.op_count++;
1265
+ io_uring_sqe_set_data(sqe, backend->event_fd_ctx);
1266
+ io_uring_backend_defer_submit(backend);
1267
+ }
1268
+ else
1269
+ backend->event_fd_ctx->ref_count += 1;
1270
+
1271
+ resume_value = backend_await((struct Backend_base *)backend);
1272
+ context_store_release(&backend->store, backend->event_fd_ctx);
1273
+
1274
+ if (backend->event_fd_ctx->ref_count == 1) {
1275
+
1276
+ // last fiber to use the eventfd, so we cancel the ongoing poll
1277
+ struct io_uring_sqe *sqe;
1278
+ sqe = io_uring_get_sqe(&backend->ring);
1279
+ io_uring_prep_cancel(sqe, (__u64)backend->event_fd_ctx, 0);
1280
+ io_uring_backend_immediate_submit(backend);
1281
+ backend->event_fd_ctx = NULL;
1282
+ }
1283
+
1255
1284
  if (RTEST(raise)) RAISE_IF_EXCEPTION(resume_value);
1256
1285
  RB_GC_GUARD(resume_value);
1257
1286
  return resume_value;
@@ -1354,8 +1383,7 @@ VALUE Backend_chain(int argc,VALUE *argv, VALUE self) {
1354
1383
  ctx->result = -ECANCELED;
1355
1384
  sqe = io_uring_get_sqe(&backend->ring);
1356
1385
  io_uring_prep_cancel(sqe, (__u64)ctx, 0);
1357
- backend->pending_sqes = 0;
1358
- io_uring_submit(&backend->ring);
1386
+ io_uring_backend_immediate_submit(backend);
1359
1387
  }
1360
1388
  else {
1361
1389
  ctx->ref_count = 1;
@@ -1385,8 +1413,7 @@ VALUE Backend_chain(int argc,VALUE *argv, VALUE self) {
1385
1413
  ctx->result = -ECANCELED;
1386
1414
  sqe = io_uring_get_sqe(&backend->ring);
1387
1415
  io_uring_prep_cancel(sqe, (__u64)ctx, 0);
1388
- backend->pending_sqes = 0;
1389
- io_uring_submit(&backend->ring);
1416
+ io_uring_backend_immediate_submit(backend);
1390
1417
  RAISE_IF_EXCEPTION(resume_value);
1391
1418
  return resume_value;
1392
1419
  }
@@ -1452,8 +1479,7 @@ static inline void splice_chunks_cancel(Backend_t *backend, op_context_t *ctx) {
1452
1479
  ctx->result = -ECANCELED;
1453
1480
  sqe = io_uring_get_sqe(&backend->ring);
1454
1481
  io_uring_prep_cancel(sqe, (__u64)ctx, 0);
1455
- backend->pending_sqes = 0;
1456
- io_uring_submit(&backend->ring);
1482
+ io_uring_backend_immediate_submit(backend);
1457
1483
  }
1458
1484
 
1459
1485
  static inline int splice_chunks_await_ops(
@@ -13,24 +13,68 @@
13
13
 
14
14
  ID ID_at;
15
15
  ID ID_read_method;
16
+ ID ID_readpartial;
16
17
  ID ID_to_i;
17
18
  ID ID_write_method;
19
+ ID ID_write;
18
20
 
21
+ VALUE SYM_backend_read;
22
+ VALUE SYM_backend_recv;
23
+ VALUE SYM_backend_send;
24
+ VALUE SYM_backend_write;
25
+ VALUE SYM_call;
26
+ VALUE SYM_comment;
19
27
  VALUE SYM_mtime;
20
28
  VALUE SYM_orig_name;
21
- VALUE SYM_comment;
29
+ VALUE SYM_readpartial;
22
30
 
23
31
  enum read_method {
24
32
  RM_BACKEND_READ,
25
- RM_BACKEND_RECV
33
+ RM_BACKEND_RECV,
34
+ RM_READPARTIAL,
35
+ RM_CALL
26
36
  };
27
37
 
28
38
  enum write_method {
29
39
  WM_BACKEND_WRITE,
30
- WM_BACKEND_SEND
40
+ WM_BACKEND_SEND,
41
+ WM_WRITE,
42
+ WM_CALL
31
43
  };
32
44
 
33
- #define print_buffer(prefix, ptr, len) { \
45
+ static enum read_method detect_read_method(VALUE io) {
46
+ if (rb_respond_to(io, ID_read_method)) {
47
+ VALUE method = rb_funcall(io, ID_read_method, 0);
48
+ if (method == SYM_readpartial) return RM_READPARTIAL;
49
+ if (method == SYM_backend_read) return RM_BACKEND_READ;
50
+ if (method == SYM_backend_recv) return RM_BACKEND_RECV;
51
+ if (method == SYM_call) return RM_CALL;
52
+
53
+ rb_raise(rb_eRuntimeError, "Given io instance uses unsupported read method");
54
+ }
55
+ else if (rb_respond_to(io, ID_call))
56
+ return RM_CALL;
57
+ else
58
+ rb_raise(rb_eRuntimeError, "Given io instance should be a callable or respond to #__read_method__");
59
+ }
60
+
61
+ static enum write_method detect_write_method(VALUE io) {
62
+ if (rb_respond_to(io, ID_write_method)) {
63
+ VALUE method = rb_funcall(io, ID_write_method, 0);
64
+ if (method == SYM_readpartial) return WM_WRITE;
65
+ if (method == SYM_backend_write) return WM_BACKEND_WRITE;
66
+ if (method == SYM_backend_send) return WM_BACKEND_SEND;
67
+ if (method == SYM_call) return WM_CALL;
68
+
69
+ rb_raise(rb_eRuntimeError, "Given io instance uses unsupported write method");
70
+ }
71
+ else if (rb_respond_to(io, ID_call))
72
+ return WM_CALL;
73
+ else
74
+ rb_raise(rb_eRuntimeError, "Given io instance should be a callable or respond to #__write_method__");
75
+ }
76
+
77
+ #define PRINT_BUFFER(prefix, ptr, len) { \
34
78
  printf("%s buffer (%d): ", prefix, (int)len); \
35
79
  for (int i = 0; i < len; i++) printf("%02X ", ptr[i]); \
36
80
  printf("\n"); \
@@ -40,6 +84,7 @@ enum write_method {
40
84
  #define MAX_WRITE_STR_LEN 16384
41
85
  #define DEFAULT_LEVEL 9
42
86
  #define DEFAULT_MEM_LEVEL 8
87
+ #define GZIP_FOOTER_LEN 8
43
88
 
44
89
  /* from zutil.h */
45
90
  #define OS_MSDOS 0x00
@@ -63,14 +108,64 @@ enum write_method {
63
108
  #define OS_CODE OS_UNIX
64
109
  #endif
65
110
 
111
+
66
112
  static inline int read_to_raw_buffer(VALUE backend, VALUE io, enum read_method method, struct raw_buffer *buffer) {
67
- VALUE len = Backend_read(backend, io, PTR2FIX(buffer), Qnil, Qfalse, INT2FIX(0));
68
- return (len == Qnil) ? 0 : FIX2INT(len);
113
+ switch (method) {
114
+ case RM_BACKEND_READ: {
115
+ VALUE len = Backend_read(backend, io, PTR2FIX(buffer), Qnil, Qfalse, INT2FIX(0));
116
+ return (len == Qnil) ? 0 : FIX2INT(len);
117
+ }
118
+ case RM_BACKEND_RECV: {
119
+ VALUE len = Backend_recv(backend, io, PTR2FIX(buffer), Qnil, INT2FIX(0));
120
+ return (len == Qnil) ? 0 : FIX2INT(len);
121
+ }
122
+ case RM_READPARTIAL: {
123
+ VALUE str = rb_funcall(io, ID_readpartial, 1, INT2FIX(buffer->len));
124
+ int len = RSTRING_LEN(str);
125
+ if (len) memcpy(buffer->ptr, RSTRING_PTR(str), len);
126
+ RB_GC_GUARD(str);
127
+ return len;
128
+ }
129
+ case RM_CALL: {
130
+ VALUE str = rb_funcall(io, ID_call, INT2FIX(buffer->len));
131
+ if (TYPE(str) != T_STRING)
132
+ rb_raise(rb_eRuntimeError, "io#call must return a string");
133
+ int len = RSTRING_LEN(str);
134
+ if (len > buffer->len) len = buffer->len;
135
+ if (len) memcpy(buffer->ptr, RSTRING_PTR(str), len);
136
+ RB_GC_GUARD(str);
137
+ return len;
138
+ }
139
+ }
69
140
  }
70
141
 
71
142
  static inline int write_from_raw_buffer(VALUE backend, VALUE io, enum write_method method, struct raw_buffer *buffer) {
72
- VALUE len = Backend_write(backend, io, PTR2FIX(buffer));
73
- return FIX2INT(len);
143
+ switch (method) {
144
+ case WM_BACKEND_WRITE: {
145
+ VALUE len = Backend_write(backend, io, PTR2FIX(buffer));
146
+ return FIX2INT(len);
147
+ }
148
+ case WM_BACKEND_SEND: {
149
+ VALUE len = Backend_send(backend, io, PTR2FIX(buffer), INT2FIX(0));
150
+ return FIX2INT(len);
151
+ }
152
+ case WM_WRITE: {
153
+ VALUE str = rb_str_new(0, buffer->len);
154
+ memcpy(RSTRING_PTR(str), buffer->ptr, buffer->len);
155
+ rb_str_modify_expand(str, buffer->len);
156
+ rb_funcall(io, ID_write, 1, str);
157
+ RB_GC_GUARD(str);
158
+ return buffer->len;
159
+ }
160
+ case WM_CALL: {
161
+ VALUE str = rb_str_new(0, buffer->len);
162
+ memcpy(RSTRING_PTR(str), buffer->ptr, buffer->len);
163
+ rb_str_modify_expand(str, buffer->len);
164
+ rb_funcall(io, ID_call, 1, str);
165
+ RB_GC_GUARD(str);
166
+ return buffer->len;
167
+ }
168
+ }
74
169
  }
75
170
 
76
171
  static inline int write_c_string_from_str(VALUE str, struct raw_buffer *buffer) {
@@ -165,12 +260,12 @@ int gzip_prepare_header(struct gzip_header_ctx *ctx, char *buffer, int maxlen) {
165
260
  }
166
261
 
167
262
  int gzip_prepare_footer(unsigned long crc32, unsigned long total_in, char *buffer, int maxlen) {
168
- assert(maxlen >= 8);
263
+ assert(maxlen >= GZIP_FOOTER_LEN);
169
264
 
170
265
  gzfile_set32(crc32, buffer);
171
266
  gzfile_set32(total_in, buffer + 4);
172
267
 
173
- return 8;
268
+ return GZIP_FOOTER_LEN;
174
269
  }
175
270
 
176
271
  enum stream_mode {
@@ -183,7 +278,11 @@ struct z_stream_ctx {
183
278
  VALUE src;
184
279
  VALUE dest;
185
280
 
281
+ enum read_method src_read_method;
282
+ enum write_method dest_write_method;
283
+
186
284
  enum stream_mode mode;
285
+ int f_gzip_footer; // should a gzip footer be generated
187
286
  z_stream strm;
188
287
 
189
288
  unsigned char in[CHUNK];
@@ -216,11 +315,11 @@ void gzip_read_header(struct z_stream_ctx *ctx, struct gzip_header_ctx *header_c
216
315
  int flags;
217
316
 
218
317
  while (ctx->in_total < 10) {
219
- int read = read_to_raw_buffer(ctx->backend, ctx->src, RM_BACKEND_READ, &in_buffer);
318
+ int read = read_to_raw_buffer(ctx->backend, ctx->src, ctx->src_read_method, &in_buffer);
220
319
  if (read == 0) goto error;
221
320
  ctx->in_total += read;
222
321
  }
223
- // print_buffer("read gzip header", ctx->in, ctx->in_total);
322
+ // PRINT_BUFFER("read gzip header", ctx->in, ctx->in_total);
224
323
  if (ctx->in[0] != GZ_MAGIC1) goto error;
225
324
  if (ctx->in[1] != GZ_MAGIC2) goto error;
226
325
  if (ctx->in[2] != GZ_METHOD_DEFLATE) goto error;
@@ -245,9 +344,6 @@ error:
245
344
  rb_raise(rb_eRuntimeError, "Invalid gzip header");
246
345
  }
247
346
 
248
- // void gzip_read_footer(struct z_stream_ctx *ctx, struct gzip_footer_ctx *footer_ctx) {
249
- // }
250
-
251
347
  static inline int z_stream_write_out(struct z_stream_ctx *ctx, zlib_func fun, int eof) {
252
348
  int ret;
253
349
  int written;
@@ -260,8 +356,14 @@ static inline int z_stream_write_out(struct z_stream_ctx *ctx, zlib_func fun, in
260
356
  written = avail_out_pre - ctx->strm.avail_out;
261
357
  out_buffer.ptr = ctx->out;
262
358
  out_buffer.len = ctx->out_pos + written;
359
+
360
+ if (eof && ctx->f_gzip_footer && (CHUNK - out_buffer.len >= GZIP_FOOTER_LEN)) {
361
+ gzip_prepare_footer(ctx->crc32, ctx->in_total, out_buffer.ptr + out_buffer.len, 8);
362
+ out_buffer.len += GZIP_FOOTER_LEN;
363
+ }
364
+
263
365
  if (out_buffer.len) {
264
- ret = write_from_raw_buffer(ctx->backend, ctx->dest, WM_BACKEND_WRITE, &out_buffer);
366
+ ret = write_from_raw_buffer(ctx->backend, ctx->dest, ctx->dest_write_method, &out_buffer);
265
367
  if (ctx->mode == SM_INFLATE)
266
368
  ctx->crc32 = crc32(ctx->crc32, out_buffer.ptr + ctx->out_pos, written);
267
369
  ctx->out_total += ret - ctx->out_pos;
@@ -271,7 +373,7 @@ static inline int z_stream_write_out(struct z_stream_ctx *ctx, zlib_func fun, in
271
373
  }
272
374
 
273
375
  void z_stream_io_loop(struct z_stream_ctx *ctx) {
274
- zlib_func fun = (ctx->mode == SM_DEFLATE) ? deflate : inflate;
376
+ zlib_func fun = (ctx->mode == SM_DEFLATE) ? deflate : inflate;
275
377
 
276
378
  if (ctx->in_total > ctx->in_pos) {
277
379
  // In bytes already read for parsing gzip header, so we need to process the
@@ -289,7 +391,7 @@ void z_stream_io_loop(struct z_stream_ctx *ctx) {
289
391
  while (1) {
290
392
  struct raw_buffer in_buffer = {ctx->in, CHUNK};
291
393
  ctx->strm.next_in = ctx->in;
292
- int read_len = ctx->strm.avail_in = read_to_raw_buffer(ctx->backend, ctx->src, RM_BACKEND_READ, &in_buffer);
394
+ int read_len = ctx->strm.avail_in = read_to_raw_buffer(ctx->backend, ctx->src, ctx->src_read_method, &in_buffer);
293
395
  if (!read_len) break;
294
396
  int eof = read_len < CHUNK;
295
397
 
@@ -297,7 +399,7 @@ void z_stream_io_loop(struct z_stream_ctx *ctx) {
297
399
  ctx->crc32 = crc32(ctx->crc32, ctx->in, read_len);
298
400
  ctx->in_total += read_len;
299
401
 
300
- // print_buffer("read stream", ctx->in, read_len);
402
+ // PRINT_BUFFER("read stream", ctx->in, read_len);
301
403
 
302
404
  while (1) {
303
405
  // z_stream_write_out returns strm.avail_out. If there's still room in the
@@ -318,7 +420,10 @@ static inline void setup_ctx(struct z_stream_ctx *ctx, enum stream_mode mode, VA
318
420
  ctx->backend = BACKEND();
319
421
  ctx->src = src;
320
422
  ctx->dest = dest;
423
+ ctx->src_read_method = detect_read_method(src);
424
+ ctx->dest_write_method = detect_write_method(dest);
321
425
  ctx->mode = mode;
426
+ ctx->f_gzip_footer = 0;
322
427
  ctx->strm.zalloc = Z_NULL;
323
428
  ctx->strm.zfree = Z_NULL;
324
429
  ctx->strm.opaque = Z_NULL;
@@ -349,14 +454,12 @@ VALUE IO_gzip(int argc, VALUE *argv, VALUE self) {
349
454
  int ret;
350
455
 
351
456
  setup_ctx(&ctx, SM_DEFLATE, src, dest);
457
+ ctx.f_gzip_footer = 1; // write gzip footer
352
458
  ctx.out_pos = gzip_prepare_header(&header_ctx, ctx.out, sizeof(ctx.out));
353
459
 
354
460
  ret = deflateInit2(&ctx.strm, level, Z_DEFLATED, -MAX_WBITS, DEFAULT_MEM_LEVEL, Z_DEFAULT_STRATEGY);
355
461
  if (ret != Z_OK) return INT2FIX(ret);
356
462
  z_stream_io_loop(&ctx);
357
- int footer_len = gzip_prepare_footer(ctx.crc32, ctx.in_total, ctx.out, sizeof(ctx.out));
358
- struct raw_buffer footer_buffer = {ctx.out, footer_len};
359
- write_from_raw_buffer(ctx.backend, dest, WM_BACKEND_WRITE, &footer_buffer);
360
463
  deflateEnd(&ctx.strm);
361
464
 
362
465
  return INT2FIX(ctx.out_total);
@@ -431,10 +534,18 @@ void Init_IOExtensions() {
431
534
 
432
535
  ID_at = rb_intern("at");
433
536
  ID_read_method = rb_intern("__read_method__");
537
+ ID_readpartial = rb_intern("readpartial");
434
538
  ID_to_i = rb_intern("to_i");
435
539
  ID_write_method = rb_intern("__write_method__");
436
-
437
- SYM_mtime = ID2SYM(rb_intern("mtime"));
438
- SYM_orig_name = ID2SYM(rb_intern("orig_name"));
439
- SYM_comment = ID2SYM(rb_intern("comment"));
540
+ ID_write = rb_intern("write");
541
+
542
+ SYM_backend_read = ID2SYM(rb_intern("backend_read"));
543
+ SYM_backend_recv = ID2SYM(rb_intern("backend_recv"));
544
+ SYM_backend_send = ID2SYM(rb_intern("backend_send"));
545
+ SYM_backend_write = ID2SYM(rb_intern("backend_write"));
546
+ SYM_call = ID2SYM(rb_intern("call"));
547
+ SYM_comment = ID2SYM(rb_intern("comment"));
548
+ SYM_mtime = ID2SYM(rb_intern("mtime"));
549
+ SYM_orig_name = ID2SYM(rb_intern("orig_name"));
550
+ SYM_readpartial = ID2SYM(rb_intern("readpartial"));
440
551
  }
@@ -390,12 +390,14 @@ module Polyphony
390
390
  def shutdown_all_children(graceful = false)
391
391
  return self unless @children
392
392
 
393
+ pending = []
393
394
  @children.keys.each do |c|
394
395
  next if c.dead?
395
396
 
396
397
  c.terminate(graceful)
397
- c.await
398
+ pending << c
398
399
  end
400
+ Fiber.await(*pending)
399
401
  self
400
402
  end
401
403
 
@@ -96,6 +96,10 @@ class ::IO
96
96
  :backend_read
97
97
  end
98
98
 
99
+ def __write_method__
100
+ :backend_write
101
+ end
102
+
99
103
  # def each(sep = $/, limit = nil, chomp: nil)
100
104
  # sep, limit = $/, sep if sep.is_a?(Integer)
101
105
  # end
@@ -6,6 +6,10 @@ class Polyphony::Pipe
6
6
  :backend_read
7
7
  end
8
8
 
9
+ def __write_method__
10
+ :backend_write
11
+ end
12
+
9
13
  def getbyte
10
14
  char = getc
11
15
  char ? char.getbyte(0) : nil
@@ -14,6 +14,10 @@ class BasicSocket
14
14
  def __read_method__
15
15
  :backend_recv
16
16
  end
17
+
18
+ def __write_method__
19
+ :backend_send
20
+ end
17
21
  end
18
22
 
19
23
  # Socket extensions # TODO: rewrite in C