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

magnus/
api.rs

1//! This module/file's name is a hack to get the `impl Ruby` defined here to
2//! show first in docs. This module shouldn't be exposed publicly.
3
4use std::{cell::RefCell, marker::PhantomData};
5
6use rb_sys::ruby_native_thread_p;
7
8// Ruby does not expose this publicly, but it is used in the fiddle gem via
9// this kind of hack, and although the function is marked experimental in
10// Ruby's source, that comment and the code have been unchanged singe 1.9.2,
11// 14 years ago as of writing.
12extern "C" {
13    fn ruby_thread_has_gvl_p() -> ::std::os::raw::c_int;
14}
15
16use crate::{error::RubyUnavailableError, value::ReprValue};
17
18#[derive(Clone, Copy)]
19enum RubyGvlState {
20    Locked,
21    Unlocked,
22    NonRubyThread,
23}
24
25thread_local! {
26    static RUBY_GVL_STATE: RefCell<Option<RubyGvlState>> = const { RefCell::new(None) };
27}
28
29impl RubyGvlState {
30    fn current() -> Self {
31        let current = if unsafe { ruby_thread_has_gvl_p() } != 0 {
32            Self::Locked
33        } else if unsafe { ruby_native_thread_p() != 0 } {
34            Self::Unlocked
35        } else {
36            Self::NonRubyThread
37        };
38        RUBY_GVL_STATE.with(|ruby_gvl_state| {
39            *ruby_gvl_state.borrow_mut() = Some(current);
40        });
41        current
42    }
43
44    fn cached() -> Self {
45        RUBY_GVL_STATE.with(|ruby_gvl_state| {
46            let x = *ruby_gvl_state.borrow();
47            match x {
48                // assumed not to change because there's currently no api to
49                // unlock.
50                Some(Self::Locked) => Self::Locked,
51                None => Self::current(),
52                // Don't expect without an api to unlock, so skip cache
53                Some(Self::Unlocked) => Self::current(),
54                // assumed not to change
55                Some(Self::NonRubyThread) => Self::NonRubyThread,
56            }
57        })
58    }
59
60    fn ok<T>(self, value: T) -> Result<T, RubyUnavailableError> {
61        match self {
62            Self::Locked => Ok(value),
63            Self::Unlocked => Err(RubyUnavailableError::GvlUnlocked),
64            Self::NonRubyThread => Err(RubyUnavailableError::NonRubyThread),
65        }
66    }
67}
68
69/// A handle to access Ruby's API.
70///
71/// Using Ruby's API requires the Ruby VM to be initialised and all access to be
72/// from a Ruby-created thread.
73///
74/// This structure allows safe access to Ruby's API as it should only be
75/// possible to acquire an instance in situations where Ruby's API is known to
76/// be available.
77///
78/// Many functions that take Ruby values as arguments are available directly
79/// without having to use a `Ruby` handle, as being able to provide a Ruby
80/// value is 'proof' the function is being called from a Ruby thread. Because
81/// of this most methods defined on `Ruby` deal with creating Ruby objects
82/// from Rust data.
83///
84/// ---
85///
86/// The methods available on `Ruby` are broken up into sections for easier
87/// navigation.
88///
89/// * [Accessing `Ruby`](#accessing-ruby) - how to get a `Ruby` handle
90/// * [Argument Parsing](#argument-parsing) - helpers for argument handling
91/// * [Blocks](#blocks) - working with Ruby blocks
92/// * [Conversion to `Value`](#conversion-to-value)
93/// * [Core Classes](#core-classes) - access built-in classes
94/// * [Core Exceptions](#core-exceptions) - access built-in exceptions
95/// * [Core Modules](#core-modules) - access built-in modules
96/// * [Embedding](#embedding) - functions relevant when embedding Ruby in Rust
97/// * [`Encoding`](#encoding) - string encoding
98/// * [Encoding Index](#encoding-index) - string encoding
99/// * [Errors](#errors)
100/// * [Extracting values from `Opaque`/`Lazy`](#extracting-values-from-opaquelazy)
101/// * [`false`](#false)
102/// * [`Fiber`](#fiber)
103/// * [`Fixnum`](#fixnum) - small/fast integers
104/// * [`Float`](#float)
105/// * [`Flonum`](#flonum) - lower precision/fast floats
106/// * [`GC`](#gc) - Garbage Collection
107/// * [Globals](#globals) - global variables, etc, plus current VM state such
108///   as calling the current `super` method.
109/// * [`Id`](#id) - low-level Symbol representation
110/// * [`Io`](#io-helper-functions) - IO helper functions
111/// * [`Integer`](#integer)
112/// * [`Mutex`](#mutex)
113/// * [`nil`](#nil)
114/// * [`Proc`](#proc) - Ruby's blocks as objects
115/// * [`Process`](#process) - external processes
116/// * [`Range`](#range)
117/// * [`RArray`](#rarray)
118/// * [`RbEncoding`](#rbencoding) - string encoding
119/// * [`RBignum`](#rbignum) - big integers
120/// * [`RFloat`](#rfloat)
121/// * [`RHash`](#rhash)
122/// * [`RModule`](#rmodule)
123/// * [`RRational`](#rrational)
124/// * [`RRegexp`](#rregexp)
125/// * [`RString`](#rstring)
126/// * [`RTypedData`](#rtypeddata) - wrapping Rust data in a Ruby object
127/// * [`StaticSymbol`](#staticsymbol) - non GC'd symbols
128/// * [`Struct`](#struct)
129/// * [`Symbol`](#symbol)
130/// * [`Thread`](#thread)
131/// * [`Time`](#time)
132/// * [`true`](#true)
133/// * [`typed_data::Obj`](#typed_dataobj) - wrapping Rust data in a Ruby object
134pub struct Ruby(PhantomData<*mut ()>);
135
136/// # Accessing `Ruby`
137///
138/// These functions allow you to obtain a `Ruby` handle only when the current
139/// thread is a Ruby thread.
140///
141/// Methods exposed to Ruby via the [`method`](macro@crate::method),
142/// [`function`](macro@crate::function) or [`init`](macro@crate::init) macros
143/// can also take an optional first argument of `&Ruby` to obtain a `Ruby`
144/// handle.
145impl Ruby {
146    /// Get a handle to Ruby's API.
147    ///
148    /// Returns a new handle to Ruby's API if it can be verified the current
149    /// thread is a Ruby thread.
150    ///
151    /// If the Ruby API is not useable, returns `Err(RubyUnavailableError)`.
152    pub fn get() -> Result<Self, RubyUnavailableError> {
153        RubyGvlState::cached().ok(Self(PhantomData))
154    }
155
156    /// Get a handle to Ruby's API.
157    ///
158    /// Returns a new handle to Ruby's API using a Ruby value as proof that the
159    /// current thread is a Ruby thread.
160    ///
161    /// Note that all Ruby values are [`Copy`], so this will not take ownership
162    /// of the passed value.
163    #[allow(unused_variables)]
164    pub fn get_with<T>(value: T) -> Self
165    where
166        T: ReprValue,
167    {
168        Self(PhantomData)
169    }
170
171    /// Get a handle to Ruby's API.
172    ///
173    /// # Safety
174    ///
175    /// This must only be called from a Ruby thread - that is one created by
176    /// Ruby, or the main thread after [`embed::init`](crate::embed::init) has
177    /// been called - and without having released the GVL.
178    #[inline]
179    pub unsafe fn get_unchecked() -> Self {
180        Self(PhantomData)
181    }
182}