omq-backend-rust 0.1.6 → 0.2.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.
@@ -0,0 +1,433 @@
1
+ use std::ffi::{CStr, CString, c_char, c_long, c_void};
2
+ use std::panic::{AssertUnwindSafe, catch_unwind};
3
+ use std::ptr;
4
+
5
+ use rb_sys::{VALUE, rb_data_type_t};
6
+
7
+ pub type RbResult<T = VALUE> = Result<T, RubyErr>;
8
+
9
+ #[derive(Debug)]
10
+ pub enum RubyErr {
11
+ Exception(VALUE),
12
+ Error { class: VALUE, message: String },
13
+ }
14
+
15
+ impl RubyErr {
16
+ pub fn new(class: VALUE, message: impl Into<String>) -> Self {
17
+ Self::Error {
18
+ class,
19
+ message: message.into(),
20
+ }
21
+ }
22
+
23
+ pub fn arg(message: impl Into<String>) -> Self {
24
+ Self::new(unsafe { rb_sys::rb_eArgError }, message)
25
+ }
26
+
27
+ pub fn io(message: impl Into<String>) -> Self {
28
+ Self::new(unsafe { rb_sys::rb_eIOError }, message)
29
+ }
30
+
31
+ pub fn runtime(message: impl Into<String>) -> Self {
32
+ Self::new(unsafe { rb_sys::rb_eRuntimeError }, message)
33
+ }
34
+
35
+ pub fn type_error(message: impl Into<String>) -> Self {
36
+ Self::new(unsafe { rb_sys::rb_eTypeError }, message)
37
+ }
38
+
39
+ fn current_exception() -> Self {
40
+ let err = unsafe { rb_sys::rb_errinfo() };
41
+ if err == qnil() {
42
+ Self::runtime("Ruby exception")
43
+ } else {
44
+ Self::Exception(err)
45
+ }
46
+ }
47
+ }
48
+
49
+ pub fn wrap<F>(f: F) -> VALUE
50
+ where
51
+ F: FnOnce() -> RbResult<VALUE>,
52
+ {
53
+ match catch_unwind(AssertUnwindSafe(f)) {
54
+ Ok(Ok(value)) => value,
55
+ Ok(Err(err)) => raise(err),
56
+ Err(_) => raise(RubyErr::runtime("native Rust panic")),
57
+ }
58
+ }
59
+
60
+ pub fn wrap_init<F>(f: F)
61
+ where
62
+ F: FnOnce() -> RbResult<()>,
63
+ {
64
+ match catch_unwind(AssertUnwindSafe(f)) {
65
+ Ok(Ok(())) => {}
66
+ Ok(Err(err)) => raise(err),
67
+ Err(_) => raise(RubyErr::runtime("native Rust panic")),
68
+ }
69
+ }
70
+
71
+ pub fn raise(err: RubyErr) -> ! {
72
+ match err {
73
+ RubyErr::Exception(exc) => unsafe { rb_sys::rb_exc_raise(exc) },
74
+ RubyErr::Error { class, message } => {
75
+ let message = message.replace('\0', "\\0");
76
+ let c_message =
77
+ CString::new(message).unwrap_or_else(|_| CString::new("Ruby error").unwrap());
78
+ let exc = unsafe { rb_sys::rb_exc_new_cstr(class, c_message.as_ptr()) };
79
+ unsafe { rb_sys::rb_exc_raise(exc) }
80
+ }
81
+ }
82
+ }
83
+
84
+ struct ProtectData<F> {
85
+ func: Option<F>,
86
+ panicked: bool,
87
+ }
88
+
89
+ pub fn protect_value<F>(func: F) -> RbResult<VALUE>
90
+ where
91
+ F: FnOnce() -> VALUE,
92
+ {
93
+ unsafe extern "C" fn call<F>(arg: VALUE) -> VALUE
94
+ where
95
+ F: FnOnce() -> VALUE,
96
+ {
97
+ let data = unsafe { &mut *(arg as *mut ProtectData<F>) };
98
+ let Some(func) = data.func.take() else {
99
+ data.panicked = true;
100
+ return qnil();
101
+ };
102
+
103
+ match catch_unwind(AssertUnwindSafe(func)) {
104
+ Ok(value) => value,
105
+ Err(_) => {
106
+ data.panicked = true;
107
+ qnil()
108
+ }
109
+ }
110
+ }
111
+
112
+ let mut data = ProtectData {
113
+ func: Some(func),
114
+ panicked: false,
115
+ };
116
+ let mut state = 0;
117
+ let value = unsafe {
118
+ rb_sys::rb_protect(
119
+ Some(call::<F>),
120
+ &mut data as *mut ProtectData<F> as VALUE,
121
+ &mut state,
122
+ )
123
+ };
124
+
125
+ if state != 0 {
126
+ Err(RubyErr::current_exception())
127
+ } else if data.panicked {
128
+ Err(RubyErr::runtime("native Rust panic"))
129
+ } else {
130
+ Ok(value)
131
+ }
132
+ }
133
+
134
+ pub fn protect_unit<F>(func: F) -> RbResult<()>
135
+ where
136
+ F: FnOnce(),
137
+ {
138
+ protect_value(|| {
139
+ func();
140
+ qnil()
141
+ })?;
142
+ Ok(())
143
+ }
144
+
145
+ pub const fn qnil() -> VALUE {
146
+ rb_sys::ruby_special_consts::RUBY_Qnil as VALUE
147
+ }
148
+
149
+ pub const fn qtrue() -> VALUE {
150
+ rb_sys::ruby_special_consts::RUBY_Qtrue as VALUE
151
+ }
152
+
153
+ pub const fn qfalse() -> VALUE {
154
+ rb_sys::ruby_special_consts::RUBY_Qfalse as VALUE
155
+ }
156
+
157
+ pub const fn qundef() -> VALUE {
158
+ rb_sys::ruby_special_consts::RUBY_Qundef as VALUE
159
+ }
160
+
161
+ pub fn bool_value(value: bool) -> VALUE {
162
+ if value { qtrue() } else { qfalse() }
163
+ }
164
+
165
+ pub fn check_hash(value: VALUE) -> RbResult<()> {
166
+ let is_hash = unsafe { rb_sys::rb_obj_is_kind_of(value, rb_sys::rb_cHash) };
167
+ if is_hash == qtrue() {
168
+ Ok(())
169
+ } else {
170
+ Err(RubyErr::type_error("expected Hash"))
171
+ }
172
+ }
173
+
174
+ pub fn check_array(value: VALUE) -> RbResult<()> {
175
+ let is_array = unsafe { rb_sys::rb_obj_is_kind_of(value, rb_sys::rb_cArray) };
176
+ if is_array == qtrue() {
177
+ Ok(())
178
+ } else {
179
+ Err(RubyErr::type_error("expected Array"))
180
+ }
181
+ }
182
+
183
+ pub fn hash_get(hash: VALUE, key: &str) -> RbResult<Option<VALUE>> {
184
+ check_hash(hash)?;
185
+ let key = new_utf8_string(key)?;
186
+ let value = protect_value(|| unsafe { rb_sys::rb_hash_lookup2(hash, key, qundef()) })?;
187
+ if value == qundef() {
188
+ Ok(None)
189
+ } else {
190
+ Ok(Some(value))
191
+ }
192
+ }
193
+
194
+ pub fn hash_new() -> RbResult<VALUE> {
195
+ protect_value(|| unsafe { rb_sys::rb_hash_new() })
196
+ }
197
+
198
+ pub fn hash_aset(hash: VALUE, key: VALUE, value: VALUE) -> RbResult<()> {
199
+ protect_value(|| unsafe { rb_sys::rb_hash_aset(hash, key, value) })?;
200
+ Ok(())
201
+ }
202
+
203
+ pub fn array_new() -> RbResult<VALUE> {
204
+ protect_value(|| unsafe { rb_sys::rb_ary_new() })
205
+ }
206
+
207
+ pub fn array_new_capa(capacity: usize) -> RbResult<VALUE> {
208
+ let capacity = c_long_len(capacity)?;
209
+ protect_value(|| unsafe { rb_sys::rb_ary_new_capa(capacity) })
210
+ }
211
+
212
+ pub fn array_len(array: VALUE) -> RbResult<usize> {
213
+ check_array(array)?;
214
+ let len = unsafe { rb_sys::RARRAY_LEN(array) };
215
+ if len < 0 {
216
+ Err(RubyErr::runtime("negative Array length"))
217
+ } else {
218
+ Ok(len as usize)
219
+ }
220
+ }
221
+
222
+ pub fn array_entry(array: VALUE, index: usize) -> RbResult<VALUE> {
223
+ check_array(array)?;
224
+ let index = c_long_len(index)?;
225
+ protect_value(|| unsafe { rb_sys::rb_ary_entry(array, index) })
226
+ }
227
+
228
+ pub fn array_push(array: VALUE, value: VALUE) -> RbResult<()> {
229
+ protect_value(|| unsafe { rb_sys::rb_ary_push(array, value) })?;
230
+ Ok(())
231
+ }
232
+
233
+ pub fn symbol(name: &str) -> RbResult<VALUE> {
234
+ let len = c_long_len(name.len())?;
235
+ protect_value(|| unsafe {
236
+ let id = rb_sys::rb_intern2(name.as_ptr() as *const c_char, len);
237
+ rb_sys::rb_id2sym(id)
238
+ })
239
+ }
240
+
241
+ pub fn new_binary_string(bytes: &[u8]) -> RbResult<VALUE> {
242
+ let len = c_long_len(bytes.len())?;
243
+ let ptr = if bytes.is_empty() {
244
+ ptr::null()
245
+ } else {
246
+ bytes.as_ptr() as *const c_char
247
+ };
248
+ let value = protect_value(|| unsafe { rb_sys::rb_str_new(ptr, len) })?;
249
+ protect_unit(|| unsafe { rb_sys::RB_OBJ_FREEZE(value) })?;
250
+ Ok(value)
251
+ }
252
+
253
+ pub fn new_utf8_string(text: &str) -> RbResult<VALUE> {
254
+ let len = c_long_len(text.len())?;
255
+ protect_value(|| unsafe { rb_sys::rb_utf8_str_new(text.as_ptr() as *const c_char, len) })
256
+ }
257
+
258
+ pub fn string_value(value: VALUE) -> RbResult<VALUE> {
259
+ protect_value(|| unsafe { rb_sys::rb_str_to_str(value) })
260
+ }
261
+
262
+ pub fn value_to_bytes(value: VALUE) -> RbResult<Vec<u8>> {
263
+ let string = string_value(value)?;
264
+ let len = unsafe { rb_sys::RSTRING_LEN(string) };
265
+ if len < 0 {
266
+ return Err(RubyErr::runtime("negative String length"));
267
+ }
268
+ if len == 0 {
269
+ return Ok(Vec::new());
270
+ }
271
+
272
+ let ptr = unsafe { rb_sys::RSTRING_PTR(string) };
273
+ if ptr.is_null() {
274
+ return Err(RubyErr::runtime("null String pointer"));
275
+ }
276
+
277
+ let bytes = unsafe { std::slice::from_raw_parts(ptr as *const u8, len as usize) };
278
+ Ok(bytes.to_vec())
279
+ }
280
+
281
+ pub fn value_to_string(value: VALUE) -> RbResult<String> {
282
+ let bytes = value_to_bytes(value)?;
283
+ String::from_utf8(bytes).map_err(|_| RubyErr::type_error("expected UTF-8 String"))
284
+ }
285
+
286
+ pub fn value_to_i64(value: VALUE) -> RbResult<i64> {
287
+ let mut out = 0i64;
288
+ protect_value(|| {
289
+ out = unsafe { rb_sys::rb_num2long(value) as i64 };
290
+ qnil()
291
+ })?;
292
+ Ok(out)
293
+ }
294
+
295
+ pub fn value_to_f64(value: VALUE) -> RbResult<f64> {
296
+ let mut out = 0.0f64;
297
+ protect_value(|| {
298
+ out = unsafe { rb_sys::rb_num2dbl(value) };
299
+ qnil()
300
+ })?;
301
+ Ok(out)
302
+ }
303
+
304
+ pub fn value_to_bool(value: VALUE) -> RbResult<bool> {
305
+ if value == qtrue() {
306
+ Ok(true)
307
+ } else if value == qfalse() {
308
+ Ok(false)
309
+ } else {
310
+ Err(RubyErr::type_error("expected true or false"))
311
+ }
312
+ }
313
+
314
+ pub fn int_value(value: i32) -> VALUE {
315
+ unsafe { rb_sys::rb_int2inum(value as isize) }
316
+ }
317
+
318
+ pub unsafe fn wrap_typed_data<T>(
319
+ class: VALUE,
320
+ value: Box<T>,
321
+ data_type: *const rb_data_type_t,
322
+ ) -> RbResult<VALUE> {
323
+ let raw = Box::into_raw(value);
324
+ match protect_value(|| unsafe {
325
+ rb_sys::rb_data_typed_object_wrap(class, raw as *mut c_void, data_type)
326
+ }) {
327
+ Ok(value) => Ok(value),
328
+ Err(err) => {
329
+ unsafe { drop(Box::from_raw(raw)) };
330
+ Err(err)
331
+ }
332
+ }
333
+ }
334
+
335
+ pub unsafe fn typed_data_ref<T>(
336
+ value: VALUE,
337
+ data_type: *const rb_data_type_t,
338
+ type_name: &str,
339
+ ) -> RbResult<&'static T> {
340
+ let mut ptr = std::ptr::null_mut();
341
+ protect_unit(|| unsafe {
342
+ ptr = rb_sys::rb_check_typeddata(value, data_type);
343
+ })
344
+ .map_err(|_| RubyErr::type_error(format!("expected {type_name}")))?;
345
+ if ptr.is_null() {
346
+ return Err(RubyErr::runtime(format!(
347
+ "{type_name} data pointer is null"
348
+ )));
349
+ }
350
+
351
+ Ok(unsafe { &*(ptr as *const T) })
352
+ }
353
+
354
+ pub unsafe fn define_module(name: &CStr) -> RbResult<VALUE> {
355
+ protect_value(|| unsafe { rb_sys::rb_define_module(name.as_ptr()) })
356
+ }
357
+
358
+ pub unsafe fn define_module_under(outer: VALUE, name: &CStr) -> RbResult<VALUE> {
359
+ protect_value(|| unsafe { rb_sys::rb_define_module_under(outer, name.as_ptr()) })
360
+ }
361
+
362
+ pub unsafe fn define_class_under(outer: VALUE, name: &CStr, superclass: VALUE) -> RbResult<VALUE> {
363
+ protect_value(|| unsafe { rb_sys::rb_define_class_under(outer, name.as_ptr(), superclass) })
364
+ }
365
+
366
+ pub unsafe fn undef_alloc_func(class: VALUE) -> RbResult<()> {
367
+ protect_unit(|| unsafe { rb_sys::rb_undef_alloc_func(class) })
368
+ }
369
+
370
+ pub unsafe fn define_module_function_1(
371
+ module: VALUE,
372
+ name: &CStr,
373
+ func: unsafe extern "C" fn(VALUE, VALUE) -> VALUE,
374
+ ) -> RbResult<()> {
375
+ protect_unit(|| unsafe {
376
+ rb_sys::rb_define_module_function(module, name.as_ptr(), Some(transmute_1(func)), 1)
377
+ })
378
+ }
379
+
380
+ pub unsafe fn define_singleton_method_1(
381
+ object: VALUE,
382
+ name: &CStr,
383
+ func: unsafe extern "C" fn(VALUE, VALUE) -> VALUE,
384
+ ) -> RbResult<()> {
385
+ protect_unit(|| unsafe {
386
+ rb_sys::rb_define_singleton_method(object, name.as_ptr(), Some(transmute_1(func)), 1)
387
+ })
388
+ }
389
+
390
+ pub unsafe fn define_method_0(
391
+ class: VALUE,
392
+ name: &CStr,
393
+ func: unsafe extern "C" fn(VALUE) -> VALUE,
394
+ ) -> RbResult<()> {
395
+ protect_unit(|| unsafe {
396
+ rb_sys::rb_define_method(class, name.as_ptr(), Some(transmute_0(func)), 0)
397
+ })
398
+ }
399
+
400
+ pub unsafe fn define_method_1(
401
+ class: VALUE,
402
+ name: &CStr,
403
+ func: unsafe extern "C" fn(VALUE, VALUE) -> VALUE,
404
+ ) -> RbResult<()> {
405
+ protect_unit(|| unsafe {
406
+ rb_sys::rb_define_method(class, name.as_ptr(), Some(transmute_1(func)), 1)
407
+ })
408
+ }
409
+
410
+ unsafe fn transmute_0(
411
+ func: unsafe extern "C" fn(VALUE) -> VALUE,
412
+ ) -> unsafe extern "C" fn() -> VALUE {
413
+ unsafe {
414
+ std::mem::transmute::<unsafe extern "C" fn(VALUE) -> VALUE, unsafe extern "C" fn() -> VALUE>(
415
+ func,
416
+ )
417
+ }
418
+ }
419
+
420
+ unsafe fn transmute_1(
421
+ func: unsafe extern "C" fn(VALUE, VALUE) -> VALUE,
422
+ ) -> unsafe extern "C" fn() -> VALUE {
423
+ unsafe {
424
+ std::mem::transmute::<
425
+ unsafe extern "C" fn(VALUE, VALUE) -> VALUE,
426
+ unsafe extern "C" fn() -> VALUE,
427
+ >(func)
428
+ }
429
+ }
430
+
431
+ fn c_long_len(len: usize) -> RbResult<c_long> {
432
+ c_long::try_from(len).map_err(|_| RubyErr::arg("length too large"))
433
+ }
@@ -27,10 +27,10 @@ pub fn ensure_runtime(io_threads: usize) -> Handle {
27
27
  }
28
28
  let mut guard = RUNTIME.lock().unwrap();
29
29
  let pid = std::process::id();
30
- if let Some(ref rt) = *guard {
31
- if rt.pid == pid {
32
- return rt.handle.clone();
33
- }
30
+ if let Some(ref rt) = *guard
31
+ && rt.pid == pid
32
+ {
33
+ return rt.handle.clone();
34
34
  }
35
35
  let (tx, rx) = flume::unbounded::<Job>();
36
36
  let (handle_tx, handle_rx) = flume::bounded::<Handle>(1);
@@ -69,28 +69,18 @@ pub fn ensure_runtime(io_threads: usize) -> Handle {
69
69
 
70
70
  fn submit_job(io_threads: usize) -> flume::Sender<Job> {
71
71
  let guard = RUNTIME.lock().unwrap();
72
- if let Some(ref rt) = *guard {
73
- if rt.pid == std::process::id() {
74
- return rt.submit.clone();
75
- }
72
+ if let Some(ref rt) = *guard
73
+ && rt.pid == std::process::id()
74
+ {
75
+ return rt.submit.clone();
76
76
  }
77
77
  drop(guard);
78
78
  ensure_runtime(io_threads);
79
79
  RUNTIME.lock().unwrap().as_ref().unwrap().submit.clone()
80
80
  }
81
81
 
82
- pub fn spawn_blocking<F, T>(io_threads: usize, fut: F) -> T
83
- where
84
- F: Future<Output = T> + Send + 'static,
85
- T: Send + 'static,
86
- {
87
- let handle = ensure_runtime(io_threads);
88
- let (otx, orx) = flume::bounded::<T>(1);
89
- handle.spawn(async move {
90
- let out = fut.await;
91
- let _ = otx.send(out);
92
- });
93
-
82
+ #[cfg(ruby_engine = "mri")]
83
+ fn recv_blocking<T>(rx: flume::Receiver<T>, missing: &'static str) -> T {
94
84
  struct RecvBox<U> {
95
85
  rx: flume::Receiver<U>,
96
86
  result: Option<U>,
@@ -102,10 +92,7 @@ where
102
92
  std::ptr::null_mut()
103
93
  }
104
94
 
105
- let mut rd = RecvBox {
106
- rx: orx,
107
- result: None,
108
- };
95
+ let mut rd = RecvBox { rx, result: None };
109
96
  unsafe {
110
97
  rb_sys::rb_thread_call_without_gvl(
111
98
  Some(blocking_recv::<T>),
@@ -114,7 +101,27 @@ where
114
101
  std::ptr::null_mut(),
115
102
  );
116
103
  }
117
- rd.result.expect("omq-backend-rust: runtime dropped result")
104
+ rd.result.expect(missing)
105
+ }
106
+
107
+ #[cfg(not(ruby_engine = "mri"))]
108
+ fn recv_blocking<T>(rx: flume::Receiver<T>, missing: &'static str) -> T {
109
+ rx.recv().expect(missing)
110
+ }
111
+
112
+ pub fn spawn_blocking<F, T>(io_threads: usize, fut: F) -> T
113
+ where
114
+ F: Future<Output = T> + Send + 'static,
115
+ T: Send + 'static,
116
+ {
117
+ let handle = ensure_runtime(io_threads);
118
+ let (otx, orx) = flume::bounded::<T>(1);
119
+ handle.spawn(async move {
120
+ let out = fut.await;
121
+ let _ = otx.send(out);
122
+ });
123
+
124
+ recv_blocking(orx, "omq-backend-rust: runtime dropped result")
118
125
  }
119
126
 
120
127
  pub struct Materialized {
@@ -171,10 +178,10 @@ fn convert_monitor_event(event: &omq_tokio::MonitorEvent) -> MonitorEventData {
171
178
  },
172
179
  HandshakeSucceeded { endpoint, peer } => {
173
180
  let mut detail = vec![("connection_id", peer.connection_id.to_string())];
174
- if let Some(ref ident) = peer.peer_identity {
175
- if !ident.is_empty() {
176
- detail.push(("identity", format!("{:?}", ident)));
177
- }
181
+ if let Some(ref ident) = peer.peer_identity
182
+ && !ident.is_empty()
183
+ {
184
+ detail.push(("identity", format!("{:?}", ident)));
178
185
  }
179
186
  MonitorEventData {
180
187
  event_type: "handshake_succeeded",
@@ -299,20 +306,15 @@ pub fn materialize(
299
306
  let job: Job = Box::new(move || {
300
307
  let sock = Arc::new(InnerSocket::new(socket_type, options));
301
308
 
302
- const SEND_YIELD_INTERVAL: u32 = 256;
303
309
  let s = sock.clone();
304
310
  let sn = send_notify.clone();
305
311
  let send_pump = tokio::spawn(async move {
306
- futures::pin_mut!(send_cons);
307
- let mut batch = 0u32;
312
+ let mut send_cons = send_cons;
308
313
  while let Some(msg) = futures::StreamExt::next(&mut send_cons).await {
309
- let _ = s.send(msg).await;
314
+ send_cons.release();
310
315
  sn.notify();
311
- batch += 1;
312
- if batch >= SEND_YIELD_INTERVAL {
313
- batch = 0;
314
- tokio::task::yield_now().await;
315
- }
316
+ let _ = s.send(msg).await;
317
+ tokio::task::yield_now().await;
316
318
  }
317
319
  sn.notify();
318
320
  });
@@ -343,6 +345,7 @@ pub fn materialize(
343
345
  });
344
346
 
345
347
  let monitor_sock = sock.clone();
348
+ let peer_ready_sock = sock.clone();
346
349
  let monitor_pump = tokio::spawn(async move {
347
350
  let mut stream = monitor_sock.monitor();
348
351
  let mut peer_count: u32 = 0;
@@ -359,6 +362,7 @@ pub fn materialize(
359
362
  had_peers = true;
360
363
  if !peer_connected_fired {
361
364
  peer_connected_fired = true;
365
+ let _ = peer_ready_sock.connections().await;
362
366
  peer_connected_notify.force_wake();
363
367
  }
364
368
  }
@@ -368,11 +372,12 @@ pub fn materialize(
368
372
  all_peers_gone_notify.force_wake();
369
373
  }
370
374
  }
371
- omq_tokio::MonitorEvent::SubscribeReceived { .. } => {
372
- if !subscriber_joined_fired {
373
- subscriber_joined_fired = true;
374
- subscriber_joined_notify.force_wake();
375
- }
375
+ omq_tokio::MonitorEvent::SubscribeReceived { .. }
376
+ | omq_tokio::MonitorEvent::JoinReceived { .. }
377
+ if !subscriber_joined_fired =>
378
+ {
379
+ subscriber_joined_fired = true;
380
+ subscriber_joined_notify.force_wake();
376
381
  }
377
382
  _ => {}
378
383
  }
@@ -392,64 +397,43 @@ pub fn materialize(
392
397
  });
393
398
  tx.send(job).expect("omq-backend-rust: tokio runtime gone");
394
399
 
395
- struct RecvBox {
396
- rx: flume::Receiver<(
397
- Arc<InnerSocket>,
398
- JoinHandle<()>,
399
- JoinHandle<()>,
400
- JoinHandle<()>,
401
- )>,
402
- result: Option<(
403
- Arc<InnerSocket>,
404
- JoinHandle<()>,
405
- JoinHandle<()>,
406
- JoinHandle<()>,
407
- )>,
408
- }
409
-
410
- extern "C" fn blocking_recv(data: *mut libc::c_void) -> *mut libc::c_void {
411
- let rd = unsafe { &mut *(data as *mut RecvBox) };
412
- rd.result = rd.rx.recv().ok();
413
- std::ptr::null_mut()
414
- }
415
-
416
- let mut rd = RecvBox {
417
- rx: orx,
418
- result: None,
419
- };
420
- unsafe {
421
- rb_sys::rb_thread_call_without_gvl(
422
- Some(blocking_recv),
423
- &mut rd as *mut RecvBox as *mut libc::c_void,
424
- None,
425
- std::ptr::null_mut(),
426
- );
427
- }
428
- rd.result.expect("omq-backend-rust: materialize failed")
400
+ recv_blocking(orx, "omq-backend-rust: materialize failed")
429
401
  }
430
402
 
431
403
  pub fn destroy_socket(
432
404
  io_threads: usize,
433
405
  sock: Arc<InnerSocket>,
434
406
  send_prod: Mutex<yring::AsyncProducer<omq_tokio::Message>>,
435
- send_pump: JoinHandle<()>,
407
+ mut send_pump: JoinHandle<()>,
436
408
  recv_pump: JoinHandle<()>,
437
409
  monitor_pump: JoinHandle<()>,
438
410
  linger: Option<Duration>,
439
411
  ) {
440
412
  recv_pump.abort();
441
413
  monitor_pump.abort();
442
- send_pump.abort();
443
- drop(send_prod);
444
- let Ok(handle) = (|| -> std::result::Result<Handle, ()> { Ok(ensure_runtime(io_threads)) })()
445
- else {
446
- return;
447
- };
414
+ let handle = ensure_runtime(io_threads);
448
415
  let close_timeout = linger
449
416
  .unwrap_or(Duration::from_secs(30))
450
417
  .max(Duration::from_millis(10));
451
- handle.spawn(async move {
418
+ let fut = async move {
419
+ drop(send_prod);
420
+ if tokio::time::timeout(close_timeout, &mut send_pump)
421
+ .await
422
+ .is_err()
423
+ {
424
+ send_pump.abort();
425
+ let _ = send_pump.await;
426
+ }
427
+
452
428
  let s = Arc::try_unwrap(sock).unwrap_or_else(|arc| (*arc).clone());
453
429
  let _ = tokio::time::timeout(close_timeout, s.close()).await;
430
+ };
431
+
432
+ let (otx, orx) = flume::bounded::<()>(1);
433
+ handle.spawn(async move {
434
+ fut.await;
435
+ let _ = otx.send(());
454
436
  });
437
+
438
+ recv_blocking(orx, "omq-backend-rust: close failed")
455
439
  }