wolfram_library_link/
library_data.rs

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