itsi-server 0.1.1 → 0.1.18

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 (184) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +5 -0
  3. data/CODE_OF_CONDUCT.md +7 -0
  4. data/Cargo.lock +3937 -0
  5. data/Cargo.toml +7 -0
  6. data/README.md +4 -0
  7. data/Rakefile +8 -1
  8. data/_index.md +6 -0
  9. data/exe/itsi +141 -46
  10. data/ext/itsi_error/Cargo.toml +3 -0
  11. data/ext/itsi_error/src/lib.rs +98 -24
  12. data/ext/itsi_error/target/debug/build/clang-sys-da71b0344e568175/out/common.rs +355 -0
  13. data/ext/itsi_error/target/debug/build/clang-sys-da71b0344e568175/out/dynamic.rs +276 -0
  14. data/ext/itsi_error/target/debug/build/clang-sys-da71b0344e568175/out/macros.rs +49 -0
  15. data/ext/itsi_error/target/debug/build/rb-sys-49f554618693db24/out/bindings-0.9.110-mri-arm64-darwin23-3.4.2.rs +8865 -0
  16. data/ext/itsi_error/target/debug/incremental/itsi_error-1mmt5sux7jb0i/s-h510z7m8v9-0bxu7yd.lock +0 -0
  17. data/ext/itsi_error/target/debug/incremental/itsi_error-2vn3jey74oiw0/s-h5113n0e7e-1v5qzs6.lock +0 -0
  18. data/ext/itsi_error/target/debug/incremental/itsi_error-37uv9dicz7awp/s-h510ykifhe-0tbnep2.lock +0 -0
  19. data/ext/itsi_error/target/debug/incremental/itsi_error-37uv9dicz7awp/s-h510yyocpj-0tz7ug7.lock +0 -0
  20. data/ext/itsi_error/target/debug/incremental/itsi_error-37uv9dicz7awp/s-h510z0xc8g-14ol18k.lock +0 -0
  21. data/ext/itsi_error/target/debug/incremental/itsi_error-3g5qf4y7d54uj/s-h5113n0e7d-1trk8on.lock +0 -0
  22. data/ext/itsi_error/target/debug/incremental/itsi_error-3lpfftm45d3e2/s-h510z7m8r3-1pxp20o.lock +0 -0
  23. data/ext/itsi_error/target/debug/incremental/itsi_error-3o4qownhl3d7n/s-h510ykifek-1uxasnk.lock +0 -0
  24. data/ext/itsi_error/target/debug/incremental/itsi_error-3o4qownhl3d7n/s-h510yyocki-11u37qm.lock +0 -0
  25. data/ext/itsi_error/target/debug/incremental/itsi_error-3o4qownhl3d7n/s-h510z0xc93-0pmy0zm.lock +0 -0
  26. data/ext/itsi_instrument_entry/Cargo.toml +15 -0
  27. data/ext/itsi_instrument_entry/src/lib.rs +31 -0
  28. data/ext/itsi_rb_helpers/Cargo.toml +3 -0
  29. data/ext/itsi_rb_helpers/src/heap_value.rs +139 -0
  30. data/ext/itsi_rb_helpers/src/lib.rs +140 -10
  31. data/ext/itsi_rb_helpers/target/debug/build/clang-sys-da71b0344e568175/out/common.rs +355 -0
  32. data/ext/itsi_rb_helpers/target/debug/build/clang-sys-da71b0344e568175/out/dynamic.rs +276 -0
  33. data/ext/itsi_rb_helpers/target/debug/build/clang-sys-da71b0344e568175/out/macros.rs +49 -0
  34. data/ext/itsi_rb_helpers/target/debug/build/rb-sys-eb9ed4ff3a60f995/out/bindings-0.9.110-mri-arm64-darwin23-3.4.2.rs +8865 -0
  35. data/ext/itsi_rb_helpers/target/debug/incremental/itsi_rb_helpers-040pxg6yhb3g3/s-h5113n7a1b-03bwlt4.lock +0 -0
  36. data/ext/itsi_rb_helpers/target/debug/incremental/itsi_rb_helpers-131g1u4dzkt1a/s-h51113xnh3-1eik1ip.lock +0 -0
  37. data/ext/itsi_rb_helpers/target/debug/incremental/itsi_rb_helpers-131g1u4dzkt1a/s-h5111704jj-0g4rj8x.lock +0 -0
  38. data/ext/itsi_rb_helpers/target/debug/incremental/itsi_rb_helpers-1q2d3drtxrzs5/s-h5113n79yl-0bxcqc5.lock +0 -0
  39. data/ext/itsi_rb_helpers/target/debug/incremental/itsi_rb_helpers-374a9h7ovycj0/s-h51113xoox-10de2hp.lock +0 -0
  40. data/ext/itsi_rb_helpers/target/debug/incremental/itsi_rb_helpers-374a9h7ovycj0/s-h5111704w7-0vdq7gq.lock +0 -0
  41. data/ext/itsi_scheduler/Cargo.toml +24 -0
  42. data/ext/itsi_scheduler/src/itsi_scheduler/io_helpers.rs +56 -0
  43. data/ext/itsi_scheduler/src/itsi_scheduler/io_waiter.rs +44 -0
  44. data/ext/itsi_scheduler/src/itsi_scheduler/timer.rs +44 -0
  45. data/ext/itsi_scheduler/src/itsi_scheduler.rs +308 -0
  46. data/ext/itsi_scheduler/src/lib.rs +38 -0
  47. data/ext/itsi_server/Cargo.lock +2956 -0
  48. data/ext/itsi_server/Cargo.toml +72 -14
  49. data/ext/itsi_server/extconf.rb +1 -1
  50. data/ext/itsi_server/src/default_responses/html/401.html +68 -0
  51. data/ext/itsi_server/src/default_responses/html/403.html +68 -0
  52. data/ext/itsi_server/src/default_responses/html/404.html +68 -0
  53. data/ext/itsi_server/src/default_responses/html/413.html +71 -0
  54. data/ext/itsi_server/src/default_responses/html/429.html +68 -0
  55. data/ext/itsi_server/src/default_responses/html/500.html +71 -0
  56. data/ext/itsi_server/src/default_responses/html/502.html +71 -0
  57. data/ext/itsi_server/src/default_responses/html/503.html +68 -0
  58. data/ext/itsi_server/src/default_responses/html/504.html +69 -0
  59. data/ext/itsi_server/src/default_responses/html/index.html +238 -0
  60. data/ext/itsi_server/src/default_responses/json/401.json +6 -0
  61. data/ext/itsi_server/src/default_responses/json/403.json +6 -0
  62. data/ext/itsi_server/src/default_responses/json/404.json +6 -0
  63. data/ext/itsi_server/src/default_responses/json/413.json +6 -0
  64. data/ext/itsi_server/src/default_responses/json/429.json +6 -0
  65. data/ext/itsi_server/src/default_responses/json/500.json +6 -0
  66. data/ext/itsi_server/src/default_responses/json/502.json +6 -0
  67. data/ext/itsi_server/src/default_responses/json/503.json +6 -0
  68. data/ext/itsi_server/src/default_responses/json/504.json +6 -0
  69. data/ext/itsi_server/src/default_responses/mod.rs +11 -0
  70. data/ext/itsi_server/src/env.rs +43 -0
  71. data/ext/itsi_server/src/lib.rs +132 -40
  72. data/ext/itsi_server/src/prelude.rs +2 -0
  73. data/ext/itsi_server/src/ruby_types/itsi_body_proxy/big_bytes.rs +109 -0
  74. data/ext/itsi_server/src/ruby_types/itsi_body_proxy/mod.rs +143 -0
  75. data/ext/itsi_server/src/ruby_types/itsi_grpc_call.rs +344 -0
  76. data/ext/itsi_server/src/ruby_types/itsi_grpc_response_stream/mod.rs +264 -0
  77. data/ext/itsi_server/src/ruby_types/itsi_http_request.rs +345 -0
  78. data/ext/itsi_server/src/ruby_types/itsi_http_response.rs +391 -0
  79. data/ext/itsi_server/src/ruby_types/itsi_server/file_watcher.rs +225 -0
  80. data/ext/itsi_server/src/ruby_types/itsi_server/itsi_server_config.rs +375 -0
  81. data/ext/itsi_server/src/ruby_types/itsi_server.rs +83 -0
  82. data/ext/itsi_server/src/ruby_types/mod.rs +48 -0
  83. data/ext/itsi_server/src/server/binds/bind.rs +201 -0
  84. data/ext/itsi_server/src/server/binds/bind_protocol.rs +37 -0
  85. data/ext/itsi_server/src/server/binds/listener.rs +432 -0
  86. data/ext/itsi_server/src/server/binds/mod.rs +4 -0
  87. data/ext/itsi_server/src/server/binds/tls/locked_dir_cache.rs +132 -0
  88. data/ext/itsi_server/src/server/binds/tls.rs +270 -0
  89. data/ext/itsi_server/src/server/byte_frame.rs +32 -0
  90. data/ext/itsi_server/src/server/http_message_types.rs +97 -0
  91. data/ext/itsi_server/src/server/io_stream.rs +105 -0
  92. data/ext/itsi_server/src/server/lifecycle_event.rs +12 -0
  93. data/ext/itsi_server/src/server/middleware_stack/middleware.rs +165 -0
  94. data/ext/itsi_server/src/server/middleware_stack/middlewares/allow_list.rs +56 -0
  95. data/ext/itsi_server/src/server/middleware_stack/middlewares/auth_api_key.rs +87 -0
  96. data/ext/itsi_server/src/server/middleware_stack/middlewares/auth_basic.rs +86 -0
  97. data/ext/itsi_server/src/server/middleware_stack/middlewares/auth_jwt.rs +285 -0
  98. data/ext/itsi_server/src/server/middleware_stack/middlewares/cache_control.rs +142 -0
  99. data/ext/itsi_server/src/server/middleware_stack/middlewares/compression.rs +289 -0
  100. data/ext/itsi_server/src/server/middleware_stack/middlewares/cors.rs +292 -0
  101. data/ext/itsi_server/src/server/middleware_stack/middlewares/deny_list.rs +55 -0
  102. data/ext/itsi_server/src/server/middleware_stack/middlewares/error_response/default_responses.rs +190 -0
  103. data/ext/itsi_server/src/server/middleware_stack/middlewares/error_response.rs +157 -0
  104. data/ext/itsi_server/src/server/middleware_stack/middlewares/etag.rs +195 -0
  105. data/ext/itsi_server/src/server/middleware_stack/middlewares/header_interpretation.rs +82 -0
  106. data/ext/itsi_server/src/server/middleware_stack/middlewares/intrusion_protection.rs +201 -0
  107. data/ext/itsi_server/src/server/middleware_stack/middlewares/log_requests.rs +82 -0
  108. data/ext/itsi_server/src/server/middleware_stack/middlewares/max_body.rs +47 -0
  109. data/ext/itsi_server/src/server/middleware_stack/middlewares/mod.rs +87 -0
  110. data/ext/itsi_server/src/server/middleware_stack/middlewares/proxy.rs +414 -0
  111. data/ext/itsi_server/src/server/middleware_stack/middlewares/rate_limit.rs +131 -0
  112. data/ext/itsi_server/src/server/middleware_stack/middlewares/redirect.rs +76 -0
  113. data/ext/itsi_server/src/server/middleware_stack/middlewares/request_headers.rs +44 -0
  114. data/ext/itsi_server/src/server/middleware_stack/middlewares/response_headers.rs +36 -0
  115. data/ext/itsi_server/src/server/middleware_stack/middlewares/ruby_app.rs +126 -0
  116. data/ext/itsi_server/src/server/middleware_stack/middlewares/static_assets.rs +180 -0
  117. data/ext/itsi_server/src/server/middleware_stack/middlewares/static_response.rs +55 -0
  118. data/ext/itsi_server/src/server/middleware_stack/middlewares/string_rewrite.rs +163 -0
  119. data/ext/itsi_server/src/server/middleware_stack/middlewares/token_source.rs +12 -0
  120. data/ext/itsi_server/src/server/middleware_stack/mod.rs +347 -0
  121. data/ext/itsi_server/src/server/mod.rs +12 -5
  122. data/ext/itsi_server/src/server/process_worker.rs +247 -0
  123. data/ext/itsi_server/src/server/request_job.rs +11 -0
  124. data/ext/itsi_server/src/server/serve_strategy/cluster_mode.rs +342 -0
  125. data/ext/itsi_server/src/server/serve_strategy/mod.rs +30 -0
  126. data/ext/itsi_server/src/server/serve_strategy/single_mode.rs +421 -0
  127. data/ext/itsi_server/src/server/signal.rs +76 -0
  128. data/ext/itsi_server/src/server/size_limited_incoming.rs +101 -0
  129. data/ext/itsi_server/src/server/thread_worker.rs +475 -0
  130. data/ext/itsi_server/src/services/cache_store.rs +74 -0
  131. data/ext/itsi_server/src/services/itsi_http_service.rs +239 -0
  132. data/ext/itsi_server/src/services/mime_types.rs +1416 -0
  133. data/ext/itsi_server/src/services/mod.rs +6 -0
  134. data/ext/itsi_server/src/services/password_hasher.rs +83 -0
  135. data/ext/itsi_server/src/services/rate_limiter.rs +569 -0
  136. data/ext/itsi_server/src/services/static_file_server.rs +1324 -0
  137. data/ext/itsi_tracing/Cargo.toml +5 -0
  138. data/ext/itsi_tracing/src/lib.rs +315 -7
  139. data/ext/itsi_tracing/target/debug/incremental/itsi_tracing-0994n8rpvvt9m/s-h510hfz1f6-1kbycmq.lock +0 -0
  140. data/ext/itsi_tracing/target/debug/incremental/itsi_tracing-0bob7bf4yq34i/s-h5113125h5-0lh4rag.lock +0 -0
  141. data/ext/itsi_tracing/target/debug/incremental/itsi_tracing-2fcodulrxbbxo/s-h510h2infk-0hp5kjw.lock +0 -0
  142. data/ext/itsi_tracing/target/debug/incremental/itsi_tracing-2iak63r1woi1l/s-h510h2in4q-0kxfzw1.lock +0 -0
  143. data/ext/itsi_tracing/target/debug/incremental/itsi_tracing-2kk4qj9gn5dg2/s-h5113124kv-0enwon2.lock +0 -0
  144. data/ext/itsi_tracing/target/debug/incremental/itsi_tracing-2mwo0yas7dtw4/s-h510hfz1ha-1udgpei.lock +0 -0
  145. data/lib/itsi/http_request/response_status_shortcodes.rb +74 -0
  146. data/lib/itsi/http_request.rb +186 -0
  147. data/lib/itsi/http_response.rb +41 -0
  148. data/lib/itsi/passfile.rb +109 -0
  149. data/lib/itsi/server/config/dsl.rb +565 -0
  150. data/lib/itsi/server/config.rb +166 -0
  151. data/lib/itsi/server/default_app/default_app.rb +34 -0
  152. data/lib/itsi/server/default_app/index.html +115 -0
  153. data/lib/itsi/server/default_config/Itsi-rackup.rb +119 -0
  154. data/lib/itsi/server/default_config/Itsi.rb +107 -0
  155. data/lib/itsi/server/grpc/grpc_call.rb +246 -0
  156. data/lib/itsi/server/grpc/grpc_interface.rb +100 -0
  157. data/lib/itsi/server/grpc/reflection/v1/reflection_pb.rb +26 -0
  158. data/lib/itsi/server/grpc/reflection/v1/reflection_services_pb.rb +122 -0
  159. data/lib/itsi/server/rack/handler/itsi.rb +27 -0
  160. data/lib/itsi/server/rack_interface.rb +94 -0
  161. data/lib/itsi/server/route_tester.rb +107 -0
  162. data/lib/itsi/server/scheduler_interface.rb +21 -0
  163. data/lib/itsi/server/scheduler_mode.rb +10 -0
  164. data/lib/itsi/server/signal_trap.rb +29 -0
  165. data/lib/itsi/server/typed_handlers/param_parser.rb +200 -0
  166. data/lib/itsi/server/typed_handlers/source_parser.rb +55 -0
  167. data/lib/itsi/server/typed_handlers.rb +17 -0
  168. data/lib/itsi/server/version.rb +1 -1
  169. data/lib/itsi/server.rb +160 -9
  170. data/lib/itsi/standard_headers.rb +86 -0
  171. data/lib/ruby_lsp/itsi/addon.rb +111 -0
  172. data/lib/shell_completions/completions.rb +26 -0
  173. metadata +182 -25
  174. data/ext/itsi_server/src/request/itsi_request.rs +0 -143
  175. data/ext/itsi_server/src/request/mod.rs +0 -1
  176. data/ext/itsi_server/src/server/bind.rs +0 -138
  177. data/ext/itsi_server/src/server/itsi_ca/itsi_ca.crt +0 -32
  178. data/ext/itsi_server/src/server/itsi_ca/itsi_ca.key +0 -52
  179. data/ext/itsi_server/src/server/itsi_server.rs +0 -182
  180. data/ext/itsi_server/src/server/listener.rs +0 -218
  181. data/ext/itsi_server/src/server/tls.rs +0 -138
  182. data/ext/itsi_server/src/server/transfer_protocol.rs +0 -23
  183. data/ext/itsi_server/src/stream_writer/mod.rs +0 -21
  184. data/lib/itsi/request.rb +0 -39
@@ -0,0 +1,15 @@
1
+ [package]
2
+ name = "itsi_instrument_entry"
3
+ version = "0.1.0"
4
+ edition = "2021"
5
+ authors = ["Wouter Coppieters <wc@pico.net.nz>"]
6
+ license = "MIT"
7
+ publish = false
8
+
9
+
10
+ [lib]
11
+ proc-macro = true
12
+ [dependencies]
13
+ proc-macro2 = "1.0"
14
+ quote = "1.0"
15
+ syn = { version = "1.0", features = ["full"] }
@@ -0,0 +1,31 @@
1
+ use proc_macro::TokenStream;
2
+ use proc_macro2::TokenStream as TokenStream2;
3
+ use quote::quote;
4
+ use syn::{parse_macro_input, ItemFn};
5
+
6
+ #[proc_macro_attribute]
7
+ pub fn instrument_with_entry(attr: TokenStream, item: TokenStream) -> TokenStream {
8
+ let attr_tokens = TokenStream2::from(attr);
9
+ let input_fn = parse_macro_input!(item as ItemFn);
10
+ let attrs = input_fn.attrs;
11
+ let vis = input_fn.vis;
12
+ let sig = input_fn.sig;
13
+ let block = input_fn.block;
14
+ let output = quote! {
15
+ #[cfg(debug_assertions)]
16
+ #[tracing::instrument(#attr_tokens)]
17
+ #(#attrs)*
18
+ #vis #sig {
19
+ tracing::trace!("");
20
+ #block
21
+ }
22
+
23
+ #[cfg(not(debug_assertions))]
24
+ #(#attrs)*
25
+ #vis #sig {
26
+ #block
27
+ }
28
+ };
29
+
30
+ output.into()
31
+ }
@@ -4,5 +4,8 @@ version = "0.1.0"
4
4
  edition = "2024"
5
5
 
6
6
  [dependencies]
7
+ cfg-if = "1.0.0"
7
8
  magnus = { version = "0.7.1", features = ["rb-sys", "bytes"] }
9
+ nix = "0.29.0"
8
10
  rb-sys = "0.9.105"
11
+ serde = "1.0.219"
@@ -0,0 +1,139 @@
1
+ use magnus::IntoValue;
2
+ use magnus::rb_sys::AsRawValue;
3
+ use magnus::value::BoxValue;
4
+ use magnus::{Ruby, Value, value::ReprValue};
5
+ use std::fmt::{self, Debug, Formatter};
6
+ use std::ops::Deref;
7
+
8
+ /// HeapVal is a wrapper for heap-allocated magnus ReprVa;ies
9
+ /// that is marked as thread-safe(Send and Sync)
10
+ /// It's up to the user to actually ensure this though,
11
+ /// typically by only interacting with the value from a thread which
12
+ /// holds the GVL.
13
+ pub struct HeapValue<T>(pub BoxValue<T>)
14
+ where
15
+ T: ReprValue;
16
+
17
+ impl<T> PartialEq for HeapValue<T>
18
+ where
19
+ T: ReprValue,
20
+ {
21
+ fn eq(&self, other: &Self) -> bool {
22
+ self.0.as_raw() == other.0.as_raw()
23
+ }
24
+ }
25
+
26
+ impl<T> HeapValue<T>
27
+ where
28
+ T: ReprValue,
29
+ {
30
+ pub fn into_inner(self) -> T {
31
+ *self.0
32
+ }
33
+ }
34
+
35
+ impl<T> HeapValue<T>
36
+ where
37
+ T: ReprValue,
38
+ {
39
+ pub fn cloned(&self) -> T {
40
+ *self.0
41
+ }
42
+ }
43
+
44
+ impl<T> Deref for HeapValue<T>
45
+ where
46
+ T: ReprValue,
47
+ {
48
+ type Target = T;
49
+
50
+ fn deref(&self) -> &Self::Target {
51
+ &self.0
52
+ }
53
+ }
54
+
55
+ impl<T> HeapValue<T>
56
+ where
57
+ T: ReprValue,
58
+ {
59
+ pub fn inner(self) -> T {
60
+ *self.0
61
+ }
62
+ }
63
+
64
+ impl<T> IntoValue for HeapValue<T>
65
+ where
66
+ T: ReprValue,
67
+ {
68
+ fn into_value_with(self, _: &Ruby) -> Value {
69
+ self.0.into_value()
70
+ }
71
+ }
72
+
73
+ impl<T> From<T> for HeapValue<T>
74
+ where
75
+ T: ReprValue,
76
+ {
77
+ fn from(value: T) -> Self {
78
+ HeapValue(BoxValue::new(value))
79
+ }
80
+ }
81
+
82
+ impl<T> Clone for HeapValue<T>
83
+ where
84
+ T: ReprValue + Clone,
85
+ {
86
+ fn clone(&self) -> Self {
87
+ HeapValue(BoxValue::new(*self.0.deref()))
88
+ }
89
+ }
90
+
91
+ impl<T> Debug for HeapValue<T>
92
+ where
93
+ T: ReprValue + Debug,
94
+ {
95
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
96
+ write!(f, "{:?}", self.0)
97
+ }
98
+ }
99
+
100
+ unsafe impl<T> Send for HeapValue<T> where T: ReprValue {}
101
+ unsafe impl<T> Sync for HeapValue<T> where T: ReprValue {}
102
+
103
+ /// HeapVal is a wrapper for heap-allocated magnus Values
104
+ /// that is marked as thread-safe(Send and Sync)
105
+ /// It's up to the user to actually ensure this though,
106
+ /// typically by only interacting with the value from a thread which
107
+ /// holds the GVL.
108
+ pub struct HeapVal(HeapValue<Value>);
109
+ impl Deref for HeapVal {
110
+ type Target = Value;
111
+
112
+ fn deref(&self) -> &Self::Target {
113
+ &self.0
114
+ }
115
+ }
116
+
117
+ impl IntoValue for HeapVal {
118
+ fn into_value_with(self, _: &Ruby) -> Value {
119
+ self.0.into_value()
120
+ }
121
+ }
122
+
123
+ impl From<Value> for HeapVal {
124
+ fn from(value: Value) -> Self {
125
+ HeapVal(HeapValue(BoxValue::new(value)))
126
+ }
127
+ }
128
+
129
+ impl Clone for HeapVal {
130
+ fn clone(&self) -> Self {
131
+ HeapVal(HeapValue(BoxValue::new(*self.0.deref())))
132
+ }
133
+ }
134
+
135
+ impl Debug for HeapVal {
136
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
137
+ write!(f, "{:?}", self.0)
138
+ }
139
+ }
@@ -1,23 +1,45 @@
1
- use std::{os::raw::c_void, ptr::null_mut};
1
+ use std::{ffi::c_int, os::raw::c_void, ptr::null_mut};
2
2
 
3
+ use magnus::{
4
+ ArgList, RArray, Ruby, Thread, Value,
5
+ block::Proc,
6
+ rb_sys::{AsRawId, FromRawValue, protect},
7
+ value::{IntoId, LazyId, ReprValue},
8
+ };
3
9
  use rb_sys::{
4
- rb_thread_call_with_gvl, rb_thread_call_without_gvl, rb_thread_create, rb_thread_wakeup,
10
+ VALUE, rb_funcallv, rb_thread_call_with_gvl, rb_thread_call_without_gvl, rb_thread_create,
11
+ rb_thread_schedule, rb_thread_wakeup,
5
12
  };
6
13
 
7
- pub fn create_ruby_thread<F>(f: F)
14
+ mod heap_value;
15
+ pub use heap_value::{HeapVal, HeapValue};
16
+ static ID_FORK: LazyId = LazyId::new("fork");
17
+ static ID_LIST: LazyId = LazyId::new("list");
18
+ static ID_EQ: LazyId = LazyId::new("==");
19
+ static ID_ALIVE: LazyId = LazyId::new("alive?");
20
+ static ID_THREAD_VARIABLE_GET: LazyId = LazyId::new("thread_variable_get");
21
+ static ID_BACKTRACE: LazyId = LazyId::new("backtrace");
22
+
23
+ pub fn schedule_thread() {
24
+ unsafe {
25
+ rb_thread_schedule();
26
+ };
27
+ }
28
+ pub fn create_ruby_thread<F>(f: F) -> Thread
8
29
  where
9
- F: FnOnce() -> u64 + Send + 'static,
30
+ F: FnOnce() + Send + 'static,
10
31
  {
11
32
  extern "C" fn trampoline<F>(ptr: *mut c_void) -> u64
12
33
  where
13
- F: FnOnce() -> u64,
34
+ F: FnOnce(),
14
35
  {
15
36
  // Reconstruct the boxed Option<F> that holds our closure.
16
37
  let boxed_closure: Box<Option<F>> = unsafe { Box::from_raw(ptr as *mut Option<F>) };
17
38
  // Extract the closure. (The Option should be Some; panic otherwise.)
18
39
  let closure = (*boxed_closure).expect("Closure already taken");
19
40
  // Call the closure and return its result.
20
- closure()
41
+ closure();
42
+ 0
21
43
  }
22
44
 
23
45
  // Box the closure (wrapped in an Option) to create a stable pointer.
@@ -26,7 +48,10 @@ where
26
48
 
27
49
  // Call rb_thread_create with our trampoline and boxed closure.
28
50
  unsafe {
29
- rb_thread_wakeup(rb_thread_create(Some(trampoline::<F>), ptr));
51
+ let thread = rb_thread_create(Some(trampoline::<F>), ptr);
52
+ rb_thread_wakeup(thread);
53
+ rb_thread_schedule();
54
+ Thread::from_value(Value::from_raw(thread)).unwrap()
30
55
  }
31
56
  }
32
57
 
@@ -67,18 +92,18 @@ where
67
92
 
68
93
  pub fn call_with_gvl<F, R>(f: F) -> R
69
94
  where
70
- F: FnOnce() -> R,
95
+ F: FnOnce(Ruby) -> R,
71
96
  {
72
97
  extern "C" fn trampoline<F, R>(arg: *mut c_void) -> *mut c_void
73
98
  where
74
- F: FnOnce() -> R,
99
+ F: FnOnce(Ruby) -> R,
75
100
  {
76
101
  // 1) Reconstruct the Box that holds our closure
77
102
  let closure_ptr = arg as *mut Option<F>;
78
103
  let closure = unsafe { (*closure_ptr).take().expect("Closure already taken") };
79
104
 
80
105
  // 2) Call the user’s closure
81
- let result = closure();
106
+ let result = closure(Ruby::get().unwrap());
82
107
 
83
108
  // 3) Box up the result so we can return a pointer to it
84
109
  let boxed_result = Box::new(result);
@@ -96,3 +121,108 @@ where
96
121
  let result_box = unsafe { Box::from_raw(raw_result_ptr as *mut R) };
97
122
  *result_box
98
123
  }
124
+
125
+ pub fn fork(after_fork: Option<HeapValue<Proc>>) -> Option<i32> {
126
+ let ruby = Ruby::get().unwrap();
127
+ let fork_result = ruby
128
+ .module_kernel()
129
+ .funcall::<_, _, Option<i32>>(*ID_FORK, ())
130
+ .unwrap();
131
+ if fork_result.is_none() {
132
+ if let Some(proc) = after_fork {
133
+ call_proc_and_log_errors(proc)
134
+ }
135
+ }
136
+ fork_result
137
+ }
138
+
139
+ pub fn call_proc_and_log_errors(proc: HeapValue<Proc>) {
140
+ if let Err(e) = proc.call::<_, Value>(()) {
141
+ if let Some(value) = e.value() {
142
+ print_rb_backtrace(value);
143
+ } else {
144
+ eprintln!("Error occurred {:?}", e);
145
+ }
146
+ }
147
+ }
148
+
149
+ pub fn kill_threads<T>(threads: Vec<T>)
150
+ where
151
+ T: ReprValue,
152
+ {
153
+ for thr in &threads {
154
+ let alive: bool = thr
155
+ .funcall(*ID_ALIVE, ())
156
+ .expect("Failed to check if thread is alive");
157
+ if !alive {
158
+ eprintln!("Thread killed");
159
+ break;
160
+ }
161
+ eprintln!("Killing thread {:?}", thr.as_value());
162
+ thr.funcall::<_, _, Value>("terminate", ())
163
+ .expect("Failed to kill thread");
164
+ }
165
+ }
166
+
167
+ pub fn terminate_non_fork_safe_threads() {
168
+ let ruby = Ruby::get().unwrap();
169
+ let thread_class = ruby.class_thread();
170
+ let current: Thread = ruby.thread_current();
171
+ let threads: RArray = thread_class
172
+ .funcall(*ID_LIST, ())
173
+ .expect("Failed to list Ruby threads");
174
+
175
+ let non_fork_safe_threads = threads
176
+ .into_iter()
177
+ .filter_map(|v| {
178
+ let v_thread = Thread::from_value(v).unwrap();
179
+ let non_fork_safe = !v_thread
180
+ .funcall::<_, _, bool>(*ID_EQ, (current,))
181
+ .unwrap_or(false)
182
+ && !v_thread
183
+ .funcall::<_, _, bool>(*ID_THREAD_VARIABLE_GET, (ruby.sym_new("fork_safe"),))
184
+ .unwrap_or(false);
185
+ if non_fork_safe { Some(v_thread) } else { None }
186
+ })
187
+ .collect::<Vec<_>>();
188
+
189
+ kill_threads(non_fork_safe_threads);
190
+ }
191
+
192
+ pub fn print_rb_backtrace(rb_err: Value) {
193
+ let backtrace = rb_err
194
+ .funcall::<_, _, Vec<String>>(*ID_BACKTRACE, ())
195
+ .unwrap_or_default();
196
+ let rust_backtrace = std::backtrace::Backtrace::capture().to_string();
197
+ eprintln!("Ruby exception {:?}", rb_err);
198
+ for line in backtrace {
199
+ eprintln!("{}", line);
200
+ }
201
+ for line in rust_backtrace.lines() {
202
+ eprintln!("{}", line);
203
+ }
204
+ }
205
+
206
+ pub fn funcall_no_ret<T, M, A>(target: T, method: M, args: A) -> magnus::error::Result<()>
207
+ where
208
+ T: ReprValue,
209
+ M: IntoId,
210
+ A: ArgList,
211
+ {
212
+ protect(|| {
213
+ let handle = Ruby::get().unwrap();
214
+ let method = method.into_id_with(&handle);
215
+ let args = args.into_arg_list_with(&handle);
216
+ let slice = args.as_ref();
217
+ unsafe {
218
+ rb_funcallv(
219
+ target.as_rb_value(),
220
+ method.as_raw(),
221
+ slice.len() as c_int,
222
+ slice.as_ptr() as *const VALUE,
223
+ );
224
+ }
225
+ 0
226
+ })?;
227
+ Ok(())
228
+ }