uringmachine 0.23.1 → 0.25.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.
- checksums.yaml +4 -4
- data/.github/workflows/test.yml +1 -1
- data/.gitignore +1 -0
- data/.gitmodules +0 -3
- data/CHANGELOG.md +17 -0
- data/Gemfile +12 -1
- data/README.md +266 -112
- data/Rakefile +8 -0
- data/TODO.md +40 -17
- data/benchmark/bm_io_pipe.rb +43 -1
- data/benchmark/bm_io_socketpair.rb +32 -2
- data/benchmark/bm_mutex_io.rb +47 -5
- data/benchmark/chart_bm_io_pipe_x.png +0 -0
- data/benchmark/common.rb +163 -17
- data/benchmark/http_parse.rb +9 -9
- data/benchmark/http_server_accept_queue.rb +104 -0
- data/benchmark/http_server_multi_accept.rb +93 -0
- data/benchmark/http_server_multi_ractor.rb +99 -0
- data/benchmark/http_server_single_thread.rb +80 -0
- data/benchmark/ips_io_pipe.rb +146 -0
- data/benchmark/openssl.rb +77 -0
- data/benchmark/openssl_socketpair.rb +112 -0
- data/benchmark/sqlite.rb +1 -1
- data/docs/design/buffer_pool.md +183 -0
- data/docs/um_api.md +91 -0
- data/examples/fiber_scheduler_file_io.rb +34 -0
- data/examples/fiber_scheduler_file_io_async.rb +33 -0
- data/ext/um/extconf.rb +15 -0
- data/ext/um/um.c +83 -50
- data/ext/um/um.h +18 -3
- data/ext/um/um_async_op_class.c +31 -0
- data/ext/um/um_class.c +759 -30
- data/ext/um/um_const.c +31 -0
- data/ext/um/um_mutex_class.c +12 -0
- data/ext/um/um_queue_class.c +16 -0
- data/ext/um/um_sidecar.c +106 -0
- data/ext/um/um_ssl.c +109 -0
- data/ext/um/um_stream.c +40 -8
- data/ext/um/um_stream_class.c +14 -0
- data/ext/um/um_utils.c +3 -4
- data/grant-2025/interim-report.md +130 -0
- data/grant-2025/journal.md +166 -2
- data/grant-2025/tasks.md +32 -20
- data/lib/uringmachine/dns_resolver.rb +38 -0
- data/lib/uringmachine/fiber_scheduler.rb +42 -32
- data/lib/uringmachine/version.rb +1 -1
- data/lib/uringmachine.rb +105 -7
- data/test/helper.rb +23 -3
- data/test/test_fiber.rb +16 -0
- data/test/test_fiber_scheduler.rb +221 -72
- data/test/test_ssl.rb +85 -0
- data/test/test_stream.rb +27 -0
- data/test/test_um.rb +250 -26
- data/uringmachine.gemspec +1 -7
- data/vendor/liburing/examples/send-zerocopy.c +43 -31
- data/vendor/liburing/examples/zcrx.c +260 -69
- data/vendor/liburing/liburing.spec +1 -1
- data/vendor/liburing/src/include/liburing/io_uring.h +12 -0
- data/vendor/liburing/src/include/liburing.h +3 -2
- data/vendor/liburing/src/liburing-ffi.map +4 -0
- data/vendor/liburing/src/liburing.map +4 -0
- data/vendor/liburing/src/queue.c +12 -0
- data/vendor/liburing/src/register.c +1 -0
- data/vendor/liburing/src/setup.c +15 -7
- data/vendor/liburing/test/Makefile +8 -4
- data/vendor/liburing/test/conn-unreach.c +1 -1
- data/vendor/liburing/test/epwait.c +32 -6
- data/vendor/liburing/test/io-wq-exit.c +131 -0
- data/vendor/liburing/test/iowait.c +1 -1
- data/vendor/liburing/test/min-timeout.c +3 -1
- data/vendor/liburing/test/open-close.c +39 -0
- data/vendor/liburing/test/poll-update-trigger.c +85 -0
- data/vendor/liburing/test/recvsend_bundle.c +14 -11
- data/vendor/liburing/test/sendzc-bug.c +146 -0
- data/vendor/liburing/test/sqe-mixed-nop.c +151 -7
- data/vendor/liburing/test/test.h +2 -0
- data/vendor/liburing/test/timestamp-bug.c +135 -0
- data/vendor/liburing/test/timestamp.c +5 -0
- data/vendor/liburing/test/vec-regbuf.c +136 -1
- metadata +50 -284
- data/vendor/libressl/.github/scripts/changelog.sh +0 -74
- data/vendor/libressl/.github/workflows/android.yml +0 -35
- data/vendor/libressl/.github/workflows/cifuzz.yml +0 -33
- data/vendor/libressl/.github/workflows/cmake-config.yml +0 -98
- data/vendor/libressl/.github/workflows/coverity.yml +0 -69
- data/vendor/libressl/.github/workflows/emscripten.yml +0 -71
- data/vendor/libressl/.github/workflows/fedora-rawhide.yml +0 -39
- data/vendor/libressl/.github/workflows/freebsd.yml +0 -71
- data/vendor/libressl/.github/workflows/linux.yml +0 -71
- data/vendor/libressl/.github/workflows/macos.yml +0 -37
- data/vendor/libressl/.github/workflows/release.yml +0 -81
- data/vendor/libressl/.github/workflows/rust-openssl.yml +0 -47
- data/vendor/libressl/.github/workflows/solaris.yml +0 -37
- data/vendor/libressl/.github/workflows/windows.yml +0 -70
- data/vendor/libressl/.gitignore +0 -333
- data/vendor/libressl/CMakeLists.txt +0 -581
- data/vendor/libressl/COPYING +0 -133
- data/vendor/libressl/ChangeLog +0 -3280
- data/vendor/libressl/FindLibreSSL.cmake +0 -232
- data/vendor/libressl/LibreSSLConfig.cmake.in +0 -36
- data/vendor/libressl/Makefile.am +0 -60
- data/vendor/libressl/Makefile.am.common +0 -20
- data/vendor/libressl/OPENBSD_BRANCH +0 -1
- data/vendor/libressl/README.md +0 -238
- data/vendor/libressl/README.mingw.md +0 -43
- data/vendor/libressl/apps/CMakeLists.txt +0 -18
- data/vendor/libressl/apps/Makefile.am +0 -5
- data/vendor/libressl/apps/nc/CMakeLists.txt +0 -67
- data/vendor/libressl/apps/nc/Makefile.am +0 -64
- data/vendor/libressl/apps/nc/compat/accept4.c +0 -17
- data/vendor/libressl/apps/nc/compat/readpassphrase.c +0 -205
- data/vendor/libressl/apps/nc/compat/socket.c +0 -29
- data/vendor/libressl/apps/nc/compat/sys/socket.h +0 -30
- data/vendor/libressl/apps/ocspcheck/CMakeLists.txt +0 -44
- data/vendor/libressl/apps/ocspcheck/Makefile.am +0 -45
- data/vendor/libressl/apps/ocspcheck/compat/.gitignore +0 -0
- data/vendor/libressl/apps/openssl/CMakeLists.txt +0 -97
- data/vendor/libressl/apps/openssl/Makefile.am +0 -108
- data/vendor/libressl/apps/openssl/apps_win.c +0 -138
- data/vendor/libressl/apps/openssl/certhash_win.c +0 -13
- data/vendor/libressl/apps/openssl/compat/clock_gettime_osx.c +0 -26
- data/vendor/libressl/apps/openssl/compat/poll_win.c +0 -329
- data/vendor/libressl/appveyor.yml +0 -53
- data/vendor/libressl/autogen.sh +0 -15
- data/vendor/libressl/check-release.sh +0 -86
- data/vendor/libressl/cmake_export_symbol.cmake +0 -71
- data/vendor/libressl/cmake_uninstall.cmake.in +0 -36
- data/vendor/libressl/config +0 -17
- data/vendor/libressl/configure.ac +0 -165
- data/vendor/libressl/crypto/CMakeLists.txt +0 -863
- data/vendor/libressl/crypto/Makefile.am +0 -962
- data/vendor/libressl/crypto/Makefile.am.arc4random +0 -46
- data/vendor/libressl/crypto/Makefile.am.elf-mips +0 -14
- data/vendor/libressl/crypto/Makefile.am.elf-mips64 +0 -14
- data/vendor/libressl/crypto/Makefile.am.elf-x86_64 +0 -35
- data/vendor/libressl/crypto/Makefile.am.macosx-x86_64 +0 -35
- data/vendor/libressl/crypto/Makefile.am.masm-x86_64 +0 -22
- data/vendor/libressl/crypto/Makefile.am.mingw64-x86_64 +0 -23
- data/vendor/libressl/crypto/arch/aarch64/crypto_cpu_caps_darwin.c +0 -60
- data/vendor/libressl/crypto/arch/aarch64/crypto_cpu_caps_linux.c +0 -62
- data/vendor/libressl/crypto/arch/aarch64/crypto_cpu_caps_none.c +0 -26
- data/vendor/libressl/crypto/arch/aarch64/crypto_cpu_caps_windows.c +0 -36
- data/vendor/libressl/crypto/arch/loongarch64/crypto_arch.h +0 -21
- data/vendor/libressl/crypto/arch/mips/crypto_arch.h +0 -21
- data/vendor/libressl/crypto/bn/arch/loongarch64/bn_arch.h +0 -23
- data/vendor/libressl/crypto/bn/arch/mips/bn_arch.h +0 -24
- data/vendor/libressl/crypto/compat/.gitignore +0 -31
- data/vendor/libressl/crypto/compat/arc4random.h +0 -41
- data/vendor/libressl/crypto/compat/b_win.c +0 -55
- data/vendor/libressl/crypto/compat/bsd-asprintf.c +0 -96
- data/vendor/libressl/crypto/compat/crypto_lock_win.c +0 -56
- data/vendor/libressl/crypto/compat/explicit_bzero_win.c +0 -13
- data/vendor/libressl/crypto/compat/freezero.c +0 -32
- data/vendor/libressl/crypto/compat/getdelim.c +0 -78
- data/vendor/libressl/crypto/compat/getline.c +0 -40
- data/vendor/libressl/crypto/compat/getopt_long.c +0 -528
- data/vendor/libressl/crypto/compat/getpagesize.c +0 -18
- data/vendor/libressl/crypto/compat/getprogname_linux.c +0 -23
- data/vendor/libressl/crypto/compat/getprogname_unimpl.c +0 -7
- data/vendor/libressl/crypto/compat/getprogname_windows.c +0 -13
- data/vendor/libressl/crypto/compat/posix_win.c +0 -296
- data/vendor/libressl/crypto/compat/syslog_r.c +0 -19
- data/vendor/libressl/crypto/compat/ui_openssl_win.c +0 -334
- data/vendor/libressl/dist.sh +0 -22
- data/vendor/libressl/gen-coverage-report.sh +0 -58
- data/vendor/libressl/gen-openbsd-tags.sh +0 -20
- data/vendor/libressl/include/CMakeLists.txt +0 -61
- data/vendor/libressl/include/Makefile.am +0 -79
- data/vendor/libressl/include/arch/loongarch64/opensslconf.h +0 -150
- data/vendor/libressl/include/arch/mips/opensslconf.h +0 -150
- data/vendor/libressl/include/compat/arpa/inet.h +0 -15
- data/vendor/libressl/include/compat/arpa/nameser.h +0 -25
- data/vendor/libressl/include/compat/cet.h +0 -19
- data/vendor/libressl/include/compat/dirent.h +0 -17
- data/vendor/libressl/include/compat/dirent_msvc.h +0 -611
- data/vendor/libressl/include/compat/endian.h +0 -161
- data/vendor/libressl/include/compat/err.h +0 -95
- data/vendor/libressl/include/compat/fcntl.h +0 -32
- data/vendor/libressl/include/compat/getopt.h +0 -50
- data/vendor/libressl/include/compat/limits.h +0 -25
- data/vendor/libressl/include/compat/netdb.h +0 -10
- data/vendor/libressl/include/compat/netinet/in.h +0 -19
- data/vendor/libressl/include/compat/netinet/ip.h +0 -49
- data/vendor/libressl/include/compat/netinet/tcp.h +0 -10
- data/vendor/libressl/include/compat/poll.h +0 -63
- data/vendor/libressl/include/compat/pthread.h +0 -122
- data/vendor/libressl/include/compat/readpassphrase.h +0 -44
- data/vendor/libressl/include/compat/resolv.h +0 -24
- data/vendor/libressl/include/compat/stdint.h +0 -31
- data/vendor/libressl/include/compat/stdio.h +0 -65
- data/vendor/libressl/include/compat/stdlib.h +0 -57
- data/vendor/libressl/include/compat/string.h +0 -98
- data/vendor/libressl/include/compat/sys/_null.h +0 -18
- data/vendor/libressl/include/compat/sys/ioctl.h +0 -11
- data/vendor/libressl/include/compat/sys/mman.h +0 -19
- data/vendor/libressl/include/compat/sys/param.h +0 -15
- data/vendor/libressl/include/compat/sys/queue.h +0 -536
- data/vendor/libressl/include/compat/sys/select.h +0 -10
- data/vendor/libressl/include/compat/sys/socket.h +0 -18
- data/vendor/libressl/include/compat/sys/stat.h +0 -129
- data/vendor/libressl/include/compat/sys/time.h +0 -37
- data/vendor/libressl/include/compat/sys/tree.h +0 -1006
- data/vendor/libressl/include/compat/sys/types.h +0 -69
- data/vendor/libressl/include/compat/sys/uio.h +0 -17
- data/vendor/libressl/include/compat/syslog.h +0 -38
- data/vendor/libressl/include/compat/time.h +0 -59
- data/vendor/libressl/include/compat/unistd.h +0 -83
- data/vendor/libressl/include/compat/win32netcompat.h +0 -57
- data/vendor/libressl/include/openssl/Makefile.am.tpl +0 -45
- data/vendor/libressl/libcrypto.pc.in +0 -28
- data/vendor/libressl/libressl.pub +0 -2
- data/vendor/libressl/libssl.pc.in +0 -28
- data/vendor/libressl/libtls.pc.in +0 -28
- data/vendor/libressl/m4/ax_add_fortify_source.m4 +0 -80
- data/vendor/libressl/m4/ax_check_compile_flag.m4 +0 -53
- data/vendor/libressl/m4/check-hardening-options.m4 +0 -110
- data/vendor/libressl/m4/check-libc.m4 +0 -189
- data/vendor/libressl/m4/check-os-options.m4 +0 -181
- data/vendor/libressl/m4/disable-compiler-warnings.m4 +0 -44
- data/vendor/libressl/man/CMakeLists.txt +0 -26
- data/vendor/libressl/man/links +0 -2780
- data/vendor/libressl/man/update_links.sh +0 -25
- data/vendor/libressl/openssl.pc.in +0 -11
- data/vendor/libressl/patches/bn_shift.patch +0 -34
- data/vendor/libressl/patches/crypto_arch.h.patch +0 -34
- data/vendor/libressl/patches/crypto_namespace.h.patch +0 -22
- data/vendor/libressl/patches/netcat.c.patch +0 -178
- data/vendor/libressl/patches/openssl.c.patch +0 -12
- data/vendor/libressl/patches/opensslfeatures.h.patch +0 -49
- data/vendor/libressl/patches/patch-amd64-crypto-cpu-caps.c.patch +0 -20
- data/vendor/libressl/patches/patch-i386-crypto-cpu-caps.c.patch +0 -20
- data/vendor/libressl/patches/speed.c.patch +0 -114
- data/vendor/libressl/patches/ssl_namespace.h.patch +0 -21
- data/vendor/libressl/patches/tls.h.patch +0 -16
- data/vendor/libressl/patches/tls_config.c.patch +0 -15
- data/vendor/libressl/patches/win32_amd64_bn_arch.h.patch +0 -28
- data/vendor/libressl/patches/windows_headers.patch +0 -80
- data/vendor/libressl/scripts/config.guess +0 -1774
- data/vendor/libressl/scripts/config.sub +0 -1907
- data/vendor/libressl/scripts/i686-w64-mingw32.cmake +0 -9
- data/vendor/libressl/scripts/test +0 -210
- data/vendor/libressl/scripts/wrap-compiler-for-flag-check +0 -31
- data/vendor/libressl/scripts/x86_64-w64-mingw32.cmake +0 -9
- data/vendor/libressl/ssl/CMakeLists.txt +0 -183
- data/vendor/libressl/ssl/Makefile.am +0 -187
- data/vendor/libressl/tests/CMakeLists.txt +0 -970
- data/vendor/libressl/tests/Makefile.am +0 -944
- data/vendor/libressl/tests/aeadtest.sh +0 -30
- data/vendor/libressl/tests/arc4randomforktest.sh +0 -21
- data/vendor/libressl/tests/asn1time_small.test +0 -10
- data/vendor/libressl/tests/cmake/CMakeLists.txt +0 -52
- data/vendor/libressl/tests/cmake/crypto.c +0 -7
- data/vendor/libressl/tests/cmake/ssl.c +0 -6
- data/vendor/libressl/tests/cmake/tls.c +0 -6
- data/vendor/libressl/tests/compat/pipe2.c +0 -186
- data/vendor/libressl/tests/dtlstest.sh +0 -28
- data/vendor/libressl/tests/evptest.sh +0 -22
- data/vendor/libressl/tests/keypairtest.sh +0 -27
- data/vendor/libressl/tests/mlkem_tests.sh +0 -39
- data/vendor/libressl/tests/ocsptest.bat +0 -25
- data/vendor/libressl/tests/ocsptest.sh +0 -23
- data/vendor/libressl/tests/openssl.cnf +0 -29
- data/vendor/libressl/tests/optionstest.c +0 -381
- data/vendor/libressl/tests/pidwraptest.c +0 -85
- data/vendor/libressl/tests/pidwraptest.sh +0 -26
- data/vendor/libressl/tests/quictest.bat +0 -27
- data/vendor/libressl/tests/quictest.sh +0 -30
- data/vendor/libressl/tests/renegotiation_test.bat +0 -27
- data/vendor/libressl/tests/renegotiation_test.sh +0 -30
- data/vendor/libressl/tests/rfc5280time_small.test +0 -10
- data/vendor/libressl/tests/servertest.bat +0 -27
- data/vendor/libressl/tests/servertest.sh +0 -30
- data/vendor/libressl/tests/shutdowntest.bat +0 -27
- data/vendor/libressl/tests/shutdowntest.sh +0 -30
- data/vendor/libressl/tests/ssltest.bat +0 -32
- data/vendor/libressl/tests/ssltest.sh +0 -48
- data/vendor/libressl/tests/testdsa.bat +0 -47
- data/vendor/libressl/tests/testdsa.sh +0 -57
- data/vendor/libressl/tests/testenc.bat +0 -85
- data/vendor/libressl/tests/testenc.sh +0 -93
- data/vendor/libressl/tests/testrsa.bat +0 -47
- data/vendor/libressl/tests/testrsa.sh +0 -57
- data/vendor/libressl/tests/testssl.bat +0 -171
- data/vendor/libressl/tests/tlstest.bat +0 -27
- data/vendor/libressl/tests/tlstest.sh +0 -28
- data/vendor/libressl/tls/CMakeLists.txt +0 -125
- data/vendor/libressl/tls/Makefile.am +0 -76
- data/vendor/libressl/tls/compat/ftruncate.c +0 -17
- data/vendor/libressl/tls/compat/pread.c +0 -29
- data/vendor/libressl/tls/compat/pwrite.c +0 -29
- data/vendor/libressl/update.sh +0 -460
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
require_relative './common'
|
|
4
4
|
require 'socket'
|
|
5
5
|
|
|
6
|
-
GROUPS =
|
|
6
|
+
GROUPS = 48
|
|
7
7
|
ITERATIONS = 10000
|
|
8
8
|
|
|
9
|
-
SIZE =
|
|
9
|
+
SIZE = 1 << 14
|
|
10
10
|
DATA = '*' * SIZE
|
|
11
11
|
|
|
12
12
|
class UMBenchmark
|
|
@@ -55,6 +55,22 @@ class UMBenchmark
|
|
|
55
55
|
end
|
|
56
56
|
end
|
|
57
57
|
|
|
58
|
+
def do_scheduler_x(div, scheduler, ios)
|
|
59
|
+
(GROUPS/div).times do
|
|
60
|
+
r, w = Socket.socketpair(:AF_UNIX, :SOCK_STREAM, 0)
|
|
61
|
+
r.sync = true
|
|
62
|
+
w.sync = true
|
|
63
|
+
Fiber.schedule do
|
|
64
|
+
ITERATIONS.times { w.send(DATA, 0) }
|
|
65
|
+
w.close
|
|
66
|
+
end
|
|
67
|
+
Fiber.schedule do
|
|
68
|
+
ITERATIONS.times { r.recv(SIZE) }
|
|
69
|
+
r.close
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
58
74
|
def do_um(machine, fibers, fds)
|
|
59
75
|
GROUPS.times do
|
|
60
76
|
r, w = UM.socketpair(UM::AF_UNIX, UM::SOCK_STREAM, 0)
|
|
@@ -68,4 +84,18 @@ class UMBenchmark
|
|
|
68
84
|
end
|
|
69
85
|
end
|
|
70
86
|
end
|
|
87
|
+
|
|
88
|
+
def do_um_x(div, machine, fibers, fds)
|
|
89
|
+
(GROUPS/div).times do
|
|
90
|
+
r, w = UM.socketpair(UM::AF_UNIX, UM::SOCK_STREAM, 0)
|
|
91
|
+
fibers << machine.spin do
|
|
92
|
+
ITERATIONS.times { machine.send(w, DATA, SIZE, UM::MSG_WAITALL) }
|
|
93
|
+
machine.close_async(w)
|
|
94
|
+
end
|
|
95
|
+
fibers << machine.spin do
|
|
96
|
+
ITERATIONS.times { machine.recv(r, +'', SIZE, 0) }
|
|
97
|
+
machine.close_async(r)
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
71
101
|
end
|
data/benchmark/bm_mutex_io.rb
CHANGED
|
@@ -4,9 +4,9 @@ require_relative './common'
|
|
|
4
4
|
require 'securerandom'
|
|
5
5
|
require 'fileutils'
|
|
6
6
|
|
|
7
|
-
GROUPS = ENV['N']&.to_i ||
|
|
7
|
+
GROUPS = ENV['N']&.to_i || 48
|
|
8
8
|
WORKERS = 10
|
|
9
|
-
ITERATIONS =
|
|
9
|
+
ITERATIONS = 10000
|
|
10
10
|
|
|
11
11
|
puts "N=#{GROUPS}"
|
|
12
12
|
|
|
@@ -14,10 +14,15 @@ SIZE = 1024
|
|
|
14
14
|
DATA = "*" * SIZE
|
|
15
15
|
|
|
16
16
|
class UMBenchmark
|
|
17
|
+
def cleanup
|
|
18
|
+
# `rm /tmp/mutex*` rescue nil
|
|
19
|
+
end
|
|
20
|
+
|
|
17
21
|
def do_threads(threads, ios)
|
|
18
22
|
GROUPS.times do
|
|
19
23
|
mutex = Mutex.new
|
|
20
|
-
ios << (f = File.open("/tmp/mutex_io_threads_#{SecureRandom.hex}", 'w'))
|
|
24
|
+
# ios << (f = File.open("/tmp/mutex_io_threads_#{SecureRandom.hex}", 'w'))
|
|
25
|
+
ios << (f = File.open("/dev/null", 'w'))
|
|
21
26
|
f.sync = true
|
|
22
27
|
WORKERS.times do
|
|
23
28
|
threads << Thread.new do
|
|
@@ -34,7 +39,24 @@ class UMBenchmark
|
|
|
34
39
|
def do_scheduler(scheduler, ios)
|
|
35
40
|
GROUPS.times do
|
|
36
41
|
mutex = Mutex.new
|
|
37
|
-
ios << (f = File.open("/tmp/mutex_io_fiber_scheduler_#{SecureRandom.hex}", 'w'))
|
|
42
|
+
# ios << (f = File.open("/tmp/mutex_io_fiber_scheduler_#{SecureRandom.hex}", 'w'))
|
|
43
|
+
ios << (f = File.open("/dev/null", 'w'))
|
|
44
|
+
f.sync = true
|
|
45
|
+
WORKERS.times do
|
|
46
|
+
Fiber.schedule do
|
|
47
|
+
ITERATIONS.times do
|
|
48
|
+
mutex.synchronize { f.write(DATA) }
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def do_scheduler_x(div, scheduler, ios)
|
|
56
|
+
(GROUPS/div).times do
|
|
57
|
+
mutex = Mutex.new
|
|
58
|
+
# ios << (f = File.open("/tmp/mutex_io_fiber_scheduler_#{SecureRandom.hex}", 'w'))
|
|
59
|
+
ios << (f = File.open("/dev/null", 'w'))
|
|
38
60
|
f.sync = true
|
|
39
61
|
WORKERS.times do
|
|
40
62
|
Fiber.schedule do
|
|
@@ -49,7 +71,27 @@ class UMBenchmark
|
|
|
49
71
|
def do_um(machine, fibers, fds)
|
|
50
72
|
GROUPS.times do
|
|
51
73
|
mutex = UM::Mutex.new
|
|
52
|
-
fds << (fd = machine.open("/tmp/mutex_io_um_#{SecureRandom.hex}", UM::O_CREAT | UM::O_WRONLY))
|
|
74
|
+
# fds << (fd = machine.open("/tmp/mutex_io_um_#{SecureRandom.hex}", UM::O_CREAT | UM::O_WRONLY))
|
|
75
|
+
fds << (fd = machine.open("/dev/null", UM::O_WRONLY))
|
|
76
|
+
WORKERS.times do
|
|
77
|
+
fibers << machine.spin do
|
|
78
|
+
ITERATIONS.times do
|
|
79
|
+
machine.synchronize(mutex) do
|
|
80
|
+
machine.write(fd, DATA)
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
rescue => e
|
|
84
|
+
p e
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def do_um_x(div, machine, fibers, fds)
|
|
91
|
+
(GROUPS/div).times do
|
|
92
|
+
mutex = UM::Mutex.new
|
|
93
|
+
# fds << (fd = machine.open("/tmp/mutex_io_um_#{SecureRandom.hex}", UM::O_CREAT | UM::O_WRONLY))
|
|
94
|
+
fds << (fd = machine.open("/dev/null", UM::O_WRONLY))
|
|
53
95
|
WORKERS.times do
|
|
54
96
|
fibers << machine.spin do
|
|
55
97
|
ITERATIONS.times do
|
|
Binary file
|
data/benchmark/common.rb
CHANGED
|
@@ -9,6 +9,9 @@ gemfile do
|
|
|
9
9
|
gem 'io-event'
|
|
10
10
|
gem 'async'
|
|
11
11
|
gem 'pg'
|
|
12
|
+
gem 'gvltools'
|
|
13
|
+
gem 'openssl'
|
|
14
|
+
gem 'localhost'
|
|
12
15
|
end
|
|
13
16
|
|
|
14
17
|
require 'uringmachine/fiber_scheduler'
|
|
@@ -54,26 +57,56 @@ class UMBenchmark
|
|
|
54
57
|
end
|
|
55
58
|
|
|
56
59
|
@@benchmarks = {
|
|
57
|
-
baseline: [:baseline, "No Concurrency"],
|
|
58
|
-
|
|
59
|
-
thread_pool: [:thread_pool, "ThreadPool"],
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
60
|
+
# baseline: [:baseline, "No Concurrency"],
|
|
61
|
+
# baseline_um: [:baseline_um, "UM no concurrency"],
|
|
62
|
+
# thread_pool: [:thread_pool, "ThreadPool"],
|
|
63
|
+
|
|
64
|
+
threads: [:threads, "Threads"],
|
|
65
|
+
|
|
66
|
+
async_uring: [:scheduler, "Async uring"],
|
|
67
|
+
async_uring_x2: [:scheduler_x, "Async uring x2"],
|
|
68
|
+
|
|
69
|
+
# async_epoll: [:scheduler, "Async epoll"],
|
|
70
|
+
# async_epoll_x2: [:scheduler_x, "Async epoll x2"],
|
|
71
|
+
|
|
72
|
+
um_fs: [:scheduler, "UM FS"],
|
|
73
|
+
um_fs_x2: [:scheduler_x, "UM FS x2"],
|
|
74
|
+
|
|
75
|
+
um: [:um, "UM"],
|
|
76
|
+
um_sidecar: [:um, "UM sidecar"],
|
|
77
|
+
# um_sqpoll: [:um, "UM sqpoll"],
|
|
78
|
+
um_x2: [:um_x, "UM x2"],
|
|
79
|
+
um_x4: [:um_x, "UM x4"],
|
|
80
|
+
um_x8: [:um_x, "UM x8"],
|
|
65
81
|
}
|
|
66
82
|
|
|
67
83
|
def run_benchmarks(b)
|
|
84
|
+
STDOUT.sync = true
|
|
68
85
|
@@benchmarks.each do |sym, (doer, name)|
|
|
69
|
-
|
|
86
|
+
if respond_to?(:"do_#{doer}")
|
|
87
|
+
STDOUT << "Running #{name}... "
|
|
88
|
+
ts = nil
|
|
89
|
+
b.report(name) {
|
|
90
|
+
ts = measure_time { send(:"run_#{sym}") }
|
|
91
|
+
}
|
|
92
|
+
p ts
|
|
93
|
+
cleanup
|
|
94
|
+
end
|
|
70
95
|
end
|
|
71
96
|
end
|
|
72
97
|
|
|
98
|
+
def cleanup
|
|
99
|
+
end
|
|
100
|
+
|
|
73
101
|
def run_baseline
|
|
74
102
|
do_baseline
|
|
75
103
|
end
|
|
76
104
|
|
|
105
|
+
def run_baseline_um
|
|
106
|
+
machine = UM.new(4096)
|
|
107
|
+
do_baseline_um(machine)
|
|
108
|
+
end
|
|
109
|
+
|
|
77
110
|
def run_threads
|
|
78
111
|
threads = []
|
|
79
112
|
ios = []
|
|
@@ -117,27 +150,140 @@ class UMBenchmark
|
|
|
117
150
|
ios.each { it.close rescue nil }
|
|
118
151
|
end
|
|
119
152
|
|
|
153
|
+
def run_async_uring_x2
|
|
154
|
+
threads = 2.times.map do
|
|
155
|
+
Thread.new do
|
|
156
|
+
selector ||= IO::Event::Selector::URing.new(Fiber.current)
|
|
157
|
+
worker_pool = Async::Scheduler::WorkerPool.new
|
|
158
|
+
scheduler = Async::Scheduler.new(selector:, worker_pool:)
|
|
159
|
+
Fiber.set_scheduler(scheduler)
|
|
160
|
+
ios = []
|
|
161
|
+
scheduler.run { do_scheduler_x(2, scheduler, ios) }
|
|
162
|
+
ios.each { it.close rescue nil }
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
threads.each(&:join)
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def run_async_epoll_x2
|
|
169
|
+
threads = 2.times.map do
|
|
170
|
+
Thread.new do
|
|
171
|
+
selector ||= IO::Event::Selector::EPoll.new(Fiber.current)
|
|
172
|
+
scheduler = Async::Scheduler.new(selector:)
|
|
173
|
+
Fiber.set_scheduler(scheduler)
|
|
174
|
+
ios = []
|
|
175
|
+
scheduler.run { do_scheduler_x(2, scheduler, ios) }
|
|
176
|
+
ios.each { it.close rescue nil }
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
threads.each(&:join)
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
def run_um_fs_x2
|
|
183
|
+
threads = 2.times.map do
|
|
184
|
+
Thread.new do
|
|
185
|
+
machine = UM.new
|
|
186
|
+
thread_pool = UM::BlockingOperationThreadPool.new(2)
|
|
187
|
+
scheduler = UM::FiberScheduler.new(machine, thread_pool)
|
|
188
|
+
Fiber.set_scheduler(scheduler)
|
|
189
|
+
ios = []
|
|
190
|
+
do_scheduler_x(2, scheduler, ios)
|
|
191
|
+
scheduler.join
|
|
192
|
+
ios.each { it.close rescue nil }
|
|
193
|
+
end
|
|
194
|
+
end
|
|
195
|
+
threads.each(&:join)
|
|
196
|
+
end
|
|
197
|
+
|
|
120
198
|
def run_um
|
|
121
|
-
machine = UM.new
|
|
199
|
+
machine = UM.new
|
|
200
|
+
fibers = []
|
|
201
|
+
fds = []
|
|
202
|
+
do_um(machine, fibers, fds)
|
|
203
|
+
machine.await_fibers(fibers)
|
|
204
|
+
fds.each { machine.close(it) }
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
def run_um_sidecar
|
|
208
|
+
machine = UM.new(sidecar: true)
|
|
122
209
|
fibers = []
|
|
123
210
|
fds = []
|
|
124
211
|
do_um(machine, fibers, fds)
|
|
125
212
|
machine.await_fibers(fibers)
|
|
126
|
-
puts "UM:"
|
|
127
|
-
p machine.metrics
|
|
128
213
|
fds.each { machine.close(it) }
|
|
129
214
|
end
|
|
130
215
|
|
|
131
216
|
def run_um_sqpoll
|
|
132
|
-
machine = UM.new(
|
|
217
|
+
machine = UM.new(sqpoll: true)
|
|
133
218
|
fibers = []
|
|
134
219
|
fds = []
|
|
135
220
|
do_um(machine, fibers, fds)
|
|
136
221
|
machine.await_fibers(fibers)
|
|
137
|
-
fds.each { machine.
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
222
|
+
fds.each { machine.close(it) }
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
def run_um_x2
|
|
226
|
+
threads = 2.times.map do
|
|
227
|
+
Thread.new do
|
|
228
|
+
machine = UM.new
|
|
229
|
+
fibers = []
|
|
230
|
+
fds = []
|
|
231
|
+
do_um_x(2, machine, fibers, fds)
|
|
232
|
+
machine.await_fibers(fibers)
|
|
233
|
+
fds.each { machine.close(it) }
|
|
234
|
+
end
|
|
235
|
+
end
|
|
236
|
+
threads.each(&:join)
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
def run_um_x4
|
|
240
|
+
threads = 4.times.map do
|
|
241
|
+
Thread.new do
|
|
242
|
+
machine = UM.new
|
|
243
|
+
fibers = []
|
|
244
|
+
fds = []
|
|
245
|
+
do_um_x(4, machine, fibers, fds)
|
|
246
|
+
machine.await_fibers(fibers)
|
|
247
|
+
fds.each { machine.close(it) }
|
|
248
|
+
end
|
|
249
|
+
end
|
|
250
|
+
threads.each(&:join)
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
def run_um_x8
|
|
254
|
+
threads = 8.times.map do
|
|
255
|
+
Thread.new do
|
|
256
|
+
machine = UM.new
|
|
257
|
+
fibers = []
|
|
258
|
+
fds = []
|
|
259
|
+
do_um_x(8, machine, fibers, fds)
|
|
260
|
+
machine.await_fibers(fibers)
|
|
261
|
+
fds.each { machine.close(it) }
|
|
262
|
+
end
|
|
263
|
+
end
|
|
264
|
+
threads.each(&:join)
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
def measure_time
|
|
268
|
+
GVLTools::GlobalTimer.enable
|
|
269
|
+
t0s = [
|
|
270
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC),
|
|
271
|
+
Process.clock_gettime(Process::CLOCK_PROCESS_CPUTIME_ID),
|
|
272
|
+
GVLTools::GlobalTimer.monotonic_time / 1_000_000_000.0
|
|
273
|
+
]
|
|
274
|
+
yield
|
|
275
|
+
t1s = [
|
|
276
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC),
|
|
277
|
+
Process.clock_gettime(Process::CLOCK_PROCESS_CPUTIME_ID),
|
|
278
|
+
GVLTools::GlobalTimer.monotonic_time / 1_000_000_000.0
|
|
279
|
+
]
|
|
280
|
+
{
|
|
281
|
+
monotonic: t1s[0] - t0s[0],
|
|
282
|
+
cpu: t1s[1] - t0s[1],
|
|
283
|
+
gvl: t1s[2] - t0s[2]
|
|
284
|
+
}
|
|
285
|
+
ensure
|
|
286
|
+
GVLTools::GlobalTimer.disable
|
|
141
287
|
end
|
|
142
288
|
end
|
|
143
289
|
|
data/benchmark/http_parse.rb
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
3
|
+
require 'bundler/inline'
|
|
4
|
+
|
|
5
|
+
gemfile do
|
|
6
|
+
source 'https://rubygems.org'
|
|
7
|
+
gem 'uringmachine', path: '..'
|
|
8
|
+
gem 'benchmark'
|
|
9
|
+
gem 'benchmark-ips'
|
|
10
|
+
gem 'http_parser.rb'
|
|
11
|
+
end
|
|
11
12
|
|
|
12
|
-
require 'bundler/setup'
|
|
13
13
|
require 'uringmachine'
|
|
14
14
|
require 'benchmark/ips'
|
|
15
15
|
require 'http/parser'
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'bundler/inline'
|
|
4
|
+
|
|
5
|
+
gemfile do
|
|
6
|
+
source 'https://rubygems.org'
|
|
7
|
+
gem 'uringmachine', path: '..'
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
require 'uringmachine'
|
|
11
|
+
|
|
12
|
+
RE_REQUEST_LINE = /^([a-z]+)\s+([^\s]+)\s+(http\/[0-9\.]{1,3})/i
|
|
13
|
+
RE_HEADER_LINE = /^([a-z0-9\-]+)\:\s+(.+)/i
|
|
14
|
+
|
|
15
|
+
def stream_get_request_line(stream, buf)
|
|
16
|
+
line = stream.get_line(buf, 0)
|
|
17
|
+
m = line&.match(RE_REQUEST_LINE)
|
|
18
|
+
return nil if !m
|
|
19
|
+
|
|
20
|
+
{
|
|
21
|
+
'method' => m[1].downcase,
|
|
22
|
+
'path' => m[2],
|
|
23
|
+
'protocol' => m[3].downcase
|
|
24
|
+
}
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
class InvalidHeadersError < StandardError; end
|
|
28
|
+
|
|
29
|
+
def get_headers(stream, buf)
|
|
30
|
+
headers = stream_get_request_line(stream, buf)
|
|
31
|
+
return nil if !headers
|
|
32
|
+
|
|
33
|
+
while true
|
|
34
|
+
line = stream.get_line(buf, 0)
|
|
35
|
+
break if line.empty?
|
|
36
|
+
|
|
37
|
+
m = line.match(RE_HEADER_LINE)
|
|
38
|
+
raise InvalidHeadersError, "Invalid header" if !m
|
|
39
|
+
|
|
40
|
+
headers[m[1]] = m[2]
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
headers
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
BODY = "Hello, world!" * 1000
|
|
47
|
+
|
|
48
|
+
def send_response(machine, fd)
|
|
49
|
+
headers = "HTTP/1.1 200\r\nContent-Length: #{BODY.bytesize}\r\n\r\n"
|
|
50
|
+
machine.sendv(fd, headers, BODY)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def handle_connection(machine, fd)
|
|
54
|
+
stream = UM::Stream.new(machine, fd)
|
|
55
|
+
buf = String.new(capacity: 65536)
|
|
56
|
+
|
|
57
|
+
while true
|
|
58
|
+
headers = get_headers(stream, buf)
|
|
59
|
+
break if !headers
|
|
60
|
+
|
|
61
|
+
send_response(machine, fd)
|
|
62
|
+
end
|
|
63
|
+
rescue InvalidHeadersError, SystemCallError => e
|
|
64
|
+
# ignore
|
|
65
|
+
ensure
|
|
66
|
+
machine.close_async(fd)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
N = ENV['N']&.to_i || 1
|
|
70
|
+
PORT = ENV['PORT']&.to_i || 1234
|
|
71
|
+
|
|
72
|
+
accept_queue = UM::Queue.new
|
|
73
|
+
|
|
74
|
+
acceptor = Thread.new do
|
|
75
|
+
machine = UM.new
|
|
76
|
+
fd = machine.socket(UM::AF_INET, UM::SOCK_STREAM, 0, 0)
|
|
77
|
+
machine.setsockopt(fd, UM::SOL_SOCKET, UM::SO_REUSEADDR, true)
|
|
78
|
+
machine.setsockopt(fd, UM::SOL_SOCKET, UM::SO_REUSEPORT, true)
|
|
79
|
+
machine.bind(fd, '127.0.0.1', PORT)
|
|
80
|
+
machine.listen(fd, 128)
|
|
81
|
+
machine.accept_into_queue(fd, accept_queue)
|
|
82
|
+
rescue Exception => e
|
|
83
|
+
p e
|
|
84
|
+
p e.backtrace
|
|
85
|
+
exit!
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
workers = N.times.map do |idx|
|
|
89
|
+
Thread.new do
|
|
90
|
+
machine = UM.new
|
|
91
|
+
|
|
92
|
+
loop do
|
|
93
|
+
fd = machine.shift(accept_queue)
|
|
94
|
+
machine.spin { handle_connection(machine, fd) }
|
|
95
|
+
end
|
|
96
|
+
rescue Exception => e
|
|
97
|
+
p e
|
|
98
|
+
p e.backtrace
|
|
99
|
+
exit!
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
puts "Listening on localhost:#{PORT}, #{N} worker thread(s)"
|
|
104
|
+
acceptor.join
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'bundler/inline'
|
|
4
|
+
|
|
5
|
+
gemfile do
|
|
6
|
+
source 'https://rubygems.org'
|
|
7
|
+
gem 'uringmachine', path: '..'
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
require 'uringmachine'
|
|
11
|
+
|
|
12
|
+
RE_REQUEST_LINE = /^([a-z]+)\s+([^\s]+)\s+(http\/[0-9\.]{1,3})/i
|
|
13
|
+
RE_HEADER_LINE = /^([a-z0-9\-]+)\:\s+(.+)/i
|
|
14
|
+
|
|
15
|
+
def stream_get_request_line(stream, buf)
|
|
16
|
+
line = stream.get_line(buf, 0)
|
|
17
|
+
m = line&.match(RE_REQUEST_LINE)
|
|
18
|
+
return nil if !m
|
|
19
|
+
|
|
20
|
+
{
|
|
21
|
+
'method' => m[1].downcase,
|
|
22
|
+
'path' => m[2],
|
|
23
|
+
'protocol' => m[3].downcase
|
|
24
|
+
}
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
class InvalidHeadersError < StandardError; end
|
|
28
|
+
|
|
29
|
+
def get_headers(stream, buf)
|
|
30
|
+
headers = stream_get_request_line(stream, buf)
|
|
31
|
+
return nil if !headers
|
|
32
|
+
|
|
33
|
+
while true
|
|
34
|
+
line = stream.get_line(buf, 0)
|
|
35
|
+
break if line.empty?
|
|
36
|
+
|
|
37
|
+
m = line.match(RE_HEADER_LINE)
|
|
38
|
+
raise InvalidHeadersError, "Invalid header" if !m
|
|
39
|
+
|
|
40
|
+
headers[m[1]] = m[2]
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
headers
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
BODY = "Hello, world!" * 1000
|
|
47
|
+
|
|
48
|
+
def send_response(machine, fd)
|
|
49
|
+
headers = "HTTP/1.1 200\r\nContent-Length: #{BODY.bytesize}\r\n\r\n"
|
|
50
|
+
machine.sendv(fd, headers, BODY)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def handle_connection(machine, fd)
|
|
54
|
+
stream = UM::Stream.new(machine, fd)
|
|
55
|
+
buf = String.new(capacity: 65536)
|
|
56
|
+
|
|
57
|
+
while true
|
|
58
|
+
headers = get_headers(stream, buf)
|
|
59
|
+
break if !headers
|
|
60
|
+
|
|
61
|
+
send_response(machine, fd)
|
|
62
|
+
end
|
|
63
|
+
rescue InvalidHeadersError, SystemCallError => e
|
|
64
|
+
# ignore
|
|
65
|
+
ensure
|
|
66
|
+
machine.close_async(fd)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
N = ENV['N']&.to_i || 1
|
|
70
|
+
PORT = ENV['PORT']&.to_i || 1234
|
|
71
|
+
|
|
72
|
+
workers = N.times.map do |idx|
|
|
73
|
+
Thread.new do
|
|
74
|
+
machine = UM.new
|
|
75
|
+
|
|
76
|
+
listen_fd = machine.socket(UM::AF_INET, UM::SOCK_STREAM, 0, 0)
|
|
77
|
+
machine.setsockopt(listen_fd, UM::SOL_SOCKET, UM::SO_REUSEADDR, true)
|
|
78
|
+
machine.setsockopt(listen_fd, UM::SOL_SOCKET, UM::SO_REUSEPORT, true)
|
|
79
|
+
machine.bind(listen_fd, '127.0.0.1', PORT)
|
|
80
|
+
machine.listen(listen_fd, 128)
|
|
81
|
+
|
|
82
|
+
machine.accept_each(listen_fd) { |fd|
|
|
83
|
+
machine.spin { handle_connection(machine, fd) }
|
|
84
|
+
}
|
|
85
|
+
rescue Exception => e
|
|
86
|
+
p e
|
|
87
|
+
p e.backtrace
|
|
88
|
+
exit!
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
puts "Listening on localhost:#{PORT}, #{N} worker thread(s)"
|
|
93
|
+
workers.each(&:join)
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'bundler/inline'
|
|
4
|
+
|
|
5
|
+
gemfile do
|
|
6
|
+
source 'https://rubygems.org'
|
|
7
|
+
gem 'uringmachine', path: '..'
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
require 'uringmachine'
|
|
11
|
+
|
|
12
|
+
RE_REQUEST_LINE = /^([a-z]+)\s+([^\s]+)\s+(http\/[0-9\.]{1,3})/i
|
|
13
|
+
RE_HEADER_LINE = /^([a-z0-9\-]+)\:\s+(.+)/i
|
|
14
|
+
|
|
15
|
+
def stream_get_request_line(stream, buf)
|
|
16
|
+
line = stream.get_line(buf, 0)
|
|
17
|
+
m = line&.match(RE_REQUEST_LINE)
|
|
18
|
+
return nil if !m
|
|
19
|
+
|
|
20
|
+
{
|
|
21
|
+
'method' => m[1].downcase,
|
|
22
|
+
'path' => m[2],
|
|
23
|
+
'protocol' => m[3].downcase
|
|
24
|
+
}
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
class InvalidHeadersError < StandardError; end
|
|
28
|
+
|
|
29
|
+
def get_headers(stream, buf)
|
|
30
|
+
headers = stream_get_request_line(stream, buf)
|
|
31
|
+
return nil if !headers
|
|
32
|
+
|
|
33
|
+
while true
|
|
34
|
+
line = stream.get_line(buf, 0)
|
|
35
|
+
break if line.empty?
|
|
36
|
+
|
|
37
|
+
m = line.match(RE_HEADER_LINE)
|
|
38
|
+
raise InvalidHeadersError, "Invalid header" if !m
|
|
39
|
+
|
|
40
|
+
headers[m[1]] = m[2]
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
headers
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
BODY = "Hello, world!" * 1000
|
|
47
|
+
Ractor.make_shareable(BODY)
|
|
48
|
+
|
|
49
|
+
def send_response(machine, fd)
|
|
50
|
+
headers = "HTTP/1.1 200\r\nContent-Length: #{BODY.bytesize}\r\n\r\n"
|
|
51
|
+
machine.sendv(fd, headers, BODY)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def handle_connection(machine, fd)
|
|
55
|
+
machine.setsockopt(fd, UM::IPPROTO_TCP, UM::TCP_NODELAY, true)
|
|
56
|
+
stream = UM::Stream.new(machine, fd)
|
|
57
|
+
buf = String.new(capacity: 65536)
|
|
58
|
+
|
|
59
|
+
while true
|
|
60
|
+
headers = get_headers(stream, buf)
|
|
61
|
+
break if !headers
|
|
62
|
+
|
|
63
|
+
send_response(machine, fd)
|
|
64
|
+
end
|
|
65
|
+
# rescue InvalidHeadersError, SystemCallError => e
|
|
66
|
+
# ignore
|
|
67
|
+
rescue Exception => e
|
|
68
|
+
p e: e
|
|
69
|
+
p bt: e.backtrace
|
|
70
|
+
exit!
|
|
71
|
+
ensure
|
|
72
|
+
machine.close_async(fd)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
N = ENV['N']&.to_i || 1
|
|
76
|
+
PORT = ENV['PORT']&.to_i || 1234
|
|
77
|
+
|
|
78
|
+
workers = N.times.map do |idx|
|
|
79
|
+
Ractor.new do
|
|
80
|
+
machine = UM.new
|
|
81
|
+
|
|
82
|
+
listen_fd = machine.socket(UM::AF_INET, UM::SOCK_STREAM, 0, 0)
|
|
83
|
+
machine.setsockopt(listen_fd, UM::SOL_SOCKET, UM::SO_REUSEADDR, true)
|
|
84
|
+
machine.setsockopt(listen_fd, UM::SOL_SOCKET, UM::SO_REUSEPORT, true)
|
|
85
|
+
machine.bind(listen_fd, '127.0.0.1', PORT)
|
|
86
|
+
machine.listen(listen_fd, 128)
|
|
87
|
+
|
|
88
|
+
machine.accept_each(listen_fd) { |fd|
|
|
89
|
+
machine.spin { handle_connection(machine, fd) }
|
|
90
|
+
}
|
|
91
|
+
rescue Exception => e
|
|
92
|
+
p e
|
|
93
|
+
p e.backtrace
|
|
94
|
+
exit!
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
puts "Listening on localhost:#{PORT}, #{N} worker ractor(s)"
|
|
99
|
+
workers.each(&:join)
|