1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
use std::convert::From;

use binding::thread;
use types::Value;

#[cfg(unix)]
use types::RawFd;

use {Class, Object, VerifiedObject};

/// `Thread`
#[derive(Debug, PartialEq)]
pub struct Thread {
    value: Value,
}

impl Thread {
    /// Creates a new green thread.
    ///
    /// The returning value of the closure will be available as `#value` of the thread
    ///
    /// # Examples
    ///
    /// ```
    /// use ruru::{Fixnum, Thread, VM};
    /// # VM::init();
    ///
    /// Thread::new(|| {
    ///     let computation_result = 1 + 2;
    ///
    ///     Fixnum::new(computation_result)
    /// });
    /// ```
    ///
    /// Ruby:
    ///
    /// ```ruby
    /// Thread.new do
    ///   computation_result = 1 + 2
    ///
    ///   computation_result
    /// end
    /// ```
    pub fn new<F, R>(func: F) -> Self
        where F: FnOnce() -> R,
              R: Object
    {
        Self::from(thread::create(func))
    }

    /// Tells scheduler to switch to other threads while current thread is waiting for a
    /// readable event on the given file descriptor.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::os::unix::io::AsRawFd;
    /// use std::os::unix::net::UnixStream;
    ///
    /// use ruru::{Thread, VM};
    /// # VM::init();
    ///
    /// let (unix_socket, _) = UnixStream::pair().unwrap();
    ///
    /// Thread::wait_fd(unix_socket.as_raw_fd());
    /// ```
    #[cfg(unix)]
    pub fn wait_fd(fd: RawFd) {
        thread::wait_fd(fd);
    }

    /// Release GVL for current thread.
    ///
    /// **Warning!** Due to MRI limitations, interaction with Ruby objects is not allowed while
    /// GVL is released, it may cause unexpected behaviour.
    /// [Read more at Ruby documentation](https://github.com/ruby/ruby/blob/2fc5210f31ad23463d7b0a0e36bcfbeee7b41b3e/thread.c#L1314-L1398)
    ///
    /// You should extract all the information from Ruby world before invoking
    /// `thread_call_without_gvl`.
    ///
    /// GVL will be re-acquired when the closure is finished.
    ///
    /// # Examples
    ///
    /// ```no_run
    /// #[macro_use] extern crate ruru;
    ///
    /// use ruru::{Class, Fixnum, Object, Thread};
    ///
    /// class!(Calculator);
    ///
    /// methods!(
    ///     Calculator,
    ///     itself,
    ///
    ///     fn heavy_computation() -> Fixnum {
    ///         let computation = || { 2 * 2 };
    ///         let unblocking_function = || {};
    ///
    ///         // release GVL for current thread until `computation` is completed
    ///         let result = Thread::call_without_gvl(
    ///             computation,
    ///             Some(unblocking_function)
    ///         );
    ///
    ///         // GVL is re-acquired, we can interact with Ruby-world
    ///         Fixnum::new(result)
    ///     }
    /// );
    ///
    /// fn main() {
    ///     Class::new("Calculator", None).define(|itself| {
    ///         itself.def("heavy_computation", heavy_computation);
    ///     });
    /// }
    /// ```
    pub fn call_without_gvl<F, R, G>(func: F, unblock_func: Option<G>) -> R
        where F: FnOnce() -> R,
              G: FnOnce()
    {
        thread::call_without_gvl(func, unblock_func)
    }

    pub fn call_without_gvl2<F, R, G>(func: F, unblock_func: Option<G>) -> R
        where F: FnOnce() -> R,
              G: FnOnce()
    {
        thread::call_without_gvl2(func, unblock_func)
    }

    pub fn call_with_gvl<F, R>(func: F) -> R
        where F: FnOnce() -> R
    {
        thread::call_with_gvl(func)
    }
}

impl From<Value> for Thread {
    fn from(value: Value) -> Self {
        Thread { value: value }
    }
}

impl Object for Thread {
    #[inline]
    fn value(&self) -> Value {
        self.value
    }
}

impl VerifiedObject for Thread {
    fn is_correct_type<T: Object>(object: &T) -> bool {
        object.class() == Class::from_existing("Thread")
    }

    fn error_message() -> &'static str {
        "Error converting to Thread"
    }
}