llama_cpp_bindings/
llama_backend.rs1use crate::LlamaCppError;
4use crate::llama_backend_numa_strategy::NumaStrategy;
5use llama_cpp_bindings_sys::ggml_log_level;
6use std::sync::atomic::AtomicBool;
7use std::sync::atomic::Ordering::SeqCst;
8
9#[derive(Eq, PartialEq, Debug)]
14pub struct LlamaBackend {}
15
16static LLAMA_BACKEND_INITIALIZED: AtomicBool = AtomicBool::new(false);
17
18impl LlamaBackend {
19 fn mark_init() -> crate::Result<()> {
21 match LLAMA_BACKEND_INITIALIZED.compare_exchange(false, true, SeqCst, SeqCst) {
22 Ok(_) => Ok(()),
23 Err(_) => Err(LlamaCppError::BackendAlreadyInitialized),
24 }
25 }
26
27 #[tracing::instrument(skip_all)]
49 pub fn init() -> crate::Result<Self> {
50 Self::mark_init()?;
51 unsafe { llama_cpp_bindings_sys::llama_backend_init() }
52 Ok(Self {})
53 }
54
55 #[tracing::instrument(skip_all)]
71 pub fn init_numa(strategy: NumaStrategy) -> crate::Result<Self> {
72 Self::mark_init()?;
73 unsafe {
74 llama_cpp_bindings_sys::llama_numa_init(
75 llama_cpp_bindings_sys::ggml_numa_strategy::from(strategy),
76 );
77 }
78 Ok(Self {})
79 }
80
81 #[must_use]
83 pub fn supports_gpu_offload(&self) -> bool {
84 unsafe { llama_cpp_bindings_sys::llama_supports_gpu_offload() }
85 }
86
87 #[must_use]
89 pub fn supports_mmap(&self) -> bool {
90 unsafe { llama_cpp_bindings_sys::llama_supports_mmap() }
91 }
92
93 #[must_use]
95 pub fn supports_mlock(&self) -> bool {
96 unsafe { llama_cpp_bindings_sys::llama_supports_mlock() }
97 }
98
99 pub fn void_logs(&mut self) {
101 unsafe {
102 llama_cpp_bindings_sys::llama_log_set(Some(void_log), std::ptr::null_mut());
103 }
104 }
105}
106
107const unsafe extern "C" fn void_log(
108 _level: ggml_log_level,
109 _text: *const ::std::os::raw::c_char,
110 _user_data: *mut ::std::os::raw::c_void,
111) {
112}
113
114impl Drop for LlamaBackend {
130 fn drop(&mut self) {
131 LLAMA_BACKEND_INITIALIZED.store(false, SeqCst);
132 unsafe { llama_cpp_bindings_sys::llama_backend_free() }
133 }
134}
135
136#[cfg(test)]
137mod tests {
138 use serial_test::serial;
139
140 use super::LlamaBackend;
141 use crate::LlamaCppError;
142
143 #[test]
144 fn void_log_callback_does_not_panic() {
145 unsafe {
146 super::void_log(
147 llama_cpp_bindings_sys::GGML_LOG_LEVEL_INFO,
148 c"test".as_ptr(),
149 std::ptr::null_mut(),
150 );
151 }
152 }
153
154 #[test]
155 #[serial]
156 fn init_succeeds() {
157 let backend = LlamaBackend::init();
158 assert!(backend.is_ok());
159 }
160
161 #[test]
162 #[serial]
163 fn double_init_returns_error() {
164 let _backend = LlamaBackend::init().unwrap();
165 let second = LlamaBackend::init();
166 assert_eq!(
167 second.unwrap_err(),
168 LlamaCppError::BackendAlreadyInitialized
169 );
170 }
171
172 #[test]
173 #[serial]
174 fn feature_queries_return_bools() {
175 let backend = LlamaBackend::init().unwrap();
176 let _gpu = backend.supports_gpu_offload();
177 let _mmap = backend.supports_mmap();
178 let _mlock = backend.supports_mlock();
179 }
180
181 #[cfg(feature = "tests_that_use_llms")]
182 #[test]
183 #[serial]
184 fn void_logs_suppresses_output() {
185 let mut backend = LlamaBackend::init().unwrap();
186 backend.void_logs();
187 let model_path = crate::test_model::download_model().unwrap();
189 let model_params = crate::model::params::LlamaModelParams::default();
190 let _model =
191 crate::model::LlamaModel::load_from_file(&backend, model_path, &model_params).unwrap();
192 }
193
194 #[test]
195 #[serial]
196 fn drop_and_reinit_works() {
197 let backend = LlamaBackend::init().unwrap();
198 drop(backend);
199 let backend = LlamaBackend::init();
200 assert!(backend.is_ok());
201 }
202
203 #[test]
204 #[serial]
205 fn init_numa_succeeds() {
206 use crate::llama_backend_numa_strategy::NumaStrategy;
207
208 let backend = LlamaBackend::init_numa(NumaStrategy::Disabled);
209 assert!(backend.is_ok());
210 }
211}