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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +39 -0
- data/ext/omq_backend_rust/Cargo.toml +3 -4
- data/ext/omq_backend_rust/build.rs +23 -0
- data/ext/omq_backend_rust/src/error.rs +11 -10
- data/ext/omq_backend_rust/src/lib.rs +35 -10
- data/ext/omq_backend_rust/src/options.rs +117 -104
- data/ext/omq_backend_rust/src/rb.rs +433 -0
- data/ext/omq_backend_rust/src/runtime.rs +69 -85
- data/ext/omq_backend_rust/src/socket.rs +395 -166
- data/lib/omq/rust/engine.rb +82 -24
- data/lib/omq/rust/fd_watcher.rb +184 -0
- data/lib/omq/rust/version.rb +1 -1
- metadata +4 -1
|
@@ -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
|
-
|
|
32
|
-
|
|
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
|
-
|
|
74
|
-
|
|
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
|
-
|
|
83
|
-
|
|
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(
|
|
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
|
-
|
|
176
|
-
|
|
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
|
-
|
|
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
|
-
|
|
314
|
+
send_cons.release();
|
|
310
315
|
sn.notify();
|
|
311
|
-
|
|
312
|
-
|
|
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
|
-
|
|
373
|
-
|
|
374
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
}
|