Skip to main content

wolfram_library_link/
library_data.rs

1#[cfg(feature = "wstp")]
2use std::thread;
3
4use once_cell::sync::OnceCell;
5
6use crate::sys::{
7    self, mbool, mcomplex, mint, mreal, st_WolframCompileLibrary_Functions,
8    st_WolframIOLibrary_Functions, st_WolframImageLibrary_Functions,
9    st_WolframNumericArrayLibrary_Functions, st_WolframRawArrayLibrary_Functions,
10    st_WolframRuntimeData, st_WolframSparseLibrary_Functions, MArgument, MInputStream,
11    MOutputStream, MTensor, WSENV, WSLINK,
12};
13
14#[derive(Copy, Clone)]
15struct Data {
16    /// The `ThreadId` of the Wolfram Kernel's main thread.
17    ///
18    /// The main evaluation loop of the Wolfram Kernel is largely a single-threaded
19    /// program, and it's functions are not all necessarily designed to be used from
20    /// multiple threads at once. This value, used in [`assert_main_thread()`], is used to
21    /// ensure that the safe API's provided by `wolfram-library-link` are only called from
22    /// the main Kernel thread.
23    #[cfg(feature = "wstp")]
24    main_thread_id: thread::ThreadId,
25    library_data: WolframLibraryData,
26}
27
28static LIBRARY_DATA: OnceCell<Data> = OnceCell::new();
29
30/// Initialize static data for the current Wolfram library.
31///
32/// This function should be called during the execution of the
33/// [`WolframLibrary_initialize()` hook][lib-init]
34/// provided by this library.
35///
36/// This function initializes the lazy Wolfram Runtime Library bindings in the
37/// [`rtl`][`crate::rtl`] module.
38///
39/// # Safety
40///
41/// The following conditions must be met for a call to this function to be valid:
42///
43/// * `data` must be a valid and fully initialized [`sys::WolframLibraryData`] instance
44///   created by the Wolfram Kernel and passed into the current LibraryLink function.
45/// * The call to `initialize()` must happen from the main Kernel thread. This is true for
46///   all LibraryLink functions called directly by the Kernel.
47///
48/// # Relation to [`#[init]`][crate::init]
49///
50/// If the [`#[init]`][crate::init] annotation is used to designate a library
51/// initialization function, `initialize()` will be called automatically.
52///
53/// # Example
54///
55/// *Note: Prefer to use [`#[init]`][crate::init] to designate an initialization function,
56/// instead of manually defining an unsafe initialization function as shown in this
57/// example.*
58///
59/// When a dynamic library is loaded by the Wolfram Language (for example, via
60/// [`LibraryFunctionLoad`](https://reference.wolfram.com/language/ref/LibraryFunctionLoad.html)),
61/// the system will call the function `WolframLibrary_initialize()` if one is provided by
62/// the library. This function is used to initialize callbacks from the library into
63/// functions provided by the Wolfram runtime.
64///
65/// ```
66/// use std::os::raw::c_int;
67/// use wolfram_library_link::{sys, initialize};
68///
69/// #[no_mangle]
70/// extern "C" fn WolframLibrary_initialize(data: sys::WolframLibraryData) -> c_int {
71///     match unsafe { initialize(data) } {
72///         Ok(()) => return 0,
73///         Err(()) => return 1,
74///     }
75/// }
76/// ```
77///
78/// [lib-init]: https://reference.wolfram.com/language/LibraryLink/tutorial/LibraryStructure.html#280210622
79pub unsafe fn initialize(data: sys::WolframLibraryData) -> Result<(), ()> {
80    let library_data = WolframLibraryData::new(data)?;
81
82    let _: Result<(), Data> = LIBRARY_DATA.set(Data {
83        #[cfg(feature = "wstp")]
84        main_thread_id: thread::current().id(),
85        library_data,
86    });
87
88    Ok(())
89}
90
91/// Get the [`WolframLibraryData`] instance recorded by the last call to [`initialize()`].
92///
93/// Prefer to use the lazy function bindings from the [`rtl`][crate::rtl] module instead
94/// of accessing the fields of [`WolframLibraryData`] directly.
95pub fn get_library_data() -> WolframLibraryData {
96    let data: Option<&_> = LIBRARY_DATA.get();
97
98    // TODO: Include a comment here mentioning that the library could/should provide a
99    //       WolframLibrary_initialize() function which calls initialize_library_data()?
100    data.expect(
101        "get_library_data: global Wolfram LIBRARY_DATA static is not initialized.",
102    )
103    .library_data
104}
105
106#[cfg(feature = "wstp")]
107pub(crate) fn is_main_thread() -> bool {
108    let data = LIBRARY_DATA
109        .get()
110        .expect("global LIBRARY_DATA static is not initialized");
111
112    data.main_thread_id == thread::current().id()
113}
114
115/// Assert that the current thread is the main Kernel thread.
116///
117/// # Panics
118///
119/// This function will panic if the current thread is not the main Kernel thread.
120///
121/// Use this function to enforce that callbacks into the Kernel happen from the
122/// main thread.
123#[cfg(feature = "wstp")]
124#[track_caller]
125pub(crate) fn assert_main_thread() {
126    let loc = std::panic::Location::caller();
127
128    assert!(
129        is_main_thread(),
130        "error: attempted to call back into the Wolfram Kernel from a non-main thread at {}:{}",
131        loc.file(),
132        loc.line()
133    );
134}
135
136#[allow(non_snake_case)]
137#[derive(Copy, Clone)]
138#[allow(missing_docs)]
139pub struct WolframLibraryData {
140    pub raw_library_data: sys::WolframLibraryData,
141
142    pub UTF8String_disown: unsafe extern "C" fn(arg1: *mut ::std::os::raw::c_char),
143
144    pub MTensor_new: unsafe extern "C" fn(
145        arg1: mint,
146        arg2: mint,
147        arg3: *const mint,
148        arg4: *mut MTensor,
149    ) -> ::std::os::raw::c_int,
150
151    pub MTensor_free: unsafe extern "C" fn(arg1: MTensor),
152
153    pub MTensor_clone:
154        unsafe extern "C" fn(arg1: MTensor, arg2: *mut MTensor) -> ::std::os::raw::c_int,
155
156    pub MTensor_shareCount: unsafe extern "C" fn(arg1: MTensor) -> mint,
157
158    pub MTensor_disown: unsafe extern "C" fn(arg1: MTensor),
159
160    pub MTensor_disownAll: unsafe extern "C" fn(arg1: MTensor),
161
162    pub MTensor_setInteger: unsafe extern "C" fn(
163        arg1: MTensor,
164        arg2: *mut mint,
165        arg3: mint,
166    ) -> ::std::os::raw::c_int,
167
168    pub MTensor_setReal: unsafe extern "C" fn(
169        arg1: MTensor,
170        arg2: *mut mint,
171        arg3: mreal,
172    ) -> ::std::os::raw::c_int,
173
174    pub MTensor_setComplex: unsafe extern "C" fn(
175        arg1: MTensor,
176        arg2: *mut mint,
177        arg3: mcomplex,
178    ) -> ::std::os::raw::c_int,
179
180    pub MTensor_setMTensor: unsafe extern "C" fn(
181        arg1: MTensor,
182        arg2: MTensor,
183        arg3: *mut mint,
184        arg4: mint,
185    ) -> ::std::os::raw::c_int,
186
187    pub MTensor_getInteger: unsafe extern "C" fn(
188        arg1: MTensor,
189        arg2: *mut mint,
190        arg3: *mut mint,
191    ) -> ::std::os::raw::c_int,
192
193    pub MTensor_getReal: unsafe extern "C" fn(
194        arg1: MTensor,
195        arg2: *mut mint,
196        arg3: *mut mreal,
197    ) -> ::std::os::raw::c_int,
198
199    pub MTensor_getComplex: unsafe extern "C" fn(
200        arg1: MTensor,
201        arg2: *mut mint,
202        arg3: *mut mcomplex,
203    ) -> ::std::os::raw::c_int,
204
205    pub MTensor_getMTensor: unsafe extern "C" fn(
206        arg1: MTensor,
207        arg2: *mut mint,
208        arg3: mint,
209        arg4: *mut MTensor,
210    ) -> ::std::os::raw::c_int,
211
212    pub MTensor_getRank: unsafe extern "C" fn(arg1: MTensor) -> mint,
213    pub MTensor_getDimensions: unsafe extern "C" fn(arg1: MTensor) -> *const mint,
214    pub MTensor_getType: unsafe extern "C" fn(arg1: MTensor) -> mint,
215    pub MTensor_getFlattenedLength: unsafe extern "C" fn(arg1: MTensor) -> mint,
216    pub MTensor_getIntegerData: unsafe extern "C" fn(arg1: MTensor) -> *mut mint,
217    pub MTensor_getRealData: unsafe extern "C" fn(arg1: MTensor) -> *mut mreal,
218    pub MTensor_getComplexData: unsafe extern "C" fn(arg1: MTensor) -> *mut mcomplex,
219
220    pub Message: unsafe extern "C" fn(arg1: *const ::std::os::raw::c_char),
221    pub AbortQ: unsafe extern "C" fn() -> mint,
222
223    pub getWSLINK: unsafe extern "C" fn(arg1: sys::WolframLibraryData) -> WSLINK,
224    pub processWSLINK: unsafe extern "C" fn(arg1: WSLINK) -> ::std::os::raw::c_int,
225
226    pub evaluateExpression: unsafe extern "C" fn(
227        arg1: sys::WolframLibraryData,
228        arg2: *mut ::std::os::raw::c_char,
229        arg3: ::std::os::raw::c_int,
230        arg4: mint,
231        arg5: *mut ::std::os::raw::c_void,
232    ) -> ::std::os::raw::c_int,
233
234    pub runtimeData: *mut st_WolframRuntimeData,
235
236    pub compileLibraryFunctions: *mut st_WolframCompileLibrary_Functions,
237
238    pub VersionNumber: mint,
239
240    pub registerInputStreamMethod: unsafe extern "C" fn(
241        name: *const ::std::os::raw::c_char,
242        ctor: Option<
243            unsafe extern "C" fn(
244                arg1: MInputStream,
245                msgHead: *const ::std::os::raw::c_char,
246                optionsIn: *mut ::std::os::raw::c_void,
247            ),
248        >,
249        handlerTest: Option<
250            unsafe extern "C" fn(
251                arg1: *mut ::std::os::raw::c_void,
252                arg2: *mut ::std::os::raw::c_char,
253            ) -> mbool,
254        >,
255        methodData: *mut ::std::os::raw::c_void,
256        destroyMethod: Option<
257            unsafe extern "C" fn(methodData: *mut ::std::os::raw::c_void),
258        >,
259    ) -> mbool,
260
261    pub unregisterInputStreamMethod:
262        unsafe extern "C" fn(name: *const ::std::os::raw::c_char) -> mbool,
263
264    pub registerOutputStreamMethod: unsafe extern "C" fn(
265        name: *const ::std::os::raw::c_char,
266        ctor: Option<
267            unsafe extern "C" fn(
268                arg1: MOutputStream,
269                msgHead: *const ::std::os::raw::c_char,
270                optionsIn: *mut ::std::os::raw::c_void,
271                appendMode: mbool,
272            ),
273        >,
274        handlerTest: Option<
275            unsafe extern "C" fn(
276                arg1: *mut ::std::os::raw::c_void,
277                arg2: *mut ::std::os::raw::c_char,
278            ) -> mbool,
279        >,
280        methodData: *mut ::std::os::raw::c_void,
281        destroyMethod: Option<
282            unsafe extern "C" fn(methodData: *mut ::std::os::raw::c_void),
283        >,
284    ) -> mbool,
285
286    pub unregisterOutputStreamMethod:
287        unsafe extern "C" fn(name: *const ::std::os::raw::c_char) -> mbool,
288
289    pub ioLibraryFunctions: *mut st_WolframIOLibrary_Functions,
290
291    pub getWSLINKEnvironment:
292        unsafe extern "C" fn(arg1: sys::WolframLibraryData) -> WSENV,
293
294    pub sparseLibraryFunctions: *mut st_WolframSparseLibrary_Functions,
295
296    pub imageLibraryFunctions: *mut st_WolframImageLibrary_Functions,
297
298    pub registerLibraryExpressionManager: unsafe extern "C" fn(
299        mname: *const ::std::os::raw::c_char,
300        mfun: Option<
301            unsafe extern "C" fn(arg1: sys::WolframLibraryData, arg2: mbool, arg3: mint),
302        >,
303    )
304        -> ::std::os::raw::c_int,
305
306    pub unregisterLibraryExpressionManager: unsafe extern "C" fn(
307        mname: *const ::std::os::raw::c_char,
308    )
309        -> ::std::os::raw::c_int,
310
311    pub releaseManagedLibraryExpression: unsafe extern "C" fn(
312        mname: *const ::std::os::raw::c_char,
313        id: mint,
314    )
315        -> ::std::os::raw::c_int,
316
317    pub registerLibraryCallbackManager: unsafe extern "C" fn(
318        name: *const ::std::os::raw::c_char,
319        mfun: Option<
320            unsafe extern "C" fn(
321                arg1: sys::WolframLibraryData,
322                arg2: mint,
323                arg3: MTensor,
324            ) -> mbool,
325        >,
326    )
327        -> ::std::os::raw::c_int,
328
329    pub unregisterLibraryCallbackManager: unsafe extern "C" fn(
330        name: *const ::std::os::raw::c_char,
331    )
332        -> ::std::os::raw::c_int,
333
334    pub callLibraryCallbackFunction: unsafe extern "C" fn(
335        id: mint,
336        ArgC: mint,
337        Args: *mut MArgument,
338        Res: MArgument,
339    ) -> ::std::os::raw::c_int,
340
341    pub releaseLibraryCallbackFunction:
342        unsafe extern "C" fn(id: mint) -> ::std::os::raw::c_int,
343
344    pub validatePath: unsafe extern "C" fn(
345        path: *mut ::std::os::raw::c_char,
346        type_: ::std::os::raw::c_char,
347    ) -> mbool,
348
349    pub protectedModeQ: unsafe extern "C" fn() -> mbool,
350    pub rawarrayLibraryFunctions: *mut st_WolframRawArrayLibrary_Functions,
351    pub numericarrayLibraryFunctions: *mut st_WolframNumericArrayLibrary_Functions,
352    pub setParallelThreadNumber:
353        unsafe extern "C" fn(arg1: ::std::os::raw::c_int) -> ::std::os::raw::c_int,
354    pub restoreParallelThreadNumber: unsafe extern "C" fn(arg1: ::std::os::raw::c_int),
355    pub getParallelThreadNumber: unsafe extern "C" fn() -> ::std::os::raw::c_int,
356}
357
358/// # Safety
359///
360/// The `WolframLibraryData` stucture contains function pointers to functions in the
361/// Wolfram Runtime Library (RTL). Sending function pointers to another thread is not
362/// dangerous; but calling some of the `unsafe` functions from that thread may be.
363/// Therefore, this type should be [`Send`].
364///
365/// Not all of the functions in the Wolfram RTL are safe to call from any thread other
366/// than the main Kernel thread. Therefore, the presense of an instance of
367/// `WolframLibraryData` on a thread other than the main Kernel thread does not imply that
368/// it is safe to call all of the functions listed in this structure from that thread.
369/// Each function is marked unsafe, and has independent safety considerations.
370unsafe impl Send for WolframLibraryData {}
371unsafe impl Sync for WolframLibraryData {}
372
373macro_rules! unwrap_fields {
374    ($raw:expr, $data:expr, [ $($field:ident),+ ]) => {{
375        WolframLibraryData {
376            raw_library_data: $raw,
377            VersionNumber: $data.VersionNumber,
378            runtimeData: $data.runtimeData,
379            compileLibraryFunctions: $data.compileLibraryFunctions,
380            rawarrayLibraryFunctions: $data.rawarrayLibraryFunctions,
381            numericarrayLibraryFunctions: $data.numericarrayLibraryFunctions,
382            sparseLibraryFunctions: $data.sparseLibraryFunctions,
383            imageLibraryFunctions: $data.imageLibraryFunctions,
384            ioLibraryFunctions: $data.ioLibraryFunctions,
385            $($field: $data.$field.unwrap()),+,
386        }
387    }}
388}
389
390impl WolframLibraryData {
391    /// Construct a new `WolframLibraryData` from a [`wolfram_library_link_sys::WolframLibraryData`].
392    pub fn new(data_ptr: sys::WolframLibraryData) -> Result<Self, ()> {
393        if data_ptr.is_null() {
394            return Err(());
395        }
396
397        let data: sys::st_WolframLibraryData = unsafe { *data_ptr };
398
399        Ok(unwrap_fields!(
400            data_ptr,
401            data,
402            [
403                UTF8String_disown,
404                MTensor_new,
405                MTensor_free,
406                MTensor_clone,
407                MTensor_shareCount,
408                MTensor_disown,
409                MTensor_disownAll,
410                MTensor_setInteger,
411                MTensor_setReal,
412                MTensor_setComplex,
413                MTensor_setMTensor,
414                MTensor_getInteger,
415                MTensor_getReal,
416                MTensor_getComplex,
417                MTensor_getMTensor,
418                MTensor_getRank,
419                MTensor_getDimensions,
420                MTensor_getType,
421                MTensor_getFlattenedLength,
422                MTensor_getIntegerData,
423                MTensor_getRealData,
424                MTensor_getComplexData,
425                Message,
426                AbortQ,
427                getWSLINK,
428                processWSLINK,
429                evaluateExpression,
430                registerInputStreamMethod,
431                unregisterInputStreamMethod,
432                registerOutputStreamMethod,
433                unregisterOutputStreamMethod,
434                getWSLINKEnvironment,
435                registerLibraryExpressionManager,
436                unregisterLibraryExpressionManager,
437                releaseManagedLibraryExpression,
438                registerLibraryCallbackManager,
439                unregisterLibraryCallbackManager,
440                callLibraryCallbackFunction,
441                releaseLibraryCallbackFunction,
442                validatePath,
443                protectedModeQ,
444                setParallelThreadNumber,
445                restoreParallelThreadNumber,
446                getParallelThreadNumber
447            ]
448        ))
449    }
450}