bp3d_os/module/loader/
core.rs

1// Copyright (c) 2025, BlockProject 3D
2//
3// All rights reserved.
4//
5// Redistribution and use in source and binary forms, with or without modification,
6// are permitted provided that the following conditions are met:
7//
8//     * Redistributions of source code must retain the above copyright notice,
9//       this list of conditions and the following disclaimer.
10//     * Redistributions in binary form must reproduce the above copyright notice,
11//       this list of conditions and the following disclaimer in the documentation
12//       and/or other materials provided with the distribution.
13//     * Neither the name of BlockProject 3D nor the names of its contributors
14//       may be used to endorse or promote products derived from this software
15//       without specific prior written permission.
16//
17// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
21// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
29use crate::module::error::Error;
30use crate::module::library::types::{OsLibrary, VirtualLibrary};
31use crate::module::library::OS_EXT;
32use crate::module::loader::util::{load_by_symbol, load_lib, module_close, Dependency, DepsMap};
33use crate::module::loader::Lock;
34use crate::module::Module;
35use bp3d_debug::{debug, error};
36use std::collections::HashMap;
37use std::path::{Path, PathBuf};
38use std::sync::atomic::Ordering::SeqCst;
39use std::sync::atomic::{AtomicBool, AtomicPtr};
40use std::sync::{Mutex, MutexGuard};
41
42struct Data {
43    loader: AtomicPtr<Mutex<ModuleLoader>>,
44    is_root: AtomicBool,
45}
46
47impl Data {
48    fn install(&self, loader: ModuleLoader) -> bool {
49        let ptr = self.loader.load(SeqCst);
50        if ptr.is_null() {
51            self.loader
52                .store(Box::leak(Box::new(Mutex::new(loader))), SeqCst);
53            self.is_root.store(true, SeqCst);
54            true
55        } else {
56            false
57        }
58    }
59
60    fn install_existing(&self, loader: &'static Mutex<ModuleLoader>) -> bool {
61        let ptr = self.loader.load(SeqCst);
62        if ptr.is_null() {
63            self.is_root.store(false, SeqCst);
64            self.loader
65                .store(loader as *const Mutex<ModuleLoader> as *mut _, SeqCst);
66            true
67        } else {
68            false
69        }
70    }
71
72    fn is_root(&self) -> bool {
73        self.is_root.load(SeqCst)
74    }
75
76    fn reset(&self) {
77        self.loader.store(std::ptr::null_mut(), SeqCst);
78        self.is_root.store(false, SeqCst);
79    }
80
81    fn is_set(&self) -> bool {
82        !self.loader.load(SeqCst).is_null()
83    }
84
85    // This is only safe if this is set.
86    unsafe fn get(&self) -> &'static Mutex<ModuleLoader> {
87        let ptr = self.loader.load(SeqCst);
88        unsafe { &*ptr }
89    }
90}
91
92static MODULE_LOADER: Data = Data {
93    loader: AtomicPtr::new(std::ptr::null_mut()),
94    is_root: AtomicBool::new(false),
95};
96
97/// Represents a module loader which can support loading multiple related modules.
98pub struct ModuleLoader {
99    paths: Vec<PathBuf>,
100    pub(super) modules: HashMap<usize, Module<OsLibrary>>,
101    pub(super) builtin_modules: HashMap<usize, Module<VirtualLibrary>>,
102    deps: DepsMap,
103    builtins: &'static [&'static VirtualLibrary],
104    module_name_to_id: HashMap<String, usize>,
105    last_module_id: usize,
106}
107
108impl ModuleLoader {
109    /// Create a new instance of a [ModuleLoader] and installs it as this application's
110    /// [ModuleLoader].
111    pub fn install(builtins: &'static [&'static VirtualLibrary]) {
112        debug!("Installing new ModuleLoader...");
113        let mut this = ModuleLoader {
114            paths: Default::default(),
115            modules: Default::default(),
116            deps: DepsMap::new(),
117            builtin_modules: Default::default(),
118            builtins,
119            module_name_to_id: Default::default(),
120            last_module_id: 0,
121        };
122        this._add_public_dependency(env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION"), ["*"]);
123        this._add_public_dependency("bp3d-debug", "1.0.0", ["*"]);
124        if !MODULE_LOADER.install(this) {
125            panic!("attempt to initialize module loader twice");
126        }
127    }
128
129    /// Uninstall the application's [ModuleLoader]. This function will panic if this module did not
130    /// install a [ModuleLoader] but is rather sharing the instance of a different application.
131    pub fn uninstall() {
132        debug!("Uninstalling ModuleLoader...");
133        if !MODULE_LOADER.is_set() {
134            panic!("attempt to uninstall a non-existent ModuleLoader");
135        }
136        if !MODULE_LOADER.is_root() {
137            MODULE_LOADER.reset()
138        } else {
139            debug!("Unloading modules...");
140            let mut loader = Self::_lock();
141            let map = loader.module_name_to_id.clone();
142            for (name, _) in map {
143                debug!("Unloading module {}...", name);
144                if let Err(e) = loader._unload(&name) {
145                    error!("Failed to unload module {}: {}", name, e);
146                }
147            }
148            drop(loader);
149            debug!("Deleting ModuleLoader...");
150            unsafe {
151                drop(Box::from_raw(
152                    MODULE_LOADER.get() as *const Mutex<ModuleLoader> as *mut Mutex<ModuleLoader>,
153                ));
154            }
155            MODULE_LOADER.reset();
156        }
157    }
158
159    pub(crate) fn _instance() -> &'static Mutex<ModuleLoader> {
160        unsafe { MODULE_LOADER.get() }
161    }
162
163    /// Installs a default [ModuleLoader] for this application.
164    pub fn install_default() {
165        Self::install(&[]);
166    }
167
168    /// Install the [ModuleLoader] of this module to an existing instance.
169    pub fn install_from_existing(loader: &'static Mutex<ModuleLoader>) {
170        if MODULE_LOADER.install_existing(loader) {
171            debug!("Installed ModuleLoader from existing instance");
172        }
173        assert_eq!(loader as *const Mutex<ModuleLoader>, unsafe {
174            MODULE_LOADER.get() as *const Mutex<ModuleLoader>
175        });
176    }
177
178    fn _lock<'a>() -> MutexGuard<'a, ModuleLoader> {
179        if !MODULE_LOADER.is_set() {
180            Self::install_default();
181        }
182        unsafe { MODULE_LOADER.get().lock().unwrap() }
183    }
184
185    fn _next_module_id(&mut self) -> usize {
186        let id = self.last_module_id;
187        self.last_module_id += 1;
188        id
189    }
190
191    pub(super) fn _get_builtin(&self, name: &str) -> Option<usize> {
192        let name = name.replace("-", "_");
193        if let Some(id) = self.module_name_to_id.get(&name) {
194            self.builtin_modules.get(id).map(|v| v.id)
195        } else {
196            None
197        }
198    }
199    pub(super) fn _get_module(&self, name: &str) -> Option<usize> {
200        let name = name.replace("-", "_");
201        if let Some(id) = self.module_name_to_id.get(&name) {
202            self.modules.get(id).map(|v| v.id)
203        } else {
204            None
205        }
206    }
207
208    pub(super) unsafe fn _load_builtin(&mut self, name: &str) -> crate::module::Result<usize> {
209        debug!("Loading builtin module: {}", name);
210        let name = name.replace("-", "_");
211        if let Some(id) = self.module_name_to_id.get(&name) {
212            match self.builtin_modules.get_mut(id) {
213                Some(v) => {
214                    v.ref_count += 1;
215                    Ok(*id)
216                }
217                None => Err(Error::NotFound(name)),
218            }
219        } else {
220            for builtin in self.builtins {
221                if builtin.name() == name {
222                    let mut module = unsafe { load_by_symbol(**builtin, &name, &mut self.deps) }
223                        .map_err(|e| match e {
224                            Error::NotFound(_) => Error::MissingMetadata,
225                            e => e,
226                        })?;
227                    let id = self._next_module_id();
228                    module.id = id;
229                    self.module_name_to_id.insert(name, id);
230                    self.builtin_modules.entry(id).or_insert(module);
231                    return Ok(id);
232                }
233            }
234            Err(Error::NotFound(name))
235        }
236    }
237
238    pub(super) unsafe fn _load_self(&mut self, name: &str) -> crate::module::Result<usize> {
239        debug!("Loading static module: {}", name);
240        let name = name.replace("-", "_");
241        if let Some(id) = self.module_name_to_id.get(&name) {
242            match self.modules.get_mut(id) {
243                Some(v) => {
244                    v.ref_count += 1;
245                    Ok(*id)
246                }
247                None => Err(Error::NotFound(name)),
248            }
249        } else {
250            let this = OsLibrary::open_self()?;
251            let mut module = unsafe { load_by_symbol(this, &name, &mut self.deps) }?;
252            let id = self._next_module_id();
253            module.id = id;
254            self.module_name_to_id.insert(name, id);
255            self.modules.entry(id).or_insert(module);
256            Ok(id)
257        }
258    }
259
260    pub(super) unsafe fn _load(&mut self, name: &str) -> crate::module::Result<usize> {
261        debug!("Loading dynamic module: {}", name);
262        let name = name.replace("-", "_");
263        if let Some(id) = self.module_name_to_id.get(&name) {
264            match self.modules.get_mut(id) {
265                Some(v) => {
266                    v.ref_count += 1;
267                    Ok(*id)
268                }
269                None => Err(Error::NotFound(name)),
270            }
271        } else {
272            let name2 = format!("{}.{}", name, OS_EXT);
273            let name3 = format!("lib{}.{}", name, OS_EXT);
274            for path in self.paths.iter() {
275                let search = path.join(&name2);
276                let search2 = path.join(&name3);
277                let mut module = None;
278                if search.exists() {
279                    module = Some(load_lib(&mut self.deps, &name, &search)?);
280                } else if search2.exists() {
281                    module = Some(load_lib(&mut self.deps, &name, &search2)?);
282                }
283                if let Some(mut module) = module {
284                    let id = self._next_module_id();
285                    module.id = id;
286                    self.module_name_to_id.insert(name, id);
287                    self.modules.insert(id, module);
288                    return Ok(id);
289                }
290            }
291            Err(Error::NotFound(name))
292        }
293    }
294
295    pub(super) fn _unload(&mut self, name: &str) -> crate::module::Result<()> {
296        debug!("Unloading module: {}", name);
297        let name = name.replace("-", "_");
298        let id = self
299            .module_name_to_id
300            .get(&name)
301            .copied()
302            .ok_or_else(|| Error::NotFound(name.clone()))?;
303        if self.modules.contains_key(&id) {
304            let module = self.modules.get_mut(&id).unwrap();
305            module.ref_count -= 1;
306            if module.ref_count == 0 {
307                self.module_name_to_id.remove(&name);
308                let module = unsafe { self.modules.remove(&id).unwrap_unchecked() };
309                unsafe { module_close(&name, false, &module) }?;
310                drop(module);
311            }
312        } else {
313            let module = self
314                .builtin_modules
315                .get_mut(&id)
316                .ok_or_else(|| Error::NotFound(name.clone()))?;
317            module.ref_count -= 1;
318            if module.ref_count == 0 {
319                self.module_name_to_id.remove(&name);
320                let module = unsafe { self.builtin_modules.remove(&id).unwrap_unchecked() };
321                unsafe { module_close(&name, true, &module) }?;
322                drop(module);
323            }
324        }
325        Ok(())
326    }
327
328    pub(super) fn _add_search_path(&mut self, path: impl AsRef<Path>) {
329        self.paths.push(path.as_ref().into());
330    }
331
332    pub(super) fn _remove_search_path(&mut self, path: impl AsRef<Path>) {
333        self.paths.retain(|p| p != path.as_ref());
334    }
335
336    pub(super) fn _add_public_dependency<'a>(
337        &mut self,
338        name: &str,
339        version: &str,
340        features: impl IntoIterator<Item = &'a str>,
341    ) {
342        let mut negative_features = Vec::new();
343        let features = features
344            .into_iter()
345            .filter_map(|s| {
346                if s.starts_with("-") {
347                    negative_features.push(s.into());
348                    return None;
349                }
350                if s != "*" {
351                    Some(String::from(name) + s)
352                } else {
353                    Some("*".into())
354                }
355            })
356            .collect();
357        self.deps.add_dep(
358            name.replace("-", "_"),
359            Dependency {
360                version: version.into(),
361                features,
362                negative_features,
363            },
364        )
365    }
366
367    /// Lock the [ModuleLoader] installed for the application and returns a lock which is used to
368    /// operate the [ModuleLoader].
369    pub fn lock<'a>() -> Lock<'a> {
370        Lock {
371            lock: Self::_lock(),
372        }
373    }
374}