gluon_vm/
vm.rs

1use std::{
2    any::{Any, TypeId},
3    result::Result as StdResult,
4    string::String as StdString,
5    sync::{atomic::AtomicUsize, Arc, Mutex, RwLock},
6    usize,
7};
8
9#[cfg(feature = "serde_derive_state")]
10use serde::de::DeserializeState;
11
12use crate::base::{
13    ast,
14    fnv::FnvMap,
15    kind::{ArcKind, Kind, KindEnv},
16    metadata::{Metadata, MetadataEnv},
17    symbol::{Name, Symbol, SymbolRef},
18    types::{
19        Alias, AliasData, AppVec, ArcType, Generic, NullInterner, PrimitiveEnv, Type, TypeCache,
20        TypeEnv, TypeExt,
21    },
22    DebugLevel,
23};
24
25use crate::{
26    api::{OpaqueValue, ValueRef, IO},
27    compiler::{CompiledFunction, CompiledModule, CompilerEnv, Variable},
28    core::{interpreter, optimize::OptimizeEnv, CoreExpr},
29    gc::{Gc, GcPtr, GcRef, Generation, Move, Trace},
30    interner::{InternedStr, Interner},
31    lazy::Lazy,
32    macros::MacroEnv,
33    thread::ThreadInternal,
34    types::*,
35    value::{BytecodeFunction, ClosureData, ClosureDataDef},
36    Error, Result, Variants,
37};
38
39pub use crate::{
40    thread::{RootedThread, RootedValue, Status, Thread},
41    value::Userdata,
42};
43
44pub(crate) type ThreadSlab = slab::Slab<GcPtr<Thread>>;
45
46unsafe impl Trace for ThreadSlab {
47    impl_trace! { self, gc,
48        for (_, x) in self {
49            mark(x, gc);
50        }
51    }
52}
53
54fn new_bytecode<'gc>(
55    env: &dyn VmEnv<Type = ArcType>,
56    interner: &mut Interner,
57    gc: &'gc mut Gc,
58    vm: &GlobalVmState,
59    m: CompiledModule,
60) -> Result<GcRef<'gc, ClosureData>> {
61    let CompiledModule {
62        module_globals,
63        function,
64    } = m;
65    let bytecode_function = new_bytecode_function(interner, gc, vm, function)?;
66
67    let globals = module_globals
68        .into_iter()
69        .map(|index| {
70            env.get_global(index.definition_name())
71                .expect("ICE: Global is missing from environment")
72                .value
73        })
74        .collect::<Vec<_>>();
75
76    // SAFETY No collection are done while we create these functions
77    unsafe {
78        let bytecode_function = bytecode_function.unrooted();
79        gc.alloc(ClosureDataDef(
80            &bytecode_function,
81            globals.iter().map(|v| v.get_value()),
82        ))
83    }
84}
85
86fn new_bytecode_function<'gc>(
87    interner: &mut Interner,
88    gc: &'gc mut Gc,
89    vm: &GlobalVmState,
90    f: CompiledFunction,
91) -> Result<GcRef<'gc, BytecodeFunction>> {
92    let CompiledFunction {
93        id,
94        args,
95        max_stack_size,
96        instructions,
97        inner_functions,
98        strings,
99        records,
100        debug_info,
101        ..
102    } = f;
103
104    let fs: Result<_> = inner_functions
105        .into_iter()
106        // SAFETY No collection are done while we create these functions
107        .map(|inner| unsafe { Ok(new_bytecode_function(interner, gc, vm, inner)?.unrooted()) })
108        .collect();
109
110    let records: StdResult<_, _> = records
111        .into_iter()
112        .map(|vec| {
113            vec.into_iter()
114                .map(|field| Ok(interner.intern(gc, field.as_ref())?))
115                .collect::<Result<_>>()
116        })
117        .collect();
118
119    gc.alloc(Move(BytecodeFunction {
120        name: id,
121        args,
122        max_stack_size,
123        instructions,
124        inner_functions: fs?,
125        strings,
126        records: records?,
127        debug_info,
128    }))
129}
130
131#[derive(Clone, Debug)]
132#[cfg_attr(
133    feature = "serde_derive_state",
134    derive(DeserializeState, SerializeState)
135)]
136#[cfg_attr(
137    feature = "serde_derive_state",
138    serde(
139        deserialize_state = "crate::serialization::DeSeed<'gc>",
140        de_parameters = "'gc",
141        bound(deserialize = "V: DeserializeState<'de, crate::serialization::DeSeed<'gc>>")
142    )
143)]
144#[cfg_attr(
145    feature = "serde_derive_state",
146    serde(serialize_state = "crate::serialization::SeSeed")
147)]
148pub struct Global<V> {
149    #[cfg_attr(
150        feature = "serde_derive",
151        serde(state_with = "crate::serialization::symbol")
152    )]
153    pub id: Symbol,
154    #[cfg_attr(
155        feature = "serde_derive",
156        serde(state_with = "crate::serialization::borrow")
157    )]
158    pub typ: ArcType,
159    pub metadata: Arc<Metadata>,
160    #[cfg_attr(feature = "serde_derive_state", serde(state))]
161    pub value: V,
162}
163
164pub type RootedGlobal = Global<RootedValue<RootedThread>>;
165
166impl<V> Eq for Global<V> {}
167
168impl<V> PartialEq for Global<V> {
169    fn eq(&self, other: &Self) -> bool {
170        self.id == other.id
171    }
172}
173
174impl<V> std::hash::Hash for Global<V> {
175    fn hash<H>(&self, hasher: &mut H)
176    where
177        H: std::hash::Hasher,
178    {
179        self.id.hash(hasher)
180    }
181}
182
183unsafe impl<V> Trace for Global<V>
184where
185    V: Trace,
186{
187    impl_trace_fields! { self, gc; value }
188}
189
190#[cfg_attr(feature = "serde_derive", derive(DeserializeState, SerializeState))]
191#[cfg_attr(
192    feature = "serde_derive",
193    serde(
194        deserialize_state = "crate::serialization::DeSeed<'gc>",
195        de_parameters = "'gc"
196    )
197)]
198#[cfg_attr(
199    feature = "serde_derive",
200    serde(serialize_state = "crate::serialization::SeSeed")
201)]
202pub struct GlobalVmState {
203    #[cfg_attr(
204        feature = "serde_derive",
205        serde(state_with = "crate::serialization::rw_lock")
206    )]
207    env: parking_lot::RwLock<Globals>,
208    #[cfg_attr(
209        feature = "serde_derive",
210        serde(state_with = "crate::serialization::borrow")
211    )]
212    generics: RwLock<FnvMap<StdString, ArcType>>,
213
214    #[cfg_attr(feature = "serde_derive", serde(skip))]
215    typeids: RwLock<FnvMap<TypeId, ArcType>>,
216
217    #[cfg_attr(feature = "serde_derive", serde(state))]
218    interner: RwLock<Interner>,
219
220    #[cfg_attr(feature = "serde_derive", serde(skip))]
221    macros: MacroEnv,
222
223    #[cfg_attr(feature = "serde_derive", serde(skip))]
224    type_cache: TypeCache<Symbol, ArcType>,
225
226    // FIXME These fields should not be public
227    #[cfg_attr(feature = "serde_derive", serde(state))]
228    pub gc: Mutex<Gc>,
229
230    // List of all generation 0 threads (ie, threads allocated by the global gc). when doing a
231    // generation 0 sweep these threads are scanned as generation 0 values may be refered to by any
232    // thread
233    #[cfg_attr(feature = "serde_derive", serde(skip))]
234    pub generation_0_threads: RwLock<ThreadSlab>,
235
236    #[cfg_attr(feature = "serde_derive", serde(skip))]
237    debug_level: RwLock<DebugLevel>,
238
239    /// Tracks how many `RootedThread`s exist that refer to this global state.
240    /// Only when all `RootedThread`s are dropped are we sure that we can drop any thread without
241    /// resorting to garbage collection
242    // The references gets added automatically when recreating the threads
243    #[cfg_attr(feature = "serde_derive", serde(skip))]
244    pub(crate) thread_reference_count: AtomicUsize,
245
246    #[cfg_attr(feature = "serde_derive", serde(skip))]
247    spawner: Option<Box<dyn futures::task::Spawn + Send + Sync>>,
248}
249
250unsafe impl Trace for GlobalVmState {
251    unsafe fn root(&mut self) {
252        self.macros.root();
253
254        // Also need to check the interned string table
255        self.interner.get_mut().unwrap().root();
256        self.generation_0_threads.get_mut().unwrap().root();
257    }
258    unsafe fn unroot(&mut self) {
259        self.macros.unroot();
260
261        // Also need to check the interned string table
262        self.interner.get_mut().unwrap().unroot();
263        self.generation_0_threads.get_mut().unwrap().unroot();
264    }
265
266    fn trace(&self, gc: &mut Gc) {
267        self.macros.trace(gc);
268
269        // Also need to check the interned string table
270        self.interner.read().unwrap().trace(gc);
271        self.generation_0_threads.read().unwrap().trace(gc);
272    }
273}
274
275#[derive(Debug, Default)]
276#[cfg_attr(feature = "serde_derive", derive(DeserializeState, SerializeState))]
277#[cfg_attr(
278    feature = "serde_derive",
279    serde(
280        deserialize_state = "crate::serialization::DeSeed<'gc>",
281        de_parameters = "'gc"
282    )
283)]
284#[cfg_attr(
285    feature = "serde_derive",
286    serde(serialize_state = "crate::serialization::SeSeed")
287)]
288pub struct Globals {
289    #[cfg_attr(feature = "serde_derive", serde(state))]
290    pub type_infos: TypeInfos,
291}
292
293pub trait VmEnv:
294    OptimizeEnv + CompilerEnv<Type = ArcType> + MetadataEnv + PrimitiveEnv + Trace
295{
296    fn get_global(&self, name: &str) -> Option<RootedGlobal>;
297}
298
299pub struct VmEnvInstance<'a> {
300    // FIXME Use the database stored here for lookups
301    vm_envs: Vec<Box<dyn VmEnv>>,
302    globals: parking_lot::RwLockReadGuard<'a, Globals>,
303    thread: &'a Thread,
304}
305
306unsafe impl Trace for VmEnvInstance<'_> {
307    impl_trace_fields! { self, gc; vm_envs }
308}
309
310impl<'a> OptimizeEnv for VmEnvInstance<'a> {
311    fn find_expr(&self, id: &Symbol) -> Option<interpreter::Global<CoreExpr>> {
312        self.vm_envs.iter().find_map(|env| env.find_expr(id))
313    }
314}
315
316impl<'a> CompilerEnv for VmEnvInstance<'a> {
317    fn find_var(&self, id: &Symbol) -> Option<(Variable<Symbol>, ArcType)> {
318        self.vm_envs
319            .iter()
320            .filter_map(|env| env.find_var(id))
321            .next()
322            .or_else(|| self.globals.type_infos.find_var(id))
323    }
324}
325
326impl<'a> KindEnv for VmEnvInstance<'a> {
327    fn find_kind(&self, id: &SymbolRef) -> Option<ArcKind> {
328        self.vm_envs
329            .iter()
330            .filter_map(|env| env.find_kind(id))
331            .next()
332            .or_else(|| self.globals.type_infos.find_kind(id))
333    }
334}
335
336impl<'a> TypeEnv for VmEnvInstance<'a> {
337    type Type = ArcType;
338
339    fn find_type(&self, id: &SymbolRef) -> Option<ArcType> {
340        self.vm_envs
341            .iter()
342            .filter_map(|env| env.find_type(id))
343            .next()
344            .or_else(|| {
345                self.globals
346                    .type_infos
347                    .id_to_type
348                    .values()
349                    .filter_map(|alias| match **alias.unresolved_type() {
350                        Type::Variant(ref row) => row
351                            .row_iter()
352                            .find(|field| *field.name == *id)
353                            .map(|field| &field.typ),
354                        _ => None,
355                    })
356                    .next()
357                    .map(|ctor| ctor.clone())
358            })
359    }
360
361    fn find_type_info(&self, id: &SymbolRef) -> Option<Alias<Symbol, ArcType>> {
362        self.vm_envs
363            .iter()
364            .filter_map(|env| env.find_type_info(id))
365            .next()
366            .or_else(|| self.globals.type_infos.find_type_info(id))
367    }
368}
369
370impl<'a> PrimitiveEnv for VmEnvInstance<'a> {
371    fn get_bool(&self) -> ArcType {
372        self.find_type_info("std.types.Bool")
373            .expect("Missing std.types.Bool")
374            .into_type()
375    }
376}
377
378impl<'a> MetadataEnv for VmEnvInstance<'a> {
379    fn get_metadata(&self, id: &SymbolRef) -> Option<Arc<Metadata>> {
380        self.get_metadata_(id.definition_name())
381    }
382}
383
384impl<'a> VmEnv for VmEnvInstance<'a> {
385    fn get_global(&self, name: &str) -> Option<RootedGlobal> {
386        self.vm_envs
387            .iter()
388            .filter_map(|env| env.get_global(name))
389            .next()
390    }
391}
392
393impl<'a> VmEnvInstance<'a> {
394    pub fn find_type_info(&self, name: &str) -> Result<Alias<Symbol, ArcType>> {
395        let name = Name::new(name);
396
397        if let Some(alias) = self.globals.type_infos.id_to_type.get(name.as_str()) {
398            return Ok(alias.clone());
399        }
400
401        let (_, typ) = self
402            .get_binding(name.module().as_str())
403            .map_err(|mut err| {
404                if let Error::UndefinedBinding(module) = &mut err {
405                    module.clear();
406                    module.push_str(name.as_str());
407                }
408                err
409            })?;
410        let maybe_type_info = {
411            let field_name = name.name();
412            typ.type_field_iter()
413                .find(|field| field.name.as_str() == field_name.as_str())
414                .map(|field| &field.typ)
415                .cloned()
416        };
417        maybe_type_info.ok_or_else(move || Error::UndefinedField(typ, name.name().as_str().into()))
418    }
419
420    fn get_scoped_global<'s, 'n>(&'s self, name: &'n str) -> Option<(&'n Name, RootedGlobal)> {
421        let mut module = Name::new(name.trim_start_matches('@'));
422        let global;
423        // Try to find a global by successively reducing the module path
424        // Input: "x.y.z.w"
425        // Test: "x.y.z"
426        // Test: "x.y"
427        // Test: "x"
428        // Test: -> Error
429        loop {
430            if module.as_str() == "" {
431                return None;
432            }
433            if let Some(g) = self.get_global(module.as_str()) {
434                global = g;
435                break;
436            }
437            module = module.module();
438        }
439
440        let remaining_offset = ::std::cmp::min(name.len(), module.as_str().len() + 1); //Add 1 byte for the '.'
441        let remaining_fields = Name::new(&name[remaining_offset..]);
442        Some((remaining_fields, global))
443    }
444
445    #[doc(hidden)]
446    pub fn get_binding(&self, name: &str) -> Result<(RootedValue<RootedThread>, ArcType)> {
447        use crate::base::resolve;
448
449        let (remaining_fields, global) = self
450            .get_scoped_global(name)
451            .ok_or_else(|| Error::UndefinedBinding(name.into()))?;
452
453        if remaining_fields.as_str().is_empty() {
454            // No fields left
455            return Ok((global.value, global.typ));
456        }
457
458        let mut typ = global.typ;
459        let mut value = Variants::new(&global.value);
460
461        for mut field_name in remaining_fields.components() {
462            if field_name.starts_with('(') && field_name.ends_with(')') {
463                field_name = &field_name[1..field_name.len() - 1];
464            } else if field_name.contains(ast::is_operator_char) {
465                return Err(Error::Message(format!(
466                    "Operators cannot be used as fields \
467                     directly. To access an operator field, \
468                     enclose the operator with parentheses \
469                     before passing it in. (test.(+) instead of \
470                     test.+)"
471                )));
472            }
473            typ = resolve::remove_aliases(self, &mut NullInterner, typ);
474            // HACK Can't return the data directly due to the use of cow on the type
475            let next_type = {
476                typ.row_iter()
477                    .enumerate()
478                    .find(|&(_, field)| field.name.as_pretty_str() == field_name)
479                    .map(|(index, field)| match value.as_ref() {
480                        ValueRef::Data(data) => {
481                            value = data.get_variant(index).unwrap();
482                            &field.typ
483                        }
484                        _ => ice!("Unexpected value {:?}", value),
485                    })
486                    .cloned()
487            };
488            typ = next_type.ok_or_else(move || Error::UndefinedField(typ, field_name.into()))?;
489        }
490        Ok((self.thread.root_value(value), typ))
491    }
492
493    pub fn get_metadata(&self, name_str: &str) -> Result<Arc<Metadata>> {
494        self.get_metadata_(name_str)
495            .ok_or_else(|| Error::MetadataDoesNotExist(name_str.into()))
496    }
497
498    fn get_metadata_(&self, name_str: &str) -> Option<Arc<Metadata>> {
499        let (remaining, global) = self.get_scoped_global(name_str)?;
500
501        let mut metadata = &global.metadata;
502        for field_name in remaining.components() {
503            metadata = metadata.module.get(field_name)?
504        }
505        Some(metadata.clone())
506    }
507}
508
509#[derive(Default)]
510pub struct GlobalVmStateBuilder {
511    spawner: Option<Box<dyn futures::task::Spawn + Send + Sync>>,
512}
513
514impl GlobalVmStateBuilder {
515    pub fn new() -> Self {
516        Self::default()
517    }
518
519    pub fn spawner(mut self, spawner: Option<Box<dyn futures::task::Spawn + Send + Sync>>) -> Self {
520        self.spawner = spawner;
521        self
522    }
523
524    pub fn build(self) -> GlobalVmState {
525        let mut vm = GlobalVmState {
526            env: Default::default(),
527            generics: RwLock::new(FnvMap::default()),
528            typeids: RwLock::new(FnvMap::default()),
529            interner: RwLock::new(Interner::new()),
530            gc: Mutex::new(Gc::new(Generation::default(), usize::MAX)),
531            macros: MacroEnv::new(),
532            type_cache: TypeCache::default(),
533            generation_0_threads: Default::default(),
534            debug_level: RwLock::new(DebugLevel::default()),
535            thread_reference_count: Default::default(),
536            spawner: self.spawner,
537        };
538        vm.add_types().unwrap();
539        vm
540    }
541}
542
543impl GlobalVmState {
544    pub fn new() -> Self {
545        GlobalVmStateBuilder::new().build()
546    }
547
548    fn add_types(&mut self) -> StdResult<(), (TypeId, ArcType)> {
549        use crate::api::generic::A;
550        use crate::api::Generic;
551        use crate::base::types::BuiltinType;
552        fn add_builtin_type<T: Any>(self_: &mut GlobalVmState, b: BuiltinType) {
553            add_builtin_type_(self_, b, TypeId::of::<T>())
554        }
555        fn add_builtin_type_(self_: &mut GlobalVmState, b: BuiltinType, id: TypeId) {
556            let typ = self_.type_cache.builtin_type(b);
557            add_type(self_, b.to_str(), typ, id)
558        }
559        fn add_type(self_: &mut GlobalVmState, name: &str, typ: ArcType, id: TypeId) {
560            let ids = self_.typeids.get_mut().unwrap();
561            let env = self_.env.get_mut();
562            ids.insert(id, typ);
563            // Insert aliases so that `find_info` can retrieve information about the primitives
564            env.type_infos.id_to_type.insert(
565                name.into(),
566                Alias::from(AliasData::new(
567                    Symbol::from(name),
568                    vec![],
569                    self_.type_cache.opaque(),
570                )),
571            );
572        }
573
574        {
575            let unit = self.type_cache.unit();
576            add_type(self, "()", unit, TypeId::of::<()>());
577            add_builtin_type::<VmInt>(self, BuiltinType::Int);
578            add_builtin_type::<u8>(self, BuiltinType::Byte);
579            add_builtin_type::<f32>(self, BuiltinType::Float);
580            add_builtin_type::<f64>(self, BuiltinType::Float);
581            add_builtin_type::<::std::string::String>(self, BuiltinType::String);
582            add_builtin_type::<char>(self, BuiltinType::Char)
583        }
584        self.register_type::<IO<Generic<A>>>("std.io.IO", &["a"])
585            .unwrap();
586        self.register_type::<Lazy<Generic<A>>>("std.lazy.Lazy", &["a"])
587            .unwrap();
588        self.register_type::<Thread>("std.thread.Thread", &[])
589            .unwrap();
590        Ok(())
591    }
592
593    pub fn type_cache(&self) -> &TypeCache<Symbol, ArcType> {
594        &self.type_cache
595    }
596
597    pub fn new_global_thunk(
598        &self,
599        thread: &Thread,
600        f: CompiledModule,
601    ) -> Result<OpaqueValue<RootedThread, GcPtr<ClosureData>>> {
602        let mut gc = self.gc.lock().unwrap();
603        let env = self.get_env(thread);
604        let mut interner = self.interner.write().unwrap();
605        let byte_code = new_bytecode(&env, &mut interner, &mut gc, self, f)?;
606        Ok(OpaqueValue::from_value(
607            thread.root_value(Variants::from(byte_code)),
608        ))
609    }
610
611    pub fn get_type<T: ?Sized + Any>(&self) -> Option<ArcType> {
612        let id = TypeId::of::<T>();
613        self.typeids.read().unwrap().get(&id).cloned()
614    }
615
616    pub fn get_generic(&self, name: &str) -> ArcType {
617        let mut generics = self.generics.write().unwrap();
618        if let Some(g) = generics.get(name) {
619            return g.clone();
620        }
621        let g: ArcType = Type::generic(Generic::new(Symbol::from(name), Kind::typ()));
622        generics.insert(name.into(), g.clone());
623        g
624    }
625
626    /// Registers a new type called `name`
627    pub fn register_type<T: ?Sized + Any>(&self, name: &str, args: &[&str]) -> Result<ArcType> {
628        self.register_type_(name, args, TypeId::of::<T>())
629    }
630
631    fn register_type_(&self, name: &str, args: &[&str], id: TypeId) -> Result<ArcType> {
632        let arg_types: AppVec<_> = args.iter().map(|g| self.get_generic(g)).collect();
633        let args = arg_types
634            .iter()
635            .map(|g| match **g {
636                Type::Generic(ref g) => g.clone(),
637                _ => unreachable!(),
638            })
639            .collect();
640        let n = Symbol::from(name);
641        let alias = Alias::from(AliasData::new(n.clone(), args, self.type_cache.opaque()));
642        self.register_type_as(n, alias, id)
643    }
644
645    pub fn register_type_as(
646        &self,
647        name: Symbol,
648        alias: Alias<Symbol, ArcType>,
649        id: TypeId,
650    ) -> Result<ArcType> {
651        let mut env = self.env.write();
652        let type_infos = &mut env.type_infos;
653        self.typeids
654            .write()
655            .unwrap()
656            .insert(id, alias.clone().into_type());
657        let t = alias.clone().into_type();
658        type_infos
659            .id_to_type
660            .insert(name.definition_name().into(), alias);
661        Ok(t)
662    }
663
664    #[doc(hidden)]
665    pub fn get_cache_alias(&self, name: &str) -> Option<ArcType> {
666        let env = self.env.read();
667        env
668            .type_infos
669            .id_to_type
670            .get(name)
671            .map(|alias| alias.clone().into_type())
672    }
673
674    #[doc(hidden)]
675    pub fn cache_alias(&self, alias: Alias<Symbol, ArcType>) -> ArcType {
676        let mut env = self.env.write();
677        let type_infos = &mut env.type_infos;
678        let t = alias.clone().into_type();
679        type_infos
680            .id_to_type
681            .insert(alias.name.definition_name().into(), alias);
682        t
683    }
684
685    pub fn get_macros(&self) -> &MacroEnv {
686        &self.macros
687    }
688
689    pub fn intern(&self, s: &str) -> Result<InternedStr> {
690        let mut gc = self.gc.lock().unwrap();
691        let mut interner = self.interner.write().unwrap();
692        interner.intern(&mut *gc, s)
693    }
694
695    /// Returns a borrowed structure which implements `CompilerEnv`
696    pub fn get_env<'t>(&'t self, thread: &'t Thread) -> VmEnvInstance<'t> {
697        let capabilities = self.macros.get_capabilities::<Box<dyn VmEnv>>(thread);
698        VmEnvInstance {
699            vm_envs: capabilities,
700            globals: self.env.read_recursive(),
701            thread,
702        }
703    }
704
705    pub fn get_capability<'t, T>(&'t self, thread: &'t Thread) -> Option<T>
706    where
707        T: Any,
708    {
709        self.macros.get_capability(thread)
710    }
711
712    #[doc(hidden)]
713    pub fn get_lookup_env<'t>(&'t self, thread: &'t Thread) -> VmEnvInstance<'t> {
714        VmEnvInstance {
715            vm_envs: Vec::new(),
716            globals: self.env.read_recursive(),
717            thread,
718        }
719    }
720
721    #[doc(hidden)]
722    pub fn get_globals(&self) -> parking_lot::RwLockReadGuard<Globals> {
723        self.env.read_recursive()
724    }
725
726    pub fn get_debug_level(&self) -> DebugLevel {
727        self.debug_level.read().unwrap().clone()
728    }
729
730    pub fn set_debug_level(&self, debug_level: DebugLevel) {
731        *self.debug_level.write().unwrap() = debug_level;
732    }
733
734    pub fn spawner(&self) -> Option<&(dyn futures::task::Spawn + Send + Sync)> {
735        self.spawner.as_ref().map(|s| &**s)
736    }
737}