Thanks to visit codestin.com
Credit goes to docs.rs

magnus/
thread.rs

1use std::{fmt, mem::size_of, os::raw::c_void, slice, time::Duration};
2
3#[allow(deprecated)]
4use rb_sys::rb_thread_fd_close;
5use rb_sys::{
6    rb_data_typed_object_wrap, rb_thread_alone, rb_thread_check_ints, rb_thread_create,
7    rb_thread_current, rb_thread_fd_writable, rb_thread_interrupted, rb_thread_kill,
8    rb_thread_local_aref, rb_thread_local_aset, rb_thread_main, rb_thread_run, rb_thread_schedule,
9    rb_thread_sleep_deadly, rb_thread_sleep_forever, rb_thread_wait_fd, rb_thread_wait_for,
10    rb_thread_wakeup, rb_thread_wakeup_alive, timeval, VALUE,
11};
12
13use crate::{
14    api::Ruby,
15    data_type_builder,
16    error::{protect, Error},
17    gc,
18    into_value::IntoValue,
19    method::{BlockReturn, Thread as _},
20    object::Object,
21    r_file::fd::AsRawFd,
22    r_typed_data::RTypedData,
23    try_convert::TryConvert,
24    typed_data::{DataType, DataTypeFunctions},
25    value::{
26        private::{self, ReprValue as _},
27        IntoId, ReprValue, Value,
28    },
29};
30
31/// # `Thread`
32///
33/// Functions to create and work with Ruby `Thread`s.
34///
35/// See also the [`Thread`] type.
36impl Ruby {
37    /// Create a Ruby thread.
38    ///
39    /// As `func` is a function pointer, only functions and closures that do
40    /// not capture any variables are permitted. For more flexibility (at the
41    /// cost of allocating) see
42    /// [`thread_create_from_fn`](Ruby::thread_create_from_fn).
43    ///
44    /// # Examples
45    ///
46    /// ```
47    /// use magnus::{rb_assert, Error, Ruby};
48    ///
49    /// fn example(ruby: &Ruby) -> Result<(), Error> {
50    ///     let t = ruby.thread_create(|_ruby| 1 + 2);
51    ///     rb_assert!("t.value == 3", t);
52    ///
53    ///     Ok(())
54    /// }
55    /// # Ruby::init(example).unwrap()
56    /// ```
57    pub fn thread_create<R>(&self, func: fn(&Ruby) -> R) -> Thread
58    where
59        R: BlockReturn,
60    {
61        unsafe extern "C" fn call<R>(arg: *mut c_void) -> VALUE
62        where
63            R: BlockReturn,
64        {
65            let func = std::mem::transmute::<*mut c_void, fn(&Ruby) -> R>(arg);
66            func.call_handle_error().as_rb_value()
67        }
68
69        let call_func = call::<R> as unsafe extern "C" fn(arg: *mut c_void) -> VALUE;
70
71        unsafe {
72            protect(|| {
73                Thread::from_rb_value_unchecked(rb_thread_create(
74                    Some(call_func),
75                    func as *mut c_void,
76                ))
77            })
78            .unwrap()
79        }
80    }
81
82    /// Create a Ruby thread.
83    ///
84    /// See also [`thread_create`](Ruby::thread_create), which is more
85    /// efficient when `func` is a function or closure that does not
86    /// capture any variables.
87    ///
88    /// # Examples
89    ///
90    /// ```
91    /// use magnus::{rb_assert, Error, Ruby};
92    ///
93    /// fn example(ruby: &Ruby) -> Result<(), Error> {
94    ///     let i = 1;
95    ///     let t = ruby.thread_create_from_fn(move |_ruby| i + 2);
96    ///     rb_assert!("t.value == 3", t);
97    ///
98    ///     Ok(())
99    /// }
100    /// # Ruby::init(example).unwrap()
101    /// ```
102    pub fn thread_create_from_fn<F, R>(&self, func: F) -> Thread
103    where
104        F: 'static + Send + FnOnce(&Ruby) -> R,
105        R: BlockReturn,
106    {
107        unsafe extern "C" fn call<F, R>(arg: *mut c_void) -> VALUE
108        where
109            F: FnOnce(&Ruby) -> R,
110            R: BlockReturn,
111        {
112            let closure = (*(arg as *mut Option<F>)).take().unwrap();
113            closure.call_handle_error().as_rb_value()
114        }
115
116        let (closure, keepalive) = wrap_closure(func);
117        let call_func = call::<F, R> as unsafe extern "C" fn(arg: *mut c_void) -> VALUE;
118
119        let t = unsafe {
120            protect(|| {
121                Thread::from_rb_value_unchecked(rb_thread_create(
122                    Some(call_func),
123                    closure as *mut c_void,
124                ))
125            })
126            .unwrap()
127        };
128        // ivar without @ prefix is invisible from Ruby
129        t.ivar_set("__rust_closure", keepalive).unwrap();
130        t
131    }
132
133    /// Return the currently executing thread.
134    ///
135    /// # Examples
136    ///
137    /// ```
138    /// use magnus::{prelude::*, Error, Ruby};
139    ///
140    /// fn example(ruby: &Ruby) -> Result<(), Error> {
141    ///     let t = ruby.thread_current();
142    ///     t.is_kind_of(ruby.class_thread());
143    ///
144    ///     Ok(())
145    /// }
146    /// # Ruby::init(example).unwrap()
147    /// ```
148    pub fn thread_current(&self) -> Thread {
149        unsafe { Thread::from_rb_value_unchecked(rb_thread_current()) }
150    }
151
152    /// Return the 'main' thread.
153    ///
154    /// # Examples
155    ///
156    /// ```
157    /// use magnus::{prelude::*, Error, Ruby};
158    ///
159    /// fn example(ruby: &Ruby) -> Result<(), Error> {
160    ///     let t = ruby.thread_main();
161    ///     t.is_kind_of(ruby.class_thread());
162    ///
163    ///     Ok(())
164    /// }
165    /// # Ruby::init(example).unwrap()
166    /// ```
167    pub fn thread_main(&self) -> Thread {
168        unsafe { Thread::from_rb_value_unchecked(rb_thread_main()) }
169    }
170
171    /// Attempt to schedule another thread.
172    ///
173    /// This function blocks until the current thread is re-scheduled.
174    pub fn thread_schedule(&self) {
175        unsafe { rb_thread_schedule() };
176    }
177
178    /// Blocks until the given file descriptor is readable.
179    ///
180    /// # Examples
181    ///
182    /// ```
183    /// # #[cfg(unix)]
184    /// # {
185    /// use std::{
186    ///     io::{Read, Write},
187    ///     net::Shutdown,
188    ///     os::unix::net::UnixStream,
189    /// };
190    ///
191    /// use magnus::{Error, Ruby};
192    ///
193    /// fn example(ruby: &Ruby) -> Result<(), Error> {
194    ///     let (mut a, mut b) = UnixStream::pair().unwrap();
195    ///     a.write_all(b"hello, world!").unwrap();
196    ///     a.shutdown(Shutdown::Both).unwrap();
197    ///
198    ///     b.set_nonblocking(true).unwrap();
199    ///     ruby.thread_wait_fd(&b)?;
200    ///
201    ///     let mut s = String::new();
202    ///     b.read_to_string(&mut s).unwrap();
203    ///     assert_eq!(s, "hello, world!");
204    ///
205    ///     Ok(())
206    /// }
207    /// # Ruby::init(example).unwrap()
208    /// # }
209    /// ```
210    pub fn thread_wait_fd<T>(&self, fd: &T) -> Result<(), Error>
211    where
212        T: AsRawFd,
213    {
214        let fd = fd.as_raw_fd();
215        protect(|| {
216            unsafe { rb_thread_wait_fd(fd) };
217            self.qnil()
218        })?;
219        Ok(())
220    }
221
222    /// Blocks until the given file descriptor is writable.
223    ///
224    /// # Examples
225    ///
226    /// ```
227    /// # #[cfg(unix)]
228    /// # {
229    /// use std::{
230    ///     io::{Read, Write},
231    ///     net::Shutdown,
232    ///     os::unix::net::UnixStream,
233    /// };
234    ///
235    /// use magnus::{Error, Ruby};
236    ///
237    /// fn example(ruby: &Ruby) -> Result<(), Error> {
238    ///     let (mut a, mut b) = UnixStream::pair().unwrap();
239    ///
240    ///     a.set_nonblocking(true).unwrap();
241    ///     ruby.thread_fd_writable(&a)?;
242    ///     a.write_all(b"hello, world!").unwrap();
243    ///     a.shutdown(Shutdown::Both).unwrap();
244    ///
245    ///     let mut s = String::new();
246    ///     b.read_to_string(&mut s).unwrap();
247    ///     assert_eq!(s, "hello, world!");
248    ///
249    ///     Ok(())
250    /// }
251    /// # Ruby::init(example).unwrap()
252    /// # }
253    /// ```
254    pub fn thread_fd_writable<T>(&self, fd: &T) -> Result<(), Error>
255    where
256        T: AsRawFd,
257    {
258        let fd = fd.as_raw_fd();
259        protect(|| {
260            unsafe { rb_thread_fd_writable(fd) };
261            self.qnil()
262        })?;
263        Ok(())
264    }
265
266    /// Schedules any Ruby threads waiting on `fd`, notifying them that `fd`
267    /// has been closed.
268    ///
269    /// Blocks until all threads waiting on `fd` have woken up.
270    #[deprecated(note = "No-op as of Ruby 3.5")]
271    pub fn thread_fd_close<T>(&self, fd: &T) -> Result<(), Error>
272    where
273        T: AsRawFd,
274    {
275        let fd = fd.as_raw_fd();
276        protect(|| {
277            unsafe {
278                #[allow(deprecated)]
279                rb_thread_fd_close(fd)
280            };
281            self.qnil()
282        })?;
283        Ok(())
284    }
285
286    /// Checks if the current thread is the only thread currently alive.
287    ///
288    /// # Examples
289    ///
290    /// ```
291    /// use std::time::Duration;
292    ///
293    /// use magnus::{Error, Ruby};
294    ///
295    /// fn example(ruby: &Ruby) -> Result<(), Error> {
296    ///     assert!(ruby.thread_alone());
297    ///
298    ///     ruby.thread_create_from_fn(|ruby| ruby.thread_sleep(Duration::from_secs(1)));
299    ///
300    ///     assert!(!ruby.thread_alone());
301    ///
302    ///     Ok(())
303    /// }
304    /// # Ruby::init(example).unwrap()
305    /// ```
306    pub fn thread_alone(&self) -> bool {
307        unsafe { rb_thread_alone() != 0 }
308    }
309
310    /// Blocks for the given period of time.
311    ///
312    /// Returns an error if sleep is interrupted by a signal.
313    ///
314    /// # Examples
315    ///
316    /// ```
317    /// use std::time::{Duration, Instant};
318    ///
319    /// use magnus::{Error, Ruby};
320    ///
321    /// fn example(ruby: &Ruby) -> Result<(), Error> {
322    ///     let now = Instant::now();
323    ///     ruby.thread_sleep(Duration::from_millis(100))?;
324    ///     let elapsed = now.elapsed();
325    ///     assert!(elapsed.as_millis() > 90);
326    ///     assert!(elapsed.as_secs() < 1);
327    ///
328    ///     Ok(())
329    /// }
330    /// # Ruby::init(example).unwrap()
331    /// ```
332    pub fn thread_sleep(&self, duration: Duration) -> Result<(), Error> {
333        let t = timeval {
334            tv_sec: duration.as_secs() as _,
335            tv_usec: duration.subsec_micros() as _,
336        };
337        protect(|| {
338            unsafe { rb_thread_wait_for(t) };
339            self.qnil()
340        })?;
341        Ok(())
342    }
343
344    /// Blocks indefinitely.
345    ///
346    /// Returns an error if sleep is interrupted by a signal.
347    pub fn thread_sleep_forever(&self) -> Result<(), Error> {
348        protect(|| {
349            unsafe { rb_thread_sleep_forever() };
350            self.qnil()
351        })?;
352        Ok(())
353    }
354
355    /// Blocks indefinitely.
356    ///
357    /// The  thread  calling  this function is considered "dead" when Ruby's
358    /// deadlock checker is triggered.
359    /// See also [`thread_sleep_forever`](Ruby::thread_sleep_forever).
360    ///
361    /// Returns an error if sleep is interrupted by a signal.
362    pub fn thread_sleep_deadly(&self) -> Result<(), Error> {
363        protect(|| {
364            unsafe { rb_thread_sleep_deadly() };
365            self.qnil()
366        })?;
367        Ok(())
368    }
369
370    /// Stop the current thread.
371    ///
372    /// The thread can later be woken up, see [`Thread::wakeup`].
373    ///
374    /// Returns an error if stopping the current thread would deadlock.
375    pub fn thread_stop(&self) -> Result<(), Error> {
376        protect(|| {
377            unsafe { rb_thread_sleep_forever() };
378            self.qnil()
379        })?;
380        Ok(())
381    }
382
383    /// Check for, and run, pending interrupts.
384    ///
385    /// While Ruby is running a native extension function (such as one written
386    /// in Rust with Magnus) it can't process interrupts (e.g. signals or
387    /// `Thread#raise` called from another thread). Periodically calling this
388    /// function in any long running function will check for *and run* any
389    /// queued interrupts. This will allow your long running function to be
390    /// interrupted with say ctrl-c or `Timeout::timeout`.
391    ///
392    /// If any interrupt raises an error it will be returned as `Err`.
393    ///
394    /// Calling this function may execute code on another thread.
395    pub fn thread_check_ints(&self) -> Result<(), Error> {
396        protect(|| {
397            unsafe { rb_thread_check_ints() };
398            self.qnil()
399        })?;
400        Ok(())
401    }
402}
403
404/// Wrapper type for a Value known to be an instance of Ruby's Thread class.
405///
406/// See the [`ReprValue`] and [`Object`] traits for additional methods
407/// available on this type. See [`Ruby`](Ruby#thread) for methods to create a
408/// `Thread`.
409#[derive(Clone, Copy)]
410#[repr(transparent)]
411pub struct Thread(RTypedData);
412
413impl Thread {
414    /// Return `Some(Thread)` if `val` is a `Thread`, `None` otherwise.
415    ///
416    /// # Examples
417    ///
418    /// ```
419    /// use magnus::eval;
420    /// # let _cleanup = unsafe { magnus::embed::init() };
421    ///
422    /// assert!(magnus::Thread::from_value(eval("Thread.new {1 + 2}").unwrap()).is_some());
423    /// assert!(magnus::Thread::from_value(eval("Proc.new {1 + 2}").unwrap()).is_none());
424    /// ```
425    #[inline]
426    pub fn from_value(val: Value) -> Option<Self> {
427        RTypedData::from_value(val)
428            .filter(|_| val.is_kind_of(Ruby::get_with(val).class_thread()))
429            .map(Self)
430    }
431
432    #[inline]
433    pub(crate) unsafe fn from_rb_value_unchecked(val: VALUE) -> Self {
434        Self(RTypedData::from_rb_value_unchecked(val))
435    }
436
437    /// Mark `self` as eligible for scheduling.
438    ///
439    /// See also [`Thread::wakeup_alive`] and [`Thread::run`].
440    ///
441    /// The thread is not scheduled immediately, simply marked as available.
442    /// The thread may also remain blocked on IO.
443    ///
444    /// Returns an error `self` is dead.
445    pub fn wakeup(self) -> Result<(), Error> {
446        let ruby = Ruby::get_with(self);
447        protect(|| {
448            unsafe { rb_thread_wakeup(self.as_rb_value()) };
449            ruby.qnil()
450        })?;
451        Ok(())
452    }
453
454    /// Mark `self` as eligible for scheduling.
455    ///
456    /// See also [`Thread::wakeup`] and [`Thread::run`].
457    ///
458    /// The thread is not scheduled immediately, simply marked as available.
459    /// The thread may also remain blocked on IO.
460    pub fn wakeup_alive(self) {
461        unsafe { rb_thread_wakeup_alive(self.as_rb_value()) };
462    }
463
464    /// Mark `self` as eligible for scheduling and invoke the thread schedular.
465    ///
466    /// See also [`Thread::wakeup`] and [`Thread::wakeup_alive`].
467    ///
468    /// There is no guarantee that `self` will be the next thread scheduled.
469    ///
470    /// Returns an error `self` is dead.
471    pub fn run(self) -> Result<(), Error> {
472        let ruby = Ruby::get_with(self);
473        protect(|| {
474            unsafe { rb_thread_run(self.as_rb_value()) };
475            ruby.qnil()
476        })?;
477        Ok(())
478    }
479
480    /// Terminates `self`.
481    ///
482    /// Returns an error if the `self` is the current or main thread, returning
483    /// this error to Ruby will end the process.
484    pub fn kill(self) -> Result<(), Error> {
485        let ruby = Ruby::get_with(self);
486        protect(|| {
487            unsafe { rb_thread_kill(self.as_rb_value()) };
488            ruby.qnil()
489        })?;
490        Ok(())
491    }
492
493    /// Get the value for `key` from the Fiber-local storage of the Fiber
494    /// currently executing on the thread `self`.
495    ///
496    /// When Fibers were added to Ruby this method became Fiber-local. If only
497    /// a single Fiber is run on a thread then this acts exactly like
498    /// thread-local storage. Ruby's C API does not expose true thread local
499    /// storage.
500    ///
501    /// # Examples
502    ///
503    /// ```
504    /// use magnus::{Error, Ruby};
505    ///
506    /// fn example(ruby: &Ruby) -> Result<(), Error> {
507    ///     let current = ruby.thread_current();
508    ///     let val: Option<String> = current.local_aref("example")?;
509    ///     assert!(val.is_none());
510    ///
511    ///     let other = ruby.thread_create(|ruby| {
512    ///         ruby.thread_stop()?;
513    ///
514    ///         let val: String = ruby.thread_current().local_aref("example")?;
515    ///         assert_eq!(val, "test");
516    ///
517    ///         Ok(())
518    ///     });
519    ///
520    ///     current.local_aset("example", "foo")?;
521    ///     other.local_aset("example", "test")?;
522    ///
523    ///     let val: String = current.local_aref("example")?;
524    ///     assert_eq!(val, "foo");
525    ///
526    ///     other.run()?;
527    ///
528    ///     Ok(())
529    /// }
530    /// # Ruby::init(example).unwrap()
531    /// ```
532    pub fn local_aref<I, T>(self, key: I) -> Result<T, Error>
533    where
534        I: IntoId,
535        T: TryConvert,
536    {
537        T::try_convert(Value::new(unsafe {
538            rb_thread_local_aref(
539                self.as_rb_value(),
540                key.into_id_with(&Ruby::get_with(self)).as_rb_id(),
541            )
542        }))
543    }
544
545    /// Set the value for `key` from the Fiber-local storage of the Fiber
546    /// currently executing on the thread `self`.
547    ///
548    /// Returns `Err` if `self` is frozen.
549    ///
550    /// When Fibers were added to Ruby this method became Fiber-local. If only
551    /// a single Fiber is run on a thread then this acts exactly like
552    /// thread-local storage. Ruby's C API does not expose true thread local
553    /// storage.
554    ///
555    /// # Examples
556    ///
557    /// ```
558    /// use magnus::{Error, Ruby};
559    ///
560    /// fn example(ruby: &Ruby) -> Result<(), Error> {
561    ///     let current = ruby.thread_current();
562    ///     let val: Option<String> = current.local_aref("example")?;
563    ///     assert!(val.is_none());
564    ///
565    ///     let other = ruby.thread_create(|ruby| {
566    ///         ruby.thread_stop()?;
567    ///
568    ///         let val: String = ruby.thread_current().local_aref("example")?;
569    ///         assert_eq!(val, "test");
570    ///
571    ///         Ok(())
572    ///     });
573    ///
574    ///     current.local_aset("example", "foo")?;
575    ///     other.local_aset("example", "test")?;
576    ///
577    ///     let val: String = current.local_aref("example")?;
578    ///     assert_eq!(val, "foo");
579    ///
580    ///     other.run()?;
581    ///
582    ///     Ok(())
583    /// }
584    /// # Ruby::init(example).unwrap()
585    /// ```
586    pub fn local_aset<I, T>(self, key: I, val: T) -> Result<(), Error>
587    where
588        I: IntoId,
589        T: IntoValue,
590    {
591        let ruby = Ruby::get_with(self);
592        let key = key.into_id_with(&ruby);
593        let val = val.into_value_with(&ruby);
594        protect(|| {
595            unsafe { rb_thread_local_aset(self.as_rb_value(), key.as_rb_id(), val.as_rb_value()) };
596            ruby.qnil()
597        })?;
598        Ok(())
599    }
600
601    /// Check if `self` has been interrupted.
602    ///
603    /// Returns true if the thread was interrupted, false otherwise. This can
604    /// be used to detect spurious wakeups.
605    pub fn interrupted(self) -> bool {
606        unsafe { rb_thread_interrupted(self.as_rb_value()) != 0 }
607    }
608}
609
610impl fmt::Display for Thread {
611    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
612        write!(f, "{}", unsafe { self.to_s_infallible() })
613    }
614}
615
616impl fmt::Debug for Thread {
617    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
618        write!(f, "{}", self.inspect())
619    }
620}
621
622impl IntoValue for Thread {
623    #[inline]
624    fn into_value_with(self, _: &Ruby) -> Value {
625        self.0.as_value()
626    }
627}
628
629impl Object for Thread {}
630
631unsafe impl private::ReprValue for Thread {}
632
633impl ReprValue for Thread {}
634
635impl TryConvert for Thread {
636    fn try_convert(val: Value) -> Result<Self, Error> {
637        Self::from_value(val).ok_or_else(|| {
638            Error::new(
639                Ruby::get_with(val).exception_type_error(),
640                format!("no implicit conversion of {} into Thread", unsafe {
641                    val.classname()
642                },),
643            )
644        })
645    }
646}
647
648/// Wrap a closure in a Ruby object with no class.
649///
650/// This effectively makes the closure's lifetime managed by Ruby. It will be
651/// dropped when the returned `Value` is garbage collected.
652fn wrap_closure<F, R>(func: F) -> (*mut Option<F>, Value)
653where
654    F: FnOnce(&Ruby) -> R,
655    R: BlockReturn,
656{
657    struct Closure<F>(Option<F>, DataType);
658    unsafe impl<F> Send for Closure<F> {}
659    impl<F> DataTypeFunctions for Closure<F> {
660        fn mark(&self, marker: &gc::Marker) {
661            // Attempt to mark any Ruby values captured in a closure.
662            // Rust's closures are structs that contain all the values they
663            // have captured. This reads that struct as a slice of VALUEs and
664            // calls rb_gc_mark_locations which calls gc_mark_maybe which
665            // marks VALUEs and ignores non-VALUEs
666            marker.mark_slice(unsafe {
667                slice::from_raw_parts(
668                    &self.0 as *const _ as *const Value,
669                    size_of::<F>() / size_of::<Value>(),
670                )
671            });
672        }
673    }
674
675    let data_type = data_type_builder!(Closure<F>, "rust closure")
676        .free_immediately()
677        .mark()
678        .build();
679
680    let boxed = Box::new(Closure(Some(func), data_type));
681    let ptr = Box::into_raw(boxed);
682    let value = unsafe {
683        Value::new(rb_data_typed_object_wrap(
684            0, // using 0 for the class will hide the object from ObjectSpace
685            ptr as *mut _,
686            (*ptr).1.as_rb_data_type() as *const _,
687        ))
688    };
689    unsafe { (&mut (*ptr).0 as *mut Option<F>, value) }
690}