pub struct SzEnvironmentCore {
is_destroyed: Arc<AtomicBool>,
init_once: Arc<Once>,
init_error: Arc<Mutex<Option<String>>>,
config_mgr_init_once: Arc<Once>,
config_mgr_init_error: Arc<Mutex<Option<String>>>,
product_init_once: Arc<Once>,
product_init_error: Arc<Mutex<Option<String>>>,
module_name: String,
ini_params: String,
verbose_logging: bool,
}Expand description
Core implementation of the SzEnvironment trait.
SzEnvironmentCore manages the lifecycle of the Senzing environment and serves
as a factory for obtaining instances of other SDK components. It implements a
singleton pattern required by the Senzing native library.
§Complete Lifecycle Example
This example shows the full lifecycle: creation, usage, and cleanup.
use sz_rust_sdk::prelude::*;
use std::sync::Arc;
// Get SDK components
let engine = env.get_engine()?;
let product = env.get_product()?;
// Use the SDK
let version = product.get_version()?;
println!("Senzing version: {}", version);
engine.add_record("TEST", "LIFECYCLE_1",
r#"{"NAME_FULL": "John Smith"}"#, None)?;
// Drop components first (they borrow the environment)
drop(engine);
drop(product);
// Destroy releases native resources
env.destroy()?;§Singleton Pattern
The Senzing native library requires a single global instance per process.
get_instance() enforces this by storing one Arc reference in a static
variable. This means:
- All
get_instance()calls with the same parameters return the same instance - The returned
Arcalways hasstrong_count() >= 2(singleton + caller) - Calling
destroy()removes the singleton reference and cleans up native resources only when you hold the last reference
§Thread Safety
SzEnvironmentCore is thread-safe (Send + Sync). The Arc<SzEnvironmentCore>
can be cloned and shared across threads. Each call to get_engine(), etc.,
returns an independent component instance that can be used in its own thread.
use sz_rust_sdk::prelude::*;
let handles: Vec<_> = (0..4).map(|i| {
let env = env.clone();
std::thread::spawn(move || {
let engine = env.get_engine().unwrap();
engine.add_record("TEST", &format!("THREAD_{}", i),
r#"{"NAME_FULL": "Test User"}"#, None)
})
}).collect();
for h in handles { h.join().unwrap()?; }Fields§
§is_destroyed: Arc<AtomicBool>§init_once: Arc<Once>Guards Sz_init() - ensures it runs exactly once and other threads wait
init_error: Arc<Mutex<Option<String>>>Stores any error that occurred during Sz_init
config_mgr_init_once: Arc<Once>Guards SzConfigMgr_init() - ensures it runs exactly once and other threads wait
config_mgr_init_error: Arc<Mutex<Option<String>>>Stores any error that occurred during SzConfigMgr_init
product_init_once: Arc<Once>Guards SzProduct_init() - ensures it runs exactly once and other threads wait
product_init_error: Arc<Mutex<Option<String>>>Stores any error that occurred during SzProduct_init
module_name: String§ini_params: String§verbose_logging: boolImplementations§
Source§impl SzEnvironmentCore
impl SzEnvironmentCore
Sourcepub fn new(
module_name: &str,
ini_params: &str,
verbose_logging: bool,
) -> SzResult<Self>
pub fn new( module_name: &str, ini_params: &str, verbose_logging: bool, ) -> SzResult<Self>
Creates a new SzEnvironment instance
§Arguments
module_name- Name of the module for logging purposesini_params- JSON string containing initialization parametersverbose_logging- Whether to enable verbose logging
Sourcepub fn new_default() -> SzResult<Self>
pub fn new_default() -> SzResult<Self>
Creates a new SzEnvironment instance with default parameters
Sourcepub fn get_instance(
module_name: &str,
ini_params: &str,
verbose_logging: bool,
) -> SzResult<Arc<Self>>
pub fn get_instance( module_name: &str, ini_params: &str, verbose_logging: bool, ) -> SzResult<Arc<Self>>
Gets or creates the global singleton SzEnvironmentCore instance
This method ensures that only one SzEnvironmentCore instance exists per process, which is required by the Senzing SDK.
§Arguments
module_name- Name of the module for logging purposesini_params- JSON string containing initialization parametersverbose_logging- Whether to enable verbose logging
§Arc Reference Count Behavior
Important: The returned Arc<SzEnvironmentCore> will always have
strong_count() >= 2 due to the singleton pattern:
- Reference 1: Stored in
GLOBAL_ENVIRONMENT(the singleton storage) - Reference 2+: Returned to the caller (and any clones they make)
This is by design and is NOT a memory leak. The singleton reference ensures
that subsequent calls to get_instance() return the same instance.
use sz_rust_sdk::prelude::*;
use std::sync::Arc;
assert!(Arc::strong_count(&env) >= 2); // Expected: singleton + caller
let env2 = SzEnvironmentCore::get_existing_instance()?;
assert!(Arc::ptr_eq(&env, &env2)); // Same instance
assert!(Arc::strong_count(&env) >= 3); // singleton + 2 callers§Cleanup
To properly release resources, use destroy() which removes the singleton
reference and calls native cleanup when you hold the last reference:
use sz_rust_sdk::prelude::*;
let env2 = SzEnvironmentCore::get_existing_instance()?;
// Drop all other references first
drop(env2);
// Then destroy - this removes singleton ref and cleans up native resources
env.destroy()?;Sourcepub fn get_existing_instance() -> SzResult<Arc<Self>>
pub fn get_existing_instance() -> SzResult<Arc<Self>>
Gets the existing global singleton SzEnvironmentCore instance
This method returns the existing singleton instance without creating a new one. It will return an error if no instance has been created yet.
§Returns
Returns the existing singleton instance or an error if none exists.
Sourcepub fn try_get_instance() -> Option<Arc<Self>>
pub fn try_get_instance() -> Option<Arc<Self>>
Gets the global singleton instance if it exists
Returns None if no instance has been created yet.
Sourcepub fn destroy(self: Arc<Self>) -> SzResult<()>
pub fn destroy(self: Arc<Self>) -> SzResult<()>
Destroys the environment, consuming the Arc and releasing all native resources.
This method uses Rust’s ownership semantics to ensure safe cleanup:
- Only succeeds if the caller holds the sole reference to the environment
- If other references exist (e.g., other threads still using the environment), returns an error and the environment remains valid
§Ownership Requirements
The Senzing native library is a global resource. Destroying the environment while other code still holds references would cause undefined behavior. This method enforces that you can only destroy when you’re the sole owner.
§Example
use sz_rust_sdk::prelude::*;
// ... use env ...
// When done, destroy (only works if this is the only reference)
env.destroy()?;§Calling destroy() from a Drop Implementation
Because destroy() takes Arc<Self> by value, calling it from a Drop
impl requires moving the Arc out of your struct. Use Option<Arc<...>>
with .take() — this is exactly what SenzingGuard does internally.
use sz_rust_sdk::prelude::*;
use std::sync::Arc;
// CORRECT: Use Option<Arc<...>> + take()
struct MyWrapper {
env: Option<Arc<SzEnvironmentCore>>,
}
impl Drop for MyWrapper {
fn drop(&mut self) {
if let Some(env) = self.env.take() {
let _ = env.destroy();
}
}
}
let wrapper = MyWrapper { env: Some(env) };
drop(wrapper); // Cleanup happens via Dropuse sz_rust_sdk::prelude::*;
use std::sync::Arc;
// WRONG: ptr::read causes double-free / heap corruption
struct MyWrapper {
env: Arc<SzEnvironmentCore>, // Not wrapped in Option!
}
impl Drop for MyWrapper {
fn drop(&mut self) {
// BUG: ptr::read creates a second Arc without incrementing the
// reference count. After destroy() frees the inner data, the
// compiler also drops self.env -> double-free -> heap corruption.
let env = unsafe { std::ptr::read(&self.env) };
let _ = env.destroy();
// <- compiler drops self.env here (use-after-free!)
}
}Note: Using ManuallyDrop instead of ptr::read avoids the
double-free but is unnecessarily complex. Prefer Option::take() or
use SenzingGuard directly — it handles all of this correctly.
§Errors
Returns SzError::Unrecoverable if:
- Other references to the environment still exist
- The environment was already destroyed
Sourcepub fn get_ini_params(&self) -> &str
pub fn get_ini_params(&self) -> &str
Get the initialization parameters used by this environment
Sourcepub fn get_verbose_logging(&self) -> bool
pub fn get_verbose_logging(&self) -> bool
Get the verbose logging setting used by this environment
Sourcefn ensure_initialized(&self) -> SzResult<()>
fn ensure_initialized(&self) -> SzResult<()>
Ensures Sz_init has been called - should be called before any engine operations
This method is thread-safe: the first thread to call this will run Sz_init(), and all other threads will block until initialization is complete.
Sourcefn ensure_config_mgr_initialized(&self) -> SzResult<()>
fn ensure_config_mgr_initialized(&self) -> SzResult<()>
Ensures SzConfigMgr_init has been called - should be called before any config manager operations
This method is thread-safe: the first thread to call this will run SzConfigMgr_init(), and all other threads will block until initialization is complete.
Sourcefn ensure_product_initialized(&self) -> SzResult<()>
fn ensure_product_initialized(&self) -> SzResult<()>
Ensures SzProduct_init has been called - should be called before any product operations
This method is thread-safe: the first thread to call this will run SzProduct_init(), and all other threads will block until initialization is complete.
Trait Implementations§
Source§impl Drop for SzEnvironmentCore
§Drop Behavior - Intentionally Does NOT Clean Up Native Resources
The Drop implementation for SzEnvironmentCore is intentionally a no-op
that only marks the environment as destroyed without releasing native Senzing
resources. This design is deliberate for several important reasons:
impl Drop for SzEnvironmentCore
§Drop Behavior - Intentionally Does NOT Clean Up Native Resources
The Drop implementation for SzEnvironmentCore is intentionally a no-op
that only marks the environment as destroyed without releasing native Senzing
resources. This design is deliberate for several important reasons:
§Why Drop Doesn’t Clean Up
-
Singleton Pattern Complexity: Due to the global singleton pattern, multiple
Arcreferences exist (one inGLOBAL_ENVIRONMENT, one or more held by callers). TheDroptrait cannot detect if this is the “last” reference being dropped. -
Native Library Safety: The Senzing native library has specific destruction ordering requirements. Calling
Sz_destroy()while other threads might still be using the library can cause undefined behavior. -
Controlled Shutdown: Users need explicit control over when native resources are released, especially in applications that may reinitialize with different configurations.
§Proper Cleanup Pattern
Recommended: Use SenzingGuard for automatic RAII cleanup:
use sz_rust_sdk::prelude::*;
// Explicit destroy (ownership-based)
let engine = env.get_engine()?;
// ... use engine ...
drop(engine);
env.destroy()?; // Explicitly release native resourcesIf you need to call destroy() from your own Drop implementation,
see the safety notes on destroy() — you must store
the Arc in an Option and use .take() to avoid double-free bugs.
§What This Implementation Does
- Marks the environment as “destroyed” to prevent further API calls
- Does NOT call
Sz_destroy()or other native cleanup functions - Allows garbage collection of Rust-side resources