Skip to main content

ort/session/builder/
mod.rs

1use alloc::{
2	borrow::Cow,
3	sync::{Arc, Weak},
4	vec::Vec
5};
6use core::{
7	any::Any,
8	ptr::{self, NonNull}
9};
10
11use smallvec::SmallVec;
12
13use crate::{
14	AsPointer, Error, environment::Environment, error::Result, logging::LoggerFunction, memory::MemoryInfo, operator::OperatorDomain, ortsys, util::with_cstr,
15	value::DynValue
16};
17
18#[cfg(feature = "api-22")]
19#[cfg_attr(docsrs, doc(cfg(feature = "api-22")))]
20mod editable;
21mod impl_commit;
22mod impl_config_keys;
23mod impl_options;
24
25#[cfg(feature = "api-22")]
26#[cfg_attr(docsrs, doc(cfg(feature = "api-22")))]
27pub use self::editable::*;
28pub use self::impl_options::*;
29
30/// `Result` type returned by [`SessionBuilder`] methods.
31///
32/// This type supports [error recovery](Error::recover):
33/// ```
34/// # use ort::session::{builder::GraphOptimizationLevel, Session};
35/// # fn main() -> ort::Result<()> {
36/// let session = Session::builder()?
37/// 	.with_optimization_level(GraphOptimizationLevel::All)
38/// 	// Optimization isn't enabled in minimal builds of ONNX Runtime, so throws an error. We can just ignore it.
39/// 	.unwrap_or_else(|e| e.recover())
40/// 	.commit_from_file("tests/data/upsample.onnx")?;
41/// # Ok(())
42/// # }
43/// ```
44pub type BuilderResult = Result<SessionBuilder, Error<SessionBuilder>>;
45
46/// Creates a session using the builder pattern.
47///
48/// Once configured, use the
49/// [`SessionBuilder::commit_from_file`](crate::session::builder::SessionBuilder::commit_from_file) method to 'commit'
50/// the builder configuration into a [`Session`].
51///
52/// ```
53/// # use ort::session::{builder::GraphOptimizationLevel, Session};
54/// # fn main() -> ort::Result<()> {
55/// let session = Session::builder()?
56/// 	.with_optimization_level(GraphOptimizationLevel::Level1)?
57/// 	.with_intra_threads(1)?
58/// 	.commit_from_file("tests/data/upsample.onnx")?;
59/// # Ok(())
60/// # }
61/// ```
62///
63/// [`Session`]: crate::session::Session
64pub struct SessionBuilder {
65	session_options_ptr: Arc<SessionOptionsPointer>,
66	memory_info: Option<Arc<MemoryInfo<'static>>>,
67	operator_domains: SmallVec<[Arc<OperatorDomain>; 1]>,
68	initializers: Vec<Arc<DynValue>>,
69	external_initializer_buffers: Vec<Cow<'static, [u8]>>,
70	prepacked_weights: Option<PrepackedWeights>,
71	thread_manager: Option<Arc<dyn Any>>,
72	logger: Option<Arc<LoggerFunction>>,
73	no_global_thread_pool: bool,
74	no_env_eps: bool,
75	pub(crate) environment: Arc<Environment>
76}
77
78impl Clone for SessionBuilder {
79	fn clone(&self) -> Self {
80		let mut session_options_ptr = ptr::null_mut();
81		ortsys![
82			unsafe CloneSessionOptions(self.ptr(), ptr::addr_of_mut!(session_options_ptr))
83				.expect("error cloning session options");
84			nonNull(session_options_ptr)
85		];
86		Self {
87			session_options_ptr: Arc::new(SessionOptionsPointer::new(session_options_ptr)),
88			memory_info: self.memory_info.clone(),
89			operator_domains: self.operator_domains.clone(),
90			initializers: self.initializers.clone(),
91			external_initializer_buffers: self.external_initializer_buffers.clone(),
92			prepacked_weights: self.prepacked_weights.clone(),
93			thread_manager: self.thread_manager.clone(),
94			logger: self.logger.clone(),
95			no_global_thread_pool: self.no_global_thread_pool,
96			no_env_eps: self.no_env_eps,
97			environment: self.environment.clone()
98		}
99	}
100}
101
102impl SessionBuilder {
103	/// Creates a new session builder.
104	///
105	/// ```
106	/// # use ort::session::{builder::GraphOptimizationLevel, Session};
107	/// # fn main() -> ort::Result<()> {
108	/// let session = Session::builder()?
109	/// 	.with_optimization_level(GraphOptimizationLevel::Level1)?
110	/// 	.with_intra_threads(1)?
111	/// 	.commit_from_file("tests/data/upsample.onnx")?;
112	/// # Ok(())
113	/// # }
114	/// ```
115	pub fn new() -> Result<Self> {
116		let environment = Environment::current()?;
117
118		let mut session_options_ptr: *mut ort_sys::OrtSessionOptions = ptr::null_mut();
119		ortsys![unsafe CreateSessionOptions(&mut session_options_ptr)?; nonNull(session_options_ptr)];
120
121		// target on-device usage; prefer efficiency by default
122		// .with_execution_providers/.with_auto_ep will override this
123		#[cfg(feature = "api-22")]
124		let _ = ortsys![@ort: unsafe SessionOptionsSetEpSelectionPolicy(session_options_ptr.as_ptr(), AutoDevicePolicy::MaxEfficiency.into()) as Result];
125
126		Ok(Self {
127			session_options_ptr: Arc::new(SessionOptionsPointer::new(session_options_ptr)),
128			memory_info: None,
129			operator_domains: SmallVec::new(),
130			initializers: Vec::new(),
131			external_initializer_buffers: Vec::new(),
132			prepacked_weights: None,
133			thread_manager: None,
134			logger: None,
135			no_global_thread_pool: false,
136			no_env_eps: false,
137			environment
138		})
139	}
140
141	#[inline]
142	pub(crate) fn add_config_entry(&mut self, key: impl AsRef<str>, value: impl AsRef<str>) -> Result<()> {
143		let ptr = self.ptr_mut();
144		with_cstr(key.as_ref().as_bytes(), &|key| {
145			with_cstr(value.as_ref().as_bytes(), &|value| {
146				ortsys![unsafe AddSessionConfigEntry(ptr, key.as_ptr(), value.as_ptr())?];
147				Ok(())
148			})
149		})
150	}
151
152	/// Creates a signaler that can be used from another thread to cancel any in-progress commits.
153	///
154	/// ```
155	/// # use ort::session::{builder::GraphOptimizationLevel, Session};
156	/// # use std::{thread, time::Duration};
157	/// # fn main() -> ort::Result<()> {
158	/// let mut builder = Session::builder()?
159	/// 	.with_optimization_level(GraphOptimizationLevel::Level1)?
160	/// 	.with_intra_threads(1)?;
161	///
162	/// let canceler = builder.canceler();
163	/// thread::spawn(move || {
164	/// 	thread::sleep(Duration::from_millis(500));
165	/// 	// timeout if model hasn't loaded in 500ms
166	/// 	let _ = canceler.cancel();
167	/// });
168	///
169	/// let session = builder.commit_from_file("tests/data/upsample.onnx")?;
170	/// # Ok(())
171	/// # }
172	/// ```
173	#[cfg(feature = "api-22")]
174	#[cfg_attr(docsrs, doc(cfg(feature = "api-22")))]
175	pub fn canceler(&self) -> LoadCanceler {
176		LoadCanceler(Arc::downgrade(&self.session_options_ptr))
177	}
178
179	/// Adds a custom configuration entry to the session.
180	pub fn with_config_entry(mut self, key: impl AsRef<str>, value: impl AsRef<str>) -> BuilderResult {
181		match self.add_config_entry(key.as_ref(), value.as_ref()) {
182			Ok(()) => Ok(self),
183			Err(e) => Err(e.with_recover(self))
184		}
185	}
186}
187
188impl AsPointer for SessionBuilder {
189	type Sys = ort_sys::OrtSessionOptions;
190
191	fn ptr(&self) -> *const Self::Sys {
192		self.session_options_ptr.as_ptr()
193	}
194}
195
196/// A handle which can be used to remotely terminate an in-progress session load.
197///
198/// See [`SessionBuilder::canceler`].
199#[derive(Debug, Clone)]
200#[cfg(feature = "api-22")]
201#[cfg_attr(docsrs, doc(cfg(feature = "api-22")))]
202pub struct LoadCanceler(Weak<SessionOptionsPointer>);
203
204#[cfg(feature = "api-22")]
205unsafe impl Send for LoadCanceler {}
206#[cfg(feature = "api-22")]
207unsafe impl Sync for LoadCanceler {}
208
209#[cfg(feature = "api-22")]
210impl LoadCanceler {
211	/// Cancels any active session commits.
212	///
213	/// ```
214	/// # use ort::session::{builder::GraphOptimizationLevel, Session};
215	/// # use std::{thread, time::Duration};
216	/// # fn main() -> ort::Result<()> {
217	/// let mut builder = Session::builder()?
218	/// 	.with_optimization_level(GraphOptimizationLevel::Level1)?
219	/// 	.with_intra_threads(1)?;
220	///
221	/// let canceler = builder.canceler();
222	/// thread::spawn(move || {
223	/// 	thread::sleep(Duration::from_millis(500));
224	/// 	// timeout if model hasn't loaded in 500ms
225	/// 	let _ = canceler.cancel();
226	/// });
227	///
228	/// let session = builder.commit_from_file("tests/data/upsample.onnx")?;
229	/// # Ok(())
230	/// # }
231	/// ```
232	#[cfg(feature = "api-22")]
233	#[cfg_attr(docsrs, doc(cfg(feature = "api-22")))]
234	pub fn cancel(&self) -> Result<()> {
235		if let Some(ptr) = self.0.upgrade() {
236			ortsys![unsafe SessionOptionsSetLoadCancellationFlag(ptr.as_ptr(), true)?];
237		}
238		Ok(())
239	}
240}
241
242#[derive(Debug)]
243#[repr(transparent)]
244pub(crate) struct SessionOptionsPointer(NonNull<ort_sys::OrtSessionOptions>);
245
246impl SessionOptionsPointer {
247	#[inline]
248	pub(crate) fn new(ptr: NonNull<ort_sys::OrtSessionOptions>) -> Self {
249		crate::logging::create!(SessionBuilder, ptr);
250		Self(ptr)
251	}
252
253	#[inline]
254	pub(crate) fn as_ptr(&self) -> *mut ort_sys::OrtSessionOptions {
255		self.0.as_ptr()
256	}
257}
258
259impl Drop for SessionOptionsPointer {
260	fn drop(&mut self) {
261		ortsys![unsafe ReleaseSessionOptions(self.0.as_ptr())];
262		crate::logging::drop!(SessionBuilder, self.0.as_ptr());
263	}
264}
265
266#[cfg(test)]
267mod tests {
268	use alloc::sync::Arc;
269	use core::sync::atomic::{AtomicBool, Ordering};
270
271	use super::SessionBuilder;
272
273	#[test]
274	fn test_session_builder_clone() -> crate::Result<()> {
275		let was_called = Arc::new(AtomicBool::new(false));
276		let builder = SessionBuilder::new()?.with_logger(Arc::new({
277			let was_called = Arc::clone(&was_called);
278			move |_level: crate::logging::LogLevel, _category: &str, _id: &str, _code_location: &str, _message: &str| {
279				was_called.store(true, Ordering::Release);
280			}
281		}))?;
282		let mut builder2 = builder.clone();
283		drop(builder);
284		let _session = builder2.commit_from_file("tests/data/upsample.onnx")?;
285		assert!(was_called.load(Ordering::Acquire));
286		Ok(())
287	}
288}