itsi-scheduler 0.1.5 → 0.1.11

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.

Potentially problematic release.


This version of itsi-scheduler might be problematic. Click here for more details.

@@ -2,24 +2,26 @@ use super::{
2
2
  bind::Bind,
3
3
  listener::Listener,
4
4
  serve_strategy::{cluster_mode::ClusterMode, single_mode::SingleMode},
5
- signal::{clear_signal_handlers, reset_signal_handlers, SIGNAL_HANDLER_CHANNEL},
5
+ signal::{
6
+ clear_signal_handlers, reset_signal_handlers, send_shutdown_event, SIGNAL_HANDLER_CHANNEL,
7
+ },
6
8
  };
7
9
  use crate::{request::itsi_request::ItsiRequest, server::serve_strategy::ServeStrategy};
8
10
  use derive_more::Debug;
9
- use itsi_rb_helpers::call_without_gvl;
10
- use itsi_tracing::error;
11
+ use itsi_rb_helpers::{call_without_gvl, HeapVal, HeapValue};
12
+ use itsi_tracing::{error, run_silently};
11
13
  use magnus::{
12
14
  block::Proc,
13
15
  error::Result,
14
- scan_args::{get_kwargs, scan_args, Args, KwArgs},
15
- value::{InnerValue, Opaque, ReprValue},
16
- RHash, Ruby, Symbol, Value,
16
+ scan_args::{get_kwargs, scan_args, Args, KwArgs, ScanArgsKw, ScanArgsOpt, ScanArgsRequired},
17
+ value::ReprValue,
18
+ ArgList, RArray, RHash, Ruby, Symbol, Value,
17
19
  };
18
- use parking_lot::Mutex;
19
- use std::{cmp::max, ops::Deref, sync::Arc};
20
+ use parking_lot::{Mutex, RwLock};
21
+ use std::{cmp::max, collections::HashMap, ops::Deref, sync::Arc};
20
22
  use tracing::{info, instrument};
21
23
 
22
- static DEFAULT_BIND: &str = "localhost:3000";
24
+ static DEFAULT_BIND: &str = "http://localhost:3000";
23
25
 
24
26
  #[magnus::wrap(class = "Itsi::Server", free_immediately, size)]
25
27
  #[derive(Clone)]
@@ -34,12 +36,11 @@ impl Deref for Server {
34
36
  &self.config
35
37
  }
36
38
  }
37
- type AfterFork = Mutex<Arc<Option<Box<dyn Fn() + Send + Sync>>>>;
38
39
 
39
40
  #[derive(Debug)]
40
41
  pub struct ServerConfig {
41
42
  #[debug(skip)]
42
- pub app: Opaque<Value>,
43
+ pub app: HeapVal,
43
44
  #[allow(unused)]
44
45
  pub workers: u8,
45
46
  #[allow(unused)]
@@ -49,12 +50,14 @@ pub struct ServerConfig {
49
50
  pub script_name: String,
50
51
  pub(crate) binds: Mutex<Vec<Bind>>,
51
52
  #[debug(skip)]
52
- pub before_fork: Mutex<Option<Box<dyn FnOnce() + Send + Sync>>>,
53
- #[debug(skip)]
54
- pub after_fork: AfterFork,
53
+ pub hooks: HashMap<String, HeapValue<Proc>>,
55
54
  pub scheduler_class: Option<String>,
56
55
  pub stream_body: Option<bool>,
57
56
  pub worker_memory_limit: Option<u64>,
57
+ #[debug(skip)]
58
+ pub(crate) strategy: RwLock<Option<ServeStrategy>>,
59
+ pub silence: bool,
60
+ pub oob_gc_responses_threshold: Option<u64>,
58
61
  }
59
62
 
60
63
  #[derive(Debug)]
@@ -63,6 +66,30 @@ pub enum RequestJob {
63
66
  Shutdown,
64
67
  }
65
68
 
69
+ fn extract_args<Req, Opt, Splat>(
70
+ scan_args: &Args<(), (), (), (), RHash, ()>,
71
+ primaries: &[&str],
72
+ rest: &[&str],
73
+ ) -> Result<KwArgs<Req, Opt, Splat>>
74
+ where
75
+ Req: ScanArgsRequired,
76
+ Opt: ScanArgsOpt,
77
+ Splat: ScanArgsKw,
78
+ {
79
+ let symbols: Vec<Symbol> = primaries
80
+ .iter()
81
+ .chain(rest.iter())
82
+ .map(|&name| Symbol::new(name))
83
+ .collect();
84
+
85
+ let hash = scan_args
86
+ .keywords
87
+ .funcall::<_, _, RHash>("slice", symbols.into_arg_list_with(&Ruby::get().unwrap()))
88
+ .unwrap();
89
+
90
+ get_kwargs(hash, primaries, rest)
91
+ }
92
+
66
93
  impl Server {
67
94
  #[instrument(
68
95
  name = "Itsi",
@@ -73,39 +100,44 @@ impl Server {
73
100
  pub fn new(args: &[Value]) -> Result<Self> {
74
101
  let scan_args: Args<(), (), (), (), RHash, ()> = scan_args(args)?;
75
102
 
76
- type ArgSet1 = (
77
- Option<u8>,
78
- Option<u8>,
79
- Option<f64>,
80
- Option<String>,
81
- Option<Vec<String>>,
82
- Option<Proc>,
83
- Option<Proc>,
84
- Option<String>,
85
- Option<bool>,
86
- );
87
-
88
- type ArgSet2 = (Option<u64>,);
89
-
90
- let args1: KwArgs<(Value,), ArgSet1, ()> = get_kwargs(
91
- scan_args
92
- .keywords
93
- .funcall::<_, _, RHash>(
94
- "slice",
95
- (
96
- Symbol::new("app"),
97
- Symbol::new("workers"),
98
- Symbol::new("threads"),
99
- Symbol::new("shutdown_timeout"),
100
- Symbol::new("script_name"),
101
- Symbol::new("binds"),
102
- Symbol::new("before_fork"),
103
- Symbol::new("after_fork"),
104
- Symbol::new("scheduler_class"),
105
- Symbol::new("stream_body"),
106
- ),
107
- )
108
- .unwrap(),
103
+ type Args1 = KwArgs<
104
+ (Value,),
105
+ (
106
+ // Workers
107
+ Option<u8>,
108
+ // Threads
109
+ Option<u8>,
110
+ // Shutdown Timeout
111
+ Option<f64>,
112
+ // Script Name
113
+ Option<String>,
114
+ // Binds
115
+ Option<Vec<String>>,
116
+ // Stream Body
117
+ Option<bool>,
118
+ ),
119
+ (),
120
+ >;
121
+
122
+ type Args2 = KwArgs<
123
+ (),
124
+ (
125
+ // Hooks
126
+ Option<RHash>,
127
+ // Scheduler Class
128
+ Option<String>,
129
+ // Worker Memory Limit
130
+ Option<u64>,
131
+ // Out-of-band GC Responses Threshold
132
+ Option<u64>,
133
+ // Silence
134
+ Option<bool>,
135
+ ),
136
+ (),
137
+ >;
138
+
139
+ let args1: Args1 = extract_args(
140
+ &scan_args,
109
141
  &["app"],
110
142
  &[
111
143
  "workers",
@@ -113,24 +145,43 @@ impl Server {
113
145
  "shutdown_timeout",
114
146
  "script_name",
115
147
  "binds",
116
- "before_fork",
117
- "after_fork",
118
- "scheduler_class",
119
148
  "stream_body",
120
149
  ],
121
150
  )?;
122
151
 
123
- let args2: KwArgs<(), ArgSet2, ()> = get_kwargs(
124
- scan_args
125
- .keywords
126
- .funcall::<_, _, RHash>("slice", (Symbol::new("worker_memory_limit"),))
127
- .unwrap(),
152
+ let args2: Args2 = extract_args(
153
+ &scan_args,
128
154
  &[],
129
- &["worker_memory_limit"],
155
+ &[
156
+ "hooks",
157
+ "scheduler_class",
158
+ "worker_memory_limit",
159
+ "oob_gc_responses_threshold",
160
+ "silence",
161
+ ],
130
162
  )?;
131
163
 
164
+ let hooks = args2
165
+ .optional
166
+ .0
167
+ .map(|rhash| -> Result<HashMap<String, HeapValue<Proc>>> {
168
+ let mut hook_map: HashMap<String, HeapValue<Proc>> = HashMap::new();
169
+ for pair in rhash.enumeratorize::<_, ()>("each", ()) {
170
+ if let Some(pair_value) = RArray::from_value(pair?) {
171
+ if let (Ok(key), Ok(value)) =
172
+ (pair_value.entry::<Value>(0), pair_value.entry::<Proc>(1))
173
+ {
174
+ hook_map.insert(key.to_string(), HeapValue::from(value));
175
+ }
176
+ }
177
+ }
178
+ Ok(hook_map)
179
+ })
180
+ .transpose()?
181
+ .unwrap_or_default();
182
+
132
183
  let config = ServerConfig {
133
- app: Opaque::from(args1.required.0),
184
+ app: HeapVal::from(args1.required.0),
134
185
  workers: max(args1.optional.0.unwrap_or(1), 1),
135
186
  threads: max(args1.optional.1.unwrap_or(1), 1),
136
187
  shutdown_timeout: args1.optional.2.unwrap_or(5.0),
@@ -144,33 +195,21 @@ impl Server {
144
195
  .map(|s| s.parse())
145
196
  .collect::<itsi_error::Result<Vec<Bind>>>()?,
146
197
  ),
147
- before_fork: Mutex::new(args1.optional.5.map(|p| {
148
- let opaque_proc = Opaque::from(p);
149
- Box::new(move || {
150
- opaque_proc
151
- .get_inner_with(&Ruby::get().unwrap())
152
- .call::<_, Value>(())
153
- .unwrap();
154
- }) as Box<dyn FnOnce() + Send + Sync>
155
- })),
156
- after_fork: Mutex::new(Arc::new(args1.optional.6.map(|p| {
157
- let opaque_proc = Opaque::from(p);
158
- Box::new(move || {
159
- opaque_proc
160
- .get_inner_with(&Ruby::get().unwrap())
161
- .call::<_, Value>(())
162
- .unwrap();
163
- }) as Box<dyn Fn() + Send + Sync>
164
- }))),
165
- scheduler_class: args1.optional.7.clone(),
166
- stream_body: args1.optional.8,
167
- worker_memory_limit: args2.optional.0,
198
+ stream_body: args1.optional.5,
199
+ hooks,
200
+ scheduler_class: args2.optional.1.clone(),
201
+ worker_memory_limit: args2.optional.2,
202
+ strategy: RwLock::new(None),
203
+ oob_gc_responses_threshold: args2.optional.3,
204
+ silence: args2.optional.4.is_some_and(|s| s),
168
205
  };
169
206
 
170
- if let Some(scheduler_class) = args1.optional.7 {
171
- info!(scheduler_class, fiber_scheduler = true);
172
- } else {
173
- info!(fiber_scheduler = false);
207
+ if !config.silence {
208
+ if let Some(scheduler_class) = args2.optional.1 {
209
+ info!(scheduler_class, fiber_scheduler = true);
210
+ } else {
211
+ info!(fiber_scheduler = false);
212
+ }
174
213
  }
175
214
 
176
215
  Ok(Server {
@@ -179,7 +218,7 @@ impl Server {
179
218
  }
180
219
 
181
220
  #[instrument(name = "Bind", skip_all, fields(binds=format!("{:?}", self.config.binds.lock())))]
182
- pub(crate) fn listeners(&self) -> Result<Arc<Vec<Arc<Listener>>>> {
221
+ pub(crate) fn build_listeners(&self) -> Result<Vec<Listener>> {
183
222
  let listeners = self
184
223
  .config
185
224
  .binds
@@ -189,17 +228,15 @@ impl Server {
189
228
  .map(Listener::try_from)
190
229
  .collect::<std::result::Result<Vec<Listener>, _>>()?
191
230
  .into_iter()
192
- .map(Arc::new)
193
231
  .collect::<Vec<_>>();
194
232
  info!("Bound {:?} listeners", listeners.len());
195
- Ok(Arc::new(listeners))
233
+ Ok(listeners)
196
234
  }
197
235
 
198
- pub(crate) fn build_strategy(
199
- self,
200
- listeners: Arc<Vec<Arc<Listener>>>,
201
- ) -> Result<ServeStrategy> {
236
+ pub(crate) fn build_strategy(self) -> Result<()> {
237
+ let listeners = self.build_listeners()?;
202
238
  let server = Arc::new(self);
239
+ let server_clone = server.clone();
203
240
 
204
241
  let strategy = if server.config.workers == 1 {
205
242
  ServeStrategy::Single(Arc::new(SingleMode::new(
@@ -214,31 +251,38 @@ impl Server {
214
251
  SIGNAL_HANDLER_CHANNEL.0.clone(),
215
252
  )))
216
253
  };
217
- Ok(strategy)
254
+
255
+ *server_clone.strategy.write() = Some(strategy);
256
+ Ok(())
257
+ }
258
+
259
+ pub fn stop(&self) -> Result<()> {
260
+ send_shutdown_event();
261
+ Ok(())
218
262
  }
219
263
 
220
264
  pub fn start(&self) -> Result<()> {
265
+ if self.silence {
266
+ run_silently(|| self.build_and_run_strategy())
267
+ } else {
268
+ self.build_and_run_strategy()
269
+ }
270
+ }
271
+
272
+ fn build_and_run_strategy(&self) -> Result<()> {
221
273
  reset_signal_handlers();
222
274
  let rself = self.clone();
223
- let listeners = self.listeners()?;
224
- let listeners_clone = listeners.clone();
225
275
  call_without_gvl(move || -> Result<()> {
226
- let strategy = rself.build_strategy(listeners_clone)?;
227
- if let Err(e) = strategy.run() {
276
+ rself.clone().build_strategy()?;
277
+ if let Err(e) = rself.strategy.read().as_ref().unwrap().run() {
228
278
  error!("Error running server: {}", e);
229
- strategy.stop()?;
279
+ rself.strategy.read().as_ref().unwrap().stop()?;
230
280
  }
231
- drop(strategy);
232
281
  Ok(())
233
282
  })?;
234
- if let Ok(listeners) = Arc::try_unwrap(listeners) {
235
- listeners.into_iter().for_each(|listener| {
236
- if let Ok(listener) = Arc::try_unwrap(listener) {
237
- listener.unbind()
238
- };
239
- });
240
- }
241
283
  clear_signal_handlers();
284
+ self.strategy.write().take();
285
+ info!("Server stopped");
242
286
  Ok(())
243
287
  }
244
288
  }
@@ -1,6 +1,7 @@
1
1
  use super::bind::{Bind, BindAddress};
2
2
  use super::bind_protocol::BindProtocol;
3
3
  use super::io_stream::IoStream;
4
+ use super::serve_strategy::single_mode::RunningPhase;
4
5
  use super::tls::ItsiTlsAcceptor;
5
6
  use itsi_error::{ItsiError, Result};
6
7
  use itsi_tracing::info;
@@ -11,6 +12,7 @@ use std::{os::unix::net::UnixListener, path::PathBuf};
11
12
  use tokio::net::TcpListener as TokioTcpListener;
12
13
  use tokio::net::UnixListener as TokioUnixListener;
13
14
  use tokio::net::{unix, TcpStream, UnixStream};
15
+ use tokio::sync::watch::Receiver;
14
16
  use tokio_rustls::TlsAcceptor;
15
17
  use tokio_stream::StreamExt;
16
18
  use tracing::error;
@@ -23,45 +25,79 @@ pub(crate) enum Listener {
23
25
  }
24
26
 
25
27
  pub(crate) enum TokioListener {
26
- Tcp {
27
- listener: TokioTcpListener,
28
- host: String,
29
- port: u16,
30
- },
31
- TcpTls {
32
- listener: TokioTcpListener,
33
- acceptor: ItsiTlsAcceptor,
34
- host: String,
35
- port: u16,
36
- },
37
- Unix {
38
- listener: TokioUnixListener,
39
- },
40
- UnixTls {
41
- listener: TokioUnixListener,
42
- acceptor: ItsiTlsAcceptor,
43
- },
28
+ Tcp(TokioTcpListener),
29
+ TcpTls(TokioTcpListener, ItsiTlsAcceptor),
30
+ Unix(TokioUnixListener),
31
+ UnixTls(TokioUnixListener, ItsiTlsAcceptor),
32
+ }
33
+
34
+ #[derive(Debug, Clone)]
35
+ pub struct ListenerInfo {
36
+ pub host: String,
37
+ pub port: u16,
38
+ pub scheme: String,
44
39
  }
45
40
 
46
41
  impl TokioListener {
47
- pub fn unbind(self) {
42
+ pub fn listener_info(&self) -> ListenerInfo {
48
43
  match self {
49
- TokioListener::Tcp { listener, .. } => drop(listener.into_std().unwrap()),
50
- TokioListener::TcpTls { listener, .. } => drop(listener.into_std().unwrap()),
51
- TokioListener::Unix { listener } => drop(listener.into_std().unwrap()),
52
- TokioListener::UnixTls { listener, .. } => drop(listener.into_std().unwrap()),
53
- };
44
+ TokioListener::Tcp(listener) => ListenerInfo {
45
+ host: listener
46
+ .local_addr()
47
+ .unwrap()
48
+ .ip()
49
+ .to_canonical()
50
+ .to_string(),
51
+ port: listener.local_addr().unwrap().port(),
52
+ scheme: "http".to_string(),
53
+ },
54
+ TokioListener::TcpTls(listener, _) => ListenerInfo {
55
+ host: listener
56
+ .local_addr()
57
+ .unwrap()
58
+ .ip()
59
+ .to_canonical()
60
+ .to_string(),
61
+ port: listener.local_addr().unwrap().port(),
62
+ scheme: "https".to_string(),
63
+ },
64
+ TokioListener::Unix(listener) => ListenerInfo {
65
+ host: listener
66
+ .local_addr()
67
+ .unwrap()
68
+ .as_pathname()
69
+ .unwrap()
70
+ .to_str()
71
+ .unwrap()
72
+ .to_owned(),
73
+ port: 0,
74
+ scheme: "unix".to_string(),
75
+ },
76
+ TokioListener::UnixTls(listener, _) => ListenerInfo {
77
+ host: listener
78
+ .local_addr()
79
+ .unwrap()
80
+ .as_pathname()
81
+ .unwrap()
82
+ .to_str()
83
+ .unwrap()
84
+ .to_owned(),
85
+ port: 0,
86
+ scheme: "ssl".to_string(),
87
+ },
88
+ }
54
89
  }
90
+
55
91
  pub(crate) async fn accept(&self) -> Result<IoStream> {
56
92
  match self {
57
- TokioListener::Tcp { listener, .. } => TokioListener::accept_tcp(listener).await,
58
- TokioListener::TcpTls {
59
- listener, acceptor, ..
60
- } => TokioListener::accept_tls(listener, acceptor).await,
61
- TokioListener::Unix { listener, .. } => TokioListener::accept_unix(listener).await,
62
- TokioListener::UnixTls {
63
- listener, acceptor, ..
64
- } => TokioListener::accept_unix_tls(listener, acceptor).await,
93
+ TokioListener::Tcp(listener) => TokioListener::accept_tcp(listener).await,
94
+ TokioListener::TcpTls(listener, acceptor) => {
95
+ TokioListener::accept_tls(listener, acceptor).await
96
+ }
97
+ TokioListener::Unix(listener) => TokioListener::accept_unix(listener).await,
98
+ TokioListener::UnixTls(listener, acceptor) => {
99
+ TokioListener::accept_unix_tls(listener, acceptor).await
100
+ }
65
101
  }
66
102
  }
67
103
 
@@ -70,17 +106,24 @@ impl TokioListener {
70
106
  Self::to_tokio_io(Stream::TcpStream(tcp_stream), None).await
71
107
  }
72
108
 
73
- pub async fn spawn_state_task(&self) {
74
- if let TokioListener::TcpTls {
75
- acceptor: ItsiTlsAcceptor::Automatic(_acme_acceptor, state, _server_config),
76
- ..
77
- } = self
109
+ pub async fn spawn_state_task(&self, mut shutdown_receiver: Receiver<RunningPhase>) {
110
+ if let TokioListener::TcpTls(
111
+ _,
112
+ ItsiTlsAcceptor::Automatic(_acme_acceptor, state, _server_config),
113
+ ) = self
78
114
  {
79
115
  let mut state = state.lock().await;
80
116
  loop {
81
- match StreamExt::next(&mut *state).await {
82
- Some(event) => info!("Received acme event: {:?}", event),
83
- None => error!("Received no acme event"),
117
+ tokio::select! {
118
+ stream_event = StreamExt::next(&mut *state) => {
119
+ match stream_event {
120
+ Some(event) => info!("ACME Event: {:?}", event),
121
+ None => error!("Received no acme event"),
122
+ }
123
+ },
124
+ _ = shutdown_receiver.changed() => {
125
+ break;
126
+ }
84
127
  }
85
128
  }
86
129
  }
@@ -175,33 +218,6 @@ impl TokioListener {
175
218
  },
176
219
  }
177
220
  }
178
-
179
- pub(crate) fn scheme(&self) -> String {
180
- match self {
181
- TokioListener::Tcp { .. } => "http".to_string(),
182
- TokioListener::TcpTls { .. } => "https".to_string(),
183
- TokioListener::Unix { .. } => "http".to_string(),
184
- TokioListener::UnixTls { .. } => "https".to_string(),
185
- }
186
- }
187
-
188
- pub(crate) fn port(&self) -> u16 {
189
- match self {
190
- TokioListener::Tcp { port, .. } => *port,
191
- TokioListener::TcpTls { port, .. } => *port,
192
- TokioListener::Unix { .. } => 0,
193
- TokioListener::UnixTls { .. } => 0,
194
- }
195
- }
196
-
197
- pub(crate) fn host(&self) -> String {
198
- match self {
199
- TokioListener::Tcp { host, .. } => host.to_string(),
200
- TokioListener::TcpTls { host, .. } => host.to_string(),
201
- TokioListener::Unix { .. } => "unix".to_string(),
202
- TokioListener::UnixTls { .. } => "unix".to_string(),
203
- }
204
- }
205
221
  }
206
222
 
207
223
  enum Stream {
@@ -227,48 +243,22 @@ impl std::fmt::Display for SockAddr {
227
243
  }
228
244
 
229
245
  impl Listener {
230
- pub fn unbind(self) {
246
+ pub fn into_tokio_listener(self) -> TokioListener {
231
247
  match self {
232
- Listener::Tcp(listener) => drop(listener),
233
- Listener::TcpTls((listener, _)) => drop(listener),
234
- Listener::Unix(listener) => drop(listener),
235
- Listener::UnixTls((listener, _)) => drop(listener),
236
- };
237
- }
238
- pub fn to_tokio_listener(&self) -> TokioListener {
239
- match self {
240
- Listener::Tcp(listener) => TokioListener::Tcp {
241
- listener: TokioTcpListener::from_std(TcpListener::try_clone(listener).unwrap())
242
- .unwrap(),
243
- host: listener
244
- .local_addr()
245
- .unwrap()
246
- .ip()
247
- .to_canonical()
248
- .to_string(),
249
- port: listener.local_addr().unwrap().port(),
250
- },
251
- Listener::TcpTls((listener, acceptor)) => TokioListener::TcpTls {
252
- listener: TokioTcpListener::from_std(TcpListener::try_clone(listener).unwrap())
253
- .unwrap(),
254
- acceptor: acceptor.clone(),
255
- host: listener
256
- .local_addr()
257
- .unwrap()
258
- .ip()
259
- .to_canonical()
260
- .to_string(),
261
- port: listener.local_addr().unwrap().port(),
262
- },
263
- Listener::Unix(listener) => TokioListener::Unix {
264
- listener: TokioUnixListener::from_std(UnixListener::try_clone(listener).unwrap())
265
- .unwrap(),
266
- },
267
- Listener::UnixTls((listener, acceptor)) => TokioListener::UnixTls {
268
- listener: TokioUnixListener::from_std(UnixListener::try_clone(listener).unwrap())
269
- .unwrap(),
270
- acceptor: acceptor.clone(),
271
- },
248
+ Listener::Tcp(listener) => {
249
+ TokioListener::Tcp(TokioTcpListener::from_std(listener).unwrap())
250
+ }
251
+ Listener::TcpTls((listener, acceptor)) => TokioListener::TcpTls(
252
+ TokioTcpListener::from_std(listener).unwrap(),
253
+ acceptor.clone(),
254
+ ),
255
+ Listener::Unix(listener) => {
256
+ TokioListener::Unix(TokioUnixListener::from_std(listener).unwrap())
257
+ }
258
+ Listener::UnixTls((listener, acceptor)) => TokioListener::UnixTls(
259
+ TokioUnixListener::from_std(listener).unwrap(),
260
+ acceptor.clone(),
261
+ ),
272
262
  }
273
263
  }
274
264
  }
@@ -307,6 +297,7 @@ fn connect_tcp_socket(addr: IpAddr, port: u16) -> Result<TcpListener> {
307
297
  socket.set_nonblocking(true).ok();
308
298
  socket.set_nodelay(true).ok();
309
299
  socket.set_recv_buffer_size(262_144).ok();
300
+ info!("Binding to {:?}", socket_address);
310
301
  socket.bind(&socket_address.into())?;
311
302
  socket.listen(1024)?;
312
303
  Ok(socket.into())
@@ -53,8 +53,8 @@ impl ProcessWorker {
53
53
  }
54
54
  *self.child_pid.lock() = None;
55
55
  }
56
-
57
- match call_with_gvl(|_ruby| fork(cluster_template.server.after_fork.lock().clone())) {
56
+ match call_with_gvl(|_ruby| fork(cluster_template.server.hooks.get("after_fork").cloned()))
57
+ {
58
58
  Some(pid) => {
59
59
  *self.child_pid.lock() = Some(Pid::from_raw(pid));
60
60
  }
@@ -67,7 +67,7 @@ impl ProcessWorker {
67
67
  }
68
68
  match SingleMode::new(
69
69
  cluster_template.server.clone(),
70
- cluster_template.listeners.clone(),
70
+ cluster_template.listeners.lock().drain(..).collect(),
71
71
  cluster_template.lifecycle_channel.clone(),
72
72
  ) {
73
73
  Ok(single_mode) => {
@@ -83,6 +83,13 @@ impl ProcessWorker {
83
83
  Ok(())
84
84
  }
85
85
 
86
+ pub fn pid(&self) -> i32 {
87
+ if let Some(pid) = *self.child_pid.lock() {
88
+ return pid.as_raw();
89
+ }
90
+ 0
91
+ }
92
+
86
93
  pub(crate) fn memory_usage(&self) -> Option<u64> {
87
94
  if let Some(pid) = *self.child_pid.lock() {
88
95
  let s = System::new_all();