llama_cpp_bindings/
llama_backend.rs1use std::sync::atomic::AtomicBool;
2use std::sync::atomic::Ordering::SeqCst;
3
4use llama_cpp_bindings_sys::ggml_log_level;
5
6use crate::LlamaCppError;
7use crate::llama_backend_numa_strategy::NumaStrategy;
8
9#[derive(Eq, PartialEq, Debug)]
10pub struct LlamaBackend {}
11
12static LLAMA_BACKEND_INITIALIZED: AtomicBool = AtomicBool::new(false);
13
14impl LlamaBackend {
15 fn mark_init() -> crate::Result<()> {
16 match LLAMA_BACKEND_INITIALIZED.compare_exchange(false, true, SeqCst, SeqCst) {
17 Ok(_was_uninitialized) => Ok(()),
18 Err(_was_already_initialized) => Err(LlamaCppError::BackendAlreadyInitialized),
19 }
20 }
21
22 pub fn init() -> crate::Result<Self> {
25 Self::mark_init()?;
26 unsafe { llama_cpp_bindings_sys::llama_backend_init() }
27 Ok(Self {})
28 }
29
30 pub fn init_numa(strategy: NumaStrategy) -> crate::Result<Self> {
33 Self::mark_init()?;
34 unsafe {
35 llama_cpp_bindings_sys::llama_numa_init(
36 llama_cpp_bindings_sys::ggml_numa_strategy::from(strategy),
37 );
38 }
39 Ok(Self {})
40 }
41
42 #[must_use]
43 pub fn supports_gpu_offload(&self) -> bool {
44 unsafe { llama_cpp_bindings_sys::llama_supports_gpu_offload() }
45 }
46
47 #[must_use]
48 pub fn supports_mmap(&self) -> bool {
49 unsafe { llama_cpp_bindings_sys::llama_supports_mmap() }
50 }
51
52 #[must_use]
53 pub fn supports_mlock(&self) -> bool {
54 unsafe { llama_cpp_bindings_sys::llama_supports_mlock() }
55 }
56
57 pub fn void_logs(&mut self) {
58 unsafe {
59 llama_cpp_bindings_sys::llama_log_set(Some(void_log), std::ptr::null_mut());
60 }
61 }
62}
63
64const unsafe extern "C" fn void_log(
65 _level: ggml_log_level,
66 _text: *const ::std::os::raw::c_char,
67 _user_data: *mut ::std::os::raw::c_void,
68) {
69}
70
71impl Drop for LlamaBackend {
72 fn drop(&mut self) {
73 LLAMA_BACKEND_INITIALIZED.store(false, SeqCst);
74 unsafe { llama_cpp_bindings_sys::llama_backend_free() }
75 }
76}
77
78#[cfg(test)]
79mod tests {
80 use serial_test::serial;
81
82 use super::LlamaBackend;
83 use crate::LlamaCppError;
84
85 #[test]
86 fn void_log_callback_does_not_panic() {
87 unsafe {
88 super::void_log(
89 llama_cpp_bindings_sys::GGML_LOG_LEVEL_INFO,
90 c"test".as_ptr(),
91 std::ptr::null_mut(),
92 );
93 }
94 }
95
96 #[test]
97 #[serial]
98 fn init_succeeds() {
99 let backend = LlamaBackend::init();
100 assert!(backend.is_ok());
101 }
102
103 #[test]
104 #[serial]
105 fn double_init_returns_error() {
106 let _backend = LlamaBackend::init().unwrap();
107 let second_err = LlamaBackend::init().unwrap_err();
108
109 assert_eq!(
110 std::mem::discriminant(&second_err),
111 std::mem::discriminant(&LlamaCppError::BackendAlreadyInitialized),
112 "expected BackendAlreadyInitialized, got {second_err:?}"
113 );
114 }
115
116 #[test]
117 #[serial]
118 fn init_numa_returns_error_when_backend_already_initialized() {
119 use crate::llama_backend_numa_strategy::NumaStrategy;
120
121 let _backend = LlamaBackend::init().unwrap();
122 let second_err = LlamaBackend::init_numa(NumaStrategy::Disabled).unwrap_err();
123
124 assert_eq!(
125 std::mem::discriminant(&second_err),
126 std::mem::discriminant(&LlamaCppError::BackendAlreadyInitialized),
127 "expected BackendAlreadyInitialized, got {second_err:?}"
128 );
129 }
130
131 #[test]
132 #[serial]
133 fn feature_queries_return_bools() {
134 let backend = LlamaBackend::init().unwrap();
135 let _gpu = backend.supports_gpu_offload();
136 let _mmap = backend.supports_mmap();
137 let _mlock = backend.supports_mlock();
138 }
139
140 #[test]
141 #[serial]
142 fn drop_and_reinit_works() {
143 let backend = LlamaBackend::init().unwrap();
144 drop(backend);
145 let backend = LlamaBackend::init();
146 assert!(backend.is_ok());
147 }
148
149 #[test]
150 #[serial]
151 fn init_numa_succeeds() {
152 use crate::llama_backend_numa_strategy::NumaStrategy;
153
154 let backend = LlamaBackend::init_numa(NumaStrategy::Disabled);
155 assert!(backend.is_ok());
156 }
157}