From 0ee50df7fdb000e4f9ffdac2297b086b1ced7ecb Mon Sep 17 00:00:00 2001 From: "Steve Lee (POWERSHELL HE/HIM) (from Dev Box)" Date: Thu, 25 Sep 2025 13:08:47 -0700 Subject: [PATCH 1/8] Add static memory cache for discovered resources --- dsc_lib/src/discovery/command_discovery.rs | 72 ++++++++++--------- .../src/discovery/command_discovery_cache.rs | 71 ++++++++++++++++++ dsc_lib/src/discovery/mod.rs | 1 + 3 files changed, 109 insertions(+), 35 deletions(-) create mode 100644 dsc_lib/src/discovery/command_discovery_cache.rs diff --git a/dsc_lib/src/discovery/command_discovery.rs b/dsc_lib/src/discovery/command_discovery.rs index a576c4323..b50a74975 100644 --- a/dsc_lib/src/discovery/command_discovery.rs +++ b/dsc_lib/src/discovery/command_discovery.rs @@ -1,7 +1,13 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -use crate::discovery::discovery_trait::{DiscoveryFilter, DiscoveryKind, ResourceDiscovery}; +use crate::discovery::{ + command_discovery_cache::{ + get_adapted_resource, get_adapted_resources, get_adapters, get_extensions, get_resource, get_resources, resources_is_empty, + adapters_is_empty, extensions_is_empty, extend_adapted_resources, extend_adapters, extend_extensions, extend_resources + }, + discovery_trait::{DiscoveryFilter, DiscoveryKind, ResourceDiscovery} +}; use crate::dscresources::dscresource::{Capability, DscResource, ImplementedAs}; use crate::dscresources::resource_manifest::{import_manifest, validate_semver, Kind, ResourceManifest, SchemaKind}; use crate::dscresources::command_resource::invoke_command; @@ -36,13 +42,9 @@ pub enum ImportedManifest { Extension(DscExtension), } + #[derive(Clone)] pub struct CommandDiscovery { - // use BTreeMap so that the results are sorted by the typename, the Vec is sorted by version - adapters: BTreeMap>, - resources: BTreeMap>, - extensions: BTreeMap, - adapted_resources: BTreeMap>, progress_format: ProgressFormat, } @@ -72,17 +74,13 @@ impl CommandDiscovery { #[must_use] pub fn new(progress_format: ProgressFormat) -> CommandDiscovery { CommandDiscovery { - adapters: BTreeMap::new(), - resources: BTreeMap::new(), - extensions: BTreeMap::new(), - adapted_resources: BTreeMap::new(), progress_format, } } #[must_use] - pub fn get_extensions(&self) -> &BTreeMap { - &self.extensions + pub fn get_extensions(&self) -> BTreeMap { + get_extensions() } fn get_resource_path_setting() -> Result @@ -307,7 +305,7 @@ impl ResourceDiscovery for CommandDiscovery { match kind { DiscoveryKind::Resource => { // Now we need to call discover extensions and add those resource to the list of resources - for extension in self.extensions.values() { + for extension in get_extensions().values() { if extension.capabilities.contains(&ExtensionCapability::Discover) { debug!("{}", t!("discovery.commandDiscovery.callingExtension", extension = extension.type_name)); let discovered_resources = extension.discover()?; @@ -320,11 +318,11 @@ impl ResourceDiscovery for CommandDiscovery { } } } - self.adapters = adapters; - self.resources = resources; + extend_adapters(adapters); + extend_resources(resources); }, DiscoveryKind::Extension => { - self.extensions = extensions; + extend_extensions(extensions); } } @@ -332,14 +330,15 @@ impl ResourceDiscovery for CommandDiscovery { } fn discover_adapted_resources(&mut self, name_filter: &str, adapter_filter: &str) -> Result<(), DscError> { - if self.resources.is_empty() && self.adapters.is_empty() { + if resources_is_empty() && adapters_is_empty() { self.discover(&DiscoveryKind::Resource, "*")?; } - if self.adapters.is_empty() { + if adapters_is_empty() { return Ok(()); } + let adapters = get_adapters(); let regex_str = convert_wildcard_to_regex(adapter_filter); debug!("Using regex {regex_str} as filter for adapter name"); let mut regex_builder = RegexBuilder::new(®ex_str); @@ -356,13 +355,13 @@ impl ResourceDiscovery for CommandDiscovery { return Err(DscError::Operation("Could not build Regex filter for resource name".to_string())); }; - let mut progress = ProgressBar::new(self.adapters.len() as u64, self.progress_format)?; + let mut progress = ProgressBar::new(adapters.len() as u64, self.progress_format)?; progress.write_activity("Searching for adapted resources"); let mut adapted_resources = BTreeMap::>::new(); let mut found_adapter: bool = false; - for (adapter_name, adapters) in &self.adapters { + for (adapter_name, adapters) in &adapters { for adapter in adapters { progress.write_increment(1); @@ -431,7 +430,7 @@ impl ResourceDiscovery for CommandDiscovery { return Err(DscError::AdapterNotFound(adapter_filter.to_string())); } - self.adapted_resources = adapted_resources; + extend_adapted_resources(adapted_resources); Ok(()) } @@ -441,10 +440,10 @@ impl ResourceDiscovery for CommandDiscovery { if *kind == DiscoveryKind::Resource { if adapter_name_filter.is_empty() { self.discover(kind, type_name_filter)?; - for (resource_name, resources_vec) in &self.resources { + for (resource_name, resources_vec) in &get_resources() { resources.insert(resource_name.clone(), resources_vec.iter().map(|r| ImportedManifest::Resource(r.clone())).collect()); } - for (adapter_name, adapter_vec) in &self.adapters { + for (adapter_name, adapter_vec) in &get_adapters() { resources.insert(adapter_name.clone(), adapter_vec.iter().map(|r| ImportedManifest::Resource(r.clone())).collect()); } } else { @@ -452,15 +451,16 @@ impl ResourceDiscovery for CommandDiscovery { self.discover_adapted_resources(type_name_filter, adapter_name_filter)?; // add/update found adapted resources to the lookup_table - add_resources_to_lookup_table(&self.adapted_resources); + let adapted_resources = get_adapted_resources(); + add_resources_to_lookup_table(&adapted_resources); - for (adapted_name, adapted_vec) in &self.adapted_resources { + for (adapted_name, adapted_vec) in &adapted_resources { resources.insert(adapted_name.clone(), adapted_vec.iter().map(|r| ImportedManifest::Resource(r.clone())).collect()); } } } else { self.discover(kind, type_name_filter)?; - for (extension_name, extension) in &self.extensions { + for (extension_name, extension) in &get_extensions() { resources.insert(extension_name.clone(), vec![ImportedManifest::Extension(extension.clone())]); } } @@ -470,7 +470,9 @@ impl ResourceDiscovery for CommandDiscovery { fn find_resources(&mut self, required_resource_types: &[DiscoveryFilter]) -> Result>, DscError> { debug!("{}", t!("discovery.commandDiscovery.searchingForResources", resources = required_resource_types : {:?})); - self.discover( &DiscoveryKind::Resource, "*")?; + if resources_is_empty() { + self.discover( &DiscoveryKind::Resource, "*")?; + } let mut found_resources = BTreeMap::>::new(); let mut required_resources = HashMap::::new(); for filter in required_resource_types { @@ -478,8 +480,8 @@ impl ResourceDiscovery for CommandDiscovery { } for filter in required_resource_types { - if let Some(resources) = self.resources.get(filter.resource_type()) { - filter_resources(&mut found_resources, &mut required_resources, resources, filter); + if let Some(resources) = get_resource(filter.resource_type()) { + filter_resources(&mut found_resources, &mut required_resources, &resources, filter); } if required_resources.values().all(|&v| v) { break; @@ -492,12 +494,12 @@ impl ResourceDiscovery for CommandDiscovery { } // now go through the adapters, this is for implicit adapters so version can't be specified so use latest version - for adapter_name in self.adapters.clone().keys() { + for adapter_name in get_adapters().keys() { self.discover_adapted_resources("*", adapter_name)?; - add_resources_to_lookup_table(&self.adapted_resources); + add_resources_to_lookup_table(&get_adapted_resources()); for filter in required_resource_types { - if let Some(adapted_resources) = self.adapted_resources.get(filter.resource_type()) { - filter_resources(&mut found_resources, &mut required_resources, adapted_resources, filter); + if let Some(adapted_resources) = get_adapted_resource(filter.resource_type()) { + filter_resources(&mut found_resources, &mut required_resources, &adapted_resources, filter); } if required_resources.values().all(|&v| v) { break; @@ -512,10 +514,10 @@ impl ResourceDiscovery for CommandDiscovery { } fn get_extensions(&mut self) -> Result, DscError> { - if self.extensions.is_empty() { + if extensions_is_empty() { self.discover(&DiscoveryKind::Extension, "*")?; } - Ok(self.extensions.clone()) + Ok(get_extensions()) } } diff --git a/dsc_lib/src/discovery/command_discovery_cache.rs b/dsc_lib/src/discovery/command_discovery_cache.rs new file mode 100644 index 000000000..bcdf8dc56 --- /dev/null +++ b/dsc_lib/src/discovery/command_discovery_cache.rs @@ -0,0 +1,71 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use crate::dscresources::dscresource::DscResource; +use crate::extensions::dscextension::DscExtension; +use std::collections::BTreeMap; +use std::sync::{LazyLock, Mutex}; + +// use BTreeMap so that the results are sorted by the typename, the Vec is sorted by version +static ADAPTERS : LazyLock>>> = LazyLock::new(|| Mutex::new(BTreeMap::new())); +static RESOURCES : LazyLock>>> = LazyLock::new(|| Mutex::new(BTreeMap::new())); +static EXTENSIONS : LazyLock>> = LazyLock::new(|| Mutex::new(BTreeMap::new())); +static ADAPTED_RESOURCES : LazyLock>>> = LazyLock::new(|| Mutex::new(BTreeMap::new())); + +pub fn get_adapters() -> BTreeMap> { + ADAPTERS.lock().unwrap().clone() +} + +pub fn get_adapted_resources() -> BTreeMap> { + ADAPTED_RESOURCES.lock().unwrap().clone() +} + +pub fn get_adapted_resource(type_name: &str) -> Option> { + if let Some(resources) = ADAPTED_RESOURCES.lock().unwrap().get(type_name) { + return Some(resources.clone()); + } + None +} + +pub fn get_extensions() -> BTreeMap { + EXTENSIONS.lock().unwrap().clone() +} + +pub fn get_resources() -> BTreeMap> { + RESOURCES.lock().unwrap().clone() +} + +pub fn get_resource(type_name: &str) -> Option> { + if let Some(resources) = RESOURCES.lock().unwrap().get(type_name) { + return Some(resources.clone()); + } + None +} + +pub fn extend_adapters(new_adapters: BTreeMap>) { + ADAPTERS.lock().unwrap().extend(new_adapters); +} + +pub fn extend_resources(new_resources: BTreeMap>) { + RESOURCES.lock().unwrap().extend(new_resources); +} + +pub fn extend_extensions(new_extensions: BTreeMap) { + EXTENSIONS.lock().unwrap().extend(new_extensions); +} + +pub fn extend_adapted_resources(new_adapted_resources: BTreeMap>) { + ADAPTED_RESOURCES.lock().unwrap().extend(new_adapted_resources); +} + +pub fn extensions_is_empty() -> bool { + EXTENSIONS.lock().unwrap().is_empty() +} + +pub fn resources_is_empty() -> bool { + RESOURCES.lock().unwrap().is_empty() +} + +pub fn adapters_is_empty() -> bool { + ADAPTERS.lock().unwrap().is_empty() +} diff --git a/dsc_lib/src/discovery/mod.rs b/dsc_lib/src/discovery/mod.rs index f96f94d09..fc71dece1 100644 --- a/dsc_lib/src/discovery/mod.rs +++ b/dsc_lib/src/discovery/mod.rs @@ -2,6 +2,7 @@ // Licensed under the MIT License. pub mod command_discovery; +mod command_discovery_cache; pub mod discovery_trait; use crate::discovery::discovery_trait::{DiscoveryKind, ResourceDiscovery, DiscoveryFilter}; From 8bf4d492c7337e7e61105985cac23fd7b200434f Mon Sep 17 00:00:00 2001 From: Steve Lee Date: Thu, 25 Sep 2025 13:36:25 -0700 Subject: [PATCH 2/8] Update dsc_lib/src/discovery/command_discovery_cache.rs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- dsc_lib/src/discovery/command_discovery_cache.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dsc_lib/src/discovery/command_discovery_cache.rs b/dsc_lib/src/discovery/command_discovery_cache.rs index bcdf8dc56..5353b7285 100644 --- a/dsc_lib/src/discovery/command_discovery_cache.rs +++ b/dsc_lib/src/discovery/command_discovery_cache.rs @@ -7,10 +7,10 @@ use std::collections::BTreeMap; use std::sync::{LazyLock, Mutex}; // use BTreeMap so that the results are sorted by the typename, the Vec is sorted by version -static ADAPTERS : LazyLock>>> = LazyLock::new(|| Mutex::new(BTreeMap::new())); -static RESOURCES : LazyLock>>> = LazyLock::new(|| Mutex::new(BTreeMap::new())); -static EXTENSIONS : LazyLock>> = LazyLock::new(|| Mutex::new(BTreeMap::new())); -static ADAPTED_RESOURCES : LazyLock>>> = LazyLock::new(|| Mutex::new(BTreeMap::new())); +static ADAPTERS: LazyLock>>> = LazyLock::new(|| Mutex::new(BTreeMap::new())); +static RESOURCES: LazyLock>>> = LazyLock::new(|| Mutex::new(BTreeMap::new())); +static EXTENSIONS: LazyLock>> = LazyLock::new(|| Mutex::new(BTreeMap::new())); +static ADAPTED_RESOURCES: LazyLock>>> = LazyLock::new(|| Mutex::new(BTreeMap::new())); pub fn get_adapters() -> BTreeMap> { ADAPTERS.lock().unwrap().clone() From e4e87a2f2855c2ae9d9b16d2548bfb962ffeea56 Mon Sep 17 00:00:00 2001 From: Steve Lee Date: Thu, 25 Sep 2025 13:36:34 -0700 Subject: [PATCH 3/8] Update dsc_lib/src/discovery/command_discovery.rs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- dsc_lib/src/discovery/command_discovery.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dsc_lib/src/discovery/command_discovery.rs b/dsc_lib/src/discovery/command_discovery.rs index b50a74975..6d9e79f21 100644 --- a/dsc_lib/src/discovery/command_discovery.rs +++ b/dsc_lib/src/discovery/command_discovery.rs @@ -471,7 +471,7 @@ impl ResourceDiscovery for CommandDiscovery { fn find_resources(&mut self, required_resource_types: &[DiscoveryFilter]) -> Result>, DscError> { debug!("{}", t!("discovery.commandDiscovery.searchingForResources", resources = required_resource_types : {:?})); if resources_is_empty() { - self.discover( &DiscoveryKind::Resource, "*")?; + self.discover(&DiscoveryKind::Resource, "*")?; } let mut found_resources = BTreeMap::>::new(); let mut required_resources = HashMap::::new(); From 2edcc4bfeba020b29911ed66e570d1d887e6f522 Mon Sep 17 00:00:00 2001 From: "Steve Lee (POWERSHELL HE/HIM) (from Dev Box)" Date: Thu, 25 Sep 2025 13:43:30 -0700 Subject: [PATCH 4/8] fix ordering --- dsc_lib/src/discovery/command_discovery.rs | 2 +- .../src/discovery/command_discovery_cache.rs | 58 +++++++++++-------- 2 files changed, 34 insertions(+), 26 deletions(-) diff --git a/dsc_lib/src/discovery/command_discovery.rs b/dsc_lib/src/discovery/command_discovery.rs index 6d9e79f21..4c3ef33f2 100644 --- a/dsc_lib/src/discovery/command_discovery.rs +++ b/dsc_lib/src/discovery/command_discovery.rs @@ -3,8 +3,8 @@ use crate::discovery::{ command_discovery_cache::{ + adapters_is_empty, extend_adapted_resources, extend_adapters, extend_extensions, extend_resources, extensions_is_empty, get_adapted_resource, get_adapted_resources, get_adapters, get_extensions, get_resource, get_resources, resources_is_empty, - adapters_is_empty, extensions_is_empty, extend_adapted_resources, extend_adapters, extend_extensions, extend_resources }, discovery_trait::{DiscoveryFilter, DiscoveryKind, ResourceDiscovery} }; diff --git a/dsc_lib/src/discovery/command_discovery_cache.rs b/dsc_lib/src/discovery/command_discovery_cache.rs index 5353b7285..2dbc6d748 100644 --- a/dsc_lib/src/discovery/command_discovery_cache.rs +++ b/dsc_lib/src/discovery/command_discovery_cache.rs @@ -12,12 +12,24 @@ static RESOURCES: LazyLock>>> = LazyLock static EXTENSIONS: LazyLock>> = LazyLock::new(|| Mutex::new(BTreeMap::new())); static ADAPTED_RESOURCES: LazyLock>>> = LazyLock::new(|| Mutex::new(BTreeMap::new())); +// Adapter functions + +pub fn adapters_is_empty() -> bool { + ADAPTERS.lock().unwrap().is_empty() +} + +pub fn extend_adapters(new_adapters: BTreeMap>) { + ADAPTERS.lock().unwrap().extend(new_adapters); +} + pub fn get_adapters() -> BTreeMap> { ADAPTERS.lock().unwrap().clone() } -pub fn get_adapted_resources() -> BTreeMap> { - ADAPTED_RESOURCES.lock().unwrap().clone() +// Adapted Resource functions + +pub fn extend_adapted_resources(new_adapted_resources: BTreeMap>) { + ADAPTED_RESOURCES.lock().unwrap().extend(new_adapted_resources); } pub fn get_adapted_resource(type_name: &str) -> Option> { @@ -27,45 +39,41 @@ pub fn get_adapted_resource(type_name: &str) -> Option> { None } -pub fn get_extensions() -> BTreeMap { - EXTENSIONS.lock().unwrap().clone() +pub fn get_adapted_resources() -> BTreeMap> { + ADAPTED_RESOURCES.lock().unwrap().clone() } -pub fn get_resources() -> BTreeMap> { - RESOURCES.lock().unwrap().clone() +// Extension functions + +pub fn extend_extensions(new_extensions: BTreeMap) { + EXTENSIONS.lock().unwrap().extend(new_extensions); } -pub fn get_resource(type_name: &str) -> Option> { - if let Some(resources) = RESOURCES.lock().unwrap().get(type_name) { - return Some(resources.clone()); - } - None +pub fn extensions_is_empty() -> bool { + EXTENSIONS.lock().unwrap().is_empty() } -pub fn extend_adapters(new_adapters: BTreeMap>) { - ADAPTERS.lock().unwrap().extend(new_adapters); +pub fn get_extensions() -> BTreeMap { + EXTENSIONS.lock().unwrap().clone() } +// Resource functions + pub fn extend_resources(new_resources: BTreeMap>) { RESOURCES.lock().unwrap().extend(new_resources); } -pub fn extend_extensions(new_extensions: BTreeMap) { - EXTENSIONS.lock().unwrap().extend(new_extensions); -} - -pub fn extend_adapted_resources(new_adapted_resources: BTreeMap>) { - ADAPTED_RESOURCES.lock().unwrap().extend(new_adapted_resources); +pub fn get_resource(type_name: &str) -> Option> { + if let Some(resources) = RESOURCES.lock().unwrap().get(type_name) { + return Some(resources.clone()); + } + None } -pub fn extensions_is_empty() -> bool { - EXTENSIONS.lock().unwrap().is_empty() +pub fn get_resources() -> BTreeMap> { + RESOURCES.lock().unwrap().clone() } pub fn resources_is_empty() -> bool { RESOURCES.lock().unwrap().is_empty() } - -pub fn adapters_is_empty() -> bool { - ADAPTERS.lock().unwrap().is_empty() -} From fd076a15a96bb68653f98165dd3ea76f89d4b909 Mon Sep 17 00:00:00 2001 From: "Steve Lee (POWERSHELL HE/HIM) (from Dev Box)" Date: Thu, 25 Sep 2025 14:26:26 -0700 Subject: [PATCH 5/8] install clippy if needed --- build.ps1 | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/build.ps1 b/build.ps1 index 6c238faed..6dc32e140 100755 --- a/build.ps1 +++ b/build.ps1 @@ -341,6 +341,18 @@ if (!$SkipBuild) { & $rustup target add --toolchain $channel $architecture } + if ($Clippy -and $null -eq (Get-Command cargo-clippy -ErrorAction Ignore)) { + Write-Verbose -Verbose "Installing cargo-clippy" + if ($UseCFS) { + cargo install clippy --config .cargo/config.toml + } else { + cargo install clippy + } + if ($LASTEXITCODE -ne 0) { + throw "Failed to install cargo-clippy" + } + } + if (Test-Path $target) { Remove-Item $target -Recurse -ErrorAction Ignore } From 3f8d616731ee2770a63df84794c83a6526aea847 Mon Sep 17 00:00:00 2001 From: "Steve Lee (POWERSHELL HE/HIM) (from Dev Box)" Date: Thu, 25 Sep 2025 14:49:54 -0700 Subject: [PATCH 6/8] remove install of clippy --- build.ps1 | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/build.ps1 b/build.ps1 index 6dc32e140..6c238faed 100755 --- a/build.ps1 +++ b/build.ps1 @@ -341,18 +341,6 @@ if (!$SkipBuild) { & $rustup target add --toolchain $channel $architecture } - if ($Clippy -and $null -eq (Get-Command cargo-clippy -ErrorAction Ignore)) { - Write-Verbose -Verbose "Installing cargo-clippy" - if ($UseCFS) { - cargo install clippy --config .cargo/config.toml - } else { - cargo install clippy - } - if ($LASTEXITCODE -ne 0) { - throw "Failed to install cargo-clippy" - } - } - if (Test-Path $target) { Remove-Item $target -Recurse -ErrorAction Ignore } From d929b6e51ff347be07702a093a96e183ad3cbeec Mon Sep 17 00:00:00 2001 From: Andy Jordan <2226434+andyleejordan@users.noreply.github.com> Date: Tue, 30 Sep 2025 14:21:27 -0700 Subject: [PATCH 7/8] WIP: Example using functional programming --- dsc_lib/src/discovery/command_discovery.rs | 55 ++++++------- .../src/discovery/command_discovery_cache.rs | 79 ------------------- dsc_lib/src/discovery/mod.rs | 1 - dsc_lib/src/util.rs | 32 ++++++++ 4 files changed, 60 insertions(+), 107 deletions(-) delete mode 100644 dsc_lib/src/discovery/command_discovery_cache.rs diff --git a/dsc_lib/src/discovery/command_discovery.rs b/dsc_lib/src/discovery/command_discovery.rs index 4c3ef33f2..56a2a7dc6 100644 --- a/dsc_lib/src/discovery/command_discovery.rs +++ b/dsc_lib/src/discovery/command_discovery.rs @@ -2,12 +2,9 @@ // Licensed under the MIT License. use crate::discovery::{ - command_discovery_cache::{ - adapters_is_empty, extend_adapted_resources, extend_adapters, extend_extensions, extend_resources, extensions_is_empty, - get_adapted_resource, get_adapted_resources, get_adapters, get_extensions, get_resource, get_resources, resources_is_empty, - }, discovery_trait::{DiscoveryFilter, DiscoveryKind, ResourceDiscovery} }; +use crate::{locked_is_empty, locked_extend, locked_clone, locked_get}; use crate::dscresources::dscresource::{Capability, DscResource, ImplementedAs}; use crate::dscresources::resource_manifest::{import_manifest, validate_semver, Kind, ResourceManifest, SchemaKind}; use crate::dscresources::command_resource::invoke_command; @@ -21,7 +18,7 @@ use rust_i18n::t; use semver::{Version, VersionReq}; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use std::collections::{BTreeMap, HashSet, HashMap}; +use std::{collections::{BTreeMap, HashMap, HashSet}, sync::{LazyLock, Mutex}}; use std::env; use std::ffi::OsStr; use std::fs; @@ -36,6 +33,12 @@ use crate::util::get_exe_path; const DSC_RESOURCE_EXTENSIONS: [&str; 3] = [".dsc.resource.json", ".dsc.resource.yaml", ".dsc.resource.yml"]; const DSC_EXTENSION_EXTENSIONS: [&str; 3] = [".dsc.extension.json", ".dsc.extension.yaml", ".dsc.extension.yml"]; +// use BTreeMap so that the results are sorted by the typename, the Vec is sorted by version +static ADAPTERS: LazyLock>>> = LazyLock::new(|| Mutex::new(BTreeMap::new())); +static RESOURCES: LazyLock>>> = LazyLock::new(|| Mutex::new(BTreeMap::new())); +static EXTENSIONS: LazyLock>> = LazyLock::new(|| Mutex::new(BTreeMap::new())); +static ADAPTED_RESOURCES: LazyLock>>> = LazyLock::new(|| Mutex::new(BTreeMap::new())); + #[derive(Clone, Serialize, Deserialize, JsonSchema)] pub enum ImportedManifest { Resource(DscResource), @@ -79,9 +82,7 @@ impl CommandDiscovery { } #[must_use] - pub fn get_extensions(&self) -> BTreeMap { - get_extensions() - } + pub fn get_extensions(&self) -> BTreeMap { locked_clone!(EXTENSIONS) } fn get_resource_path_setting() -> Result { @@ -305,7 +306,7 @@ impl ResourceDiscovery for CommandDiscovery { match kind { DiscoveryKind::Resource => { // Now we need to call discover extensions and add those resource to the list of resources - for extension in get_extensions().values() { + for extension in locked_clone!(EXTENSIONS).values() { if extension.capabilities.contains(&ExtensionCapability::Discover) { debug!("{}", t!("discovery.commandDiscovery.callingExtension", extension = extension.type_name)); let discovered_resources = extension.discover()?; @@ -318,11 +319,11 @@ impl ResourceDiscovery for CommandDiscovery { } } } - extend_adapters(adapters); - extend_resources(resources); + locked_extend!(ADAPTERS, adapters); + locked_extend!(RESOURCES, resources); }, DiscoveryKind::Extension => { - extend_extensions(extensions); + locked_extend!(EXTENSIONS, extensions); } } @@ -330,15 +331,15 @@ impl ResourceDiscovery for CommandDiscovery { } fn discover_adapted_resources(&mut self, name_filter: &str, adapter_filter: &str) -> Result<(), DscError> { - if resources_is_empty() && adapters_is_empty() { + if locked_is_empty!(RESOURCES) && locked_is_empty!(ADAPTERS) { self.discover(&DiscoveryKind::Resource, "*")?; } - if adapters_is_empty() { + if locked_is_empty!(ADAPTERS) { return Ok(()); } - let adapters = get_adapters(); + let adapters = locked_clone!(ADAPTERS); let regex_str = convert_wildcard_to_regex(adapter_filter); debug!("Using regex {regex_str} as filter for adapter name"); let mut regex_builder = RegexBuilder::new(®ex_str); @@ -430,7 +431,7 @@ impl ResourceDiscovery for CommandDiscovery { return Err(DscError::AdapterNotFound(adapter_filter.to_string())); } - extend_adapted_resources(adapted_resources); + locked_extend!(ADAPTED_RESOURCES, adapted_resources); Ok(()) } @@ -440,10 +441,10 @@ impl ResourceDiscovery for CommandDiscovery { if *kind == DiscoveryKind::Resource { if adapter_name_filter.is_empty() { self.discover(kind, type_name_filter)?; - for (resource_name, resources_vec) in &get_resources() { + for (resource_name, resources_vec) in &locked_clone!(RESOURCES) { resources.insert(resource_name.clone(), resources_vec.iter().map(|r| ImportedManifest::Resource(r.clone())).collect()); } - for (adapter_name, adapter_vec) in &get_adapters() { + for (adapter_name, adapter_vec) in &locked_clone!(ADAPTERS) { resources.insert(adapter_name.clone(), adapter_vec.iter().map(|r| ImportedManifest::Resource(r.clone())).collect()); } } else { @@ -451,7 +452,7 @@ impl ResourceDiscovery for CommandDiscovery { self.discover_adapted_resources(type_name_filter, adapter_name_filter)?; // add/update found adapted resources to the lookup_table - let adapted_resources = get_adapted_resources(); + let adapted_resources = locked_clone!(ADAPTED_RESOURCES); add_resources_to_lookup_table(&adapted_resources); for (adapted_name, adapted_vec) in &adapted_resources { @@ -460,7 +461,7 @@ impl ResourceDiscovery for CommandDiscovery { } } else { self.discover(kind, type_name_filter)?; - for (extension_name, extension) in &get_extensions() { + for (extension_name, extension) in &locked_clone!(EXTENSIONS) { resources.insert(extension_name.clone(), vec![ImportedManifest::Extension(extension.clone())]); } } @@ -470,7 +471,7 @@ impl ResourceDiscovery for CommandDiscovery { fn find_resources(&mut self, required_resource_types: &[DiscoveryFilter]) -> Result>, DscError> { debug!("{}", t!("discovery.commandDiscovery.searchingForResources", resources = required_resource_types : {:?})); - if resources_is_empty() { + if locked_is_empty!(RESOURCES) { self.discover(&DiscoveryKind::Resource, "*")?; } let mut found_resources = BTreeMap::>::new(); @@ -480,7 +481,7 @@ impl ResourceDiscovery for CommandDiscovery { } for filter in required_resource_types { - if let Some(resources) = get_resource(filter.resource_type()) { + if let Some(resources) = locked_get!(RESOURCES, filter.resource_type()) { filter_resources(&mut found_resources, &mut required_resources, &resources, filter); } if required_resources.values().all(|&v| v) { @@ -494,11 +495,11 @@ impl ResourceDiscovery for CommandDiscovery { } // now go through the adapters, this is for implicit adapters so version can't be specified so use latest version - for adapter_name in get_adapters().keys() { + for adapter_name in locked_clone!(ADAPTERS).keys() { self.discover_adapted_resources("*", adapter_name)?; - add_resources_to_lookup_table(&get_adapted_resources()); + add_resources_to_lookup_table(&locked_clone!(ADAPTED_RESOURCES)); for filter in required_resource_types { - if let Some(adapted_resources) = get_adapted_resource(filter.resource_type()) { + if let Some(adapted_resources) = locked_get!(ADAPTED_RESOURCES, filter.resource_type()) { filter_resources(&mut found_resources, &mut required_resources, &adapted_resources, filter); } if required_resources.values().all(|&v| v) { @@ -514,10 +515,10 @@ impl ResourceDiscovery for CommandDiscovery { } fn get_extensions(&mut self) -> Result, DscError> { - if extensions_is_empty() { + if locked_is_empty!(EXTENSIONS) { self.discover(&DiscoveryKind::Extension, "*")?; } - Ok(get_extensions()) + Ok(locked_clone!(EXTENSIONS)) } } diff --git a/dsc_lib/src/discovery/command_discovery_cache.rs b/dsc_lib/src/discovery/command_discovery_cache.rs deleted file mode 100644 index 2dbc6d748..000000000 --- a/dsc_lib/src/discovery/command_discovery_cache.rs +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -use crate::dscresources::dscresource::DscResource; -use crate::extensions::dscextension::DscExtension; -use std::collections::BTreeMap; -use std::sync::{LazyLock, Mutex}; - -// use BTreeMap so that the results are sorted by the typename, the Vec is sorted by version -static ADAPTERS: LazyLock>>> = LazyLock::new(|| Mutex::new(BTreeMap::new())); -static RESOURCES: LazyLock>>> = LazyLock::new(|| Mutex::new(BTreeMap::new())); -static EXTENSIONS: LazyLock>> = LazyLock::new(|| Mutex::new(BTreeMap::new())); -static ADAPTED_RESOURCES: LazyLock>>> = LazyLock::new(|| Mutex::new(BTreeMap::new())); - -// Adapter functions - -pub fn adapters_is_empty() -> bool { - ADAPTERS.lock().unwrap().is_empty() -} - -pub fn extend_adapters(new_adapters: BTreeMap>) { - ADAPTERS.lock().unwrap().extend(new_adapters); -} - -pub fn get_adapters() -> BTreeMap> { - ADAPTERS.lock().unwrap().clone() -} - -// Adapted Resource functions - -pub fn extend_adapted_resources(new_adapted_resources: BTreeMap>) { - ADAPTED_RESOURCES.lock().unwrap().extend(new_adapted_resources); -} - -pub fn get_adapted_resource(type_name: &str) -> Option> { - if let Some(resources) = ADAPTED_RESOURCES.lock().unwrap().get(type_name) { - return Some(resources.clone()); - } - None -} - -pub fn get_adapted_resources() -> BTreeMap> { - ADAPTED_RESOURCES.lock().unwrap().clone() -} - -// Extension functions - -pub fn extend_extensions(new_extensions: BTreeMap) { - EXTENSIONS.lock().unwrap().extend(new_extensions); -} - -pub fn extensions_is_empty() -> bool { - EXTENSIONS.lock().unwrap().is_empty() -} - -pub fn get_extensions() -> BTreeMap { - EXTENSIONS.lock().unwrap().clone() -} - -// Resource functions - -pub fn extend_resources(new_resources: BTreeMap>) { - RESOURCES.lock().unwrap().extend(new_resources); -} - -pub fn get_resource(type_name: &str) -> Option> { - if let Some(resources) = RESOURCES.lock().unwrap().get(type_name) { - return Some(resources.clone()); - } - None -} - -pub fn get_resources() -> BTreeMap> { - RESOURCES.lock().unwrap().clone() -} - -pub fn resources_is_empty() -> bool { - RESOURCES.lock().unwrap().is_empty() -} diff --git a/dsc_lib/src/discovery/mod.rs b/dsc_lib/src/discovery/mod.rs index fc71dece1..f96f94d09 100644 --- a/dsc_lib/src/discovery/mod.rs +++ b/dsc_lib/src/discovery/mod.rs @@ -2,7 +2,6 @@ // Licensed under the MIT License. pub mod command_discovery; -mod command_discovery_cache; pub mod discovery_trait; use crate::discovery::discovery_trait::{DiscoveryKind, ResourceDiscovery, DiscoveryFilter}; diff --git a/dsc_lib/src/util.rs b/dsc_lib/src/util.rs index 094bd1514..c8a40dfc4 100644 --- a/dsc_lib/src/util.rs +++ b/dsc_lib/src/util.rs @@ -212,3 +212,35 @@ fn get_settings_policy_file_path() -> String // This location is writable only by admins, but readable by all users Path::new("/etc").join("dsc").join("dsc.settings.json").display().to_string() } + +#[macro_export] +macro_rules! locked_is_empty { + ($lockable:expr) => {{ + $lockable.lock().unwrap().is_empty() + }}; +} + +#[macro_export] +macro_rules! locked_extend { + ($lockable:expr, $items:expr) => {{ + $lockable.lock().unwrap().extend($items); + }}; +} + +#[macro_export] +macro_rules! locked_clone { + ($lockable:expr) => {{ + $lockable.lock().unwrap().clone() + }}; +} + +#[macro_export] +macro_rules! locked_get { + ($lockable:expr, $key:expr) => {{ + if let Some(v) = $lockable.lock().unwrap().get($key) { + Some(v.clone()) + } else { + None + } + }}; +} From 8ce497cd1c15e9a473bc3790123ab99527ddb4d6 Mon Sep 17 00:00:00 2001 From: Steve Lee Date: Thu, 2 Oct 2025 15:28:01 -0700 Subject: [PATCH 8/8] change from mutex to rwlock --- dsc_lib/src/discovery/command_discovery.rs | 10 +++++----- dsc_lib/src/util.rs | 8 ++++---- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/dsc_lib/src/discovery/command_discovery.rs b/dsc_lib/src/discovery/command_discovery.rs index 56a2a7dc6..3de227723 100644 --- a/dsc_lib/src/discovery/command_discovery.rs +++ b/dsc_lib/src/discovery/command_discovery.rs @@ -18,7 +18,7 @@ use rust_i18n::t; use semver::{Version, VersionReq}; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use std::{collections::{BTreeMap, HashMap, HashSet}, sync::{LazyLock, Mutex}}; +use std::{collections::{BTreeMap, HashMap, HashSet}, sync::{LazyLock, RwLock}}; use std::env; use std::ffi::OsStr; use std::fs; @@ -34,10 +34,10 @@ const DSC_RESOURCE_EXTENSIONS: [&str; 3] = [".dsc.resource.json", ".dsc.resource const DSC_EXTENSION_EXTENSIONS: [&str; 3] = [".dsc.extension.json", ".dsc.extension.yaml", ".dsc.extension.yml"]; // use BTreeMap so that the results are sorted by the typename, the Vec is sorted by version -static ADAPTERS: LazyLock>>> = LazyLock::new(|| Mutex::new(BTreeMap::new())); -static RESOURCES: LazyLock>>> = LazyLock::new(|| Mutex::new(BTreeMap::new())); -static EXTENSIONS: LazyLock>> = LazyLock::new(|| Mutex::new(BTreeMap::new())); -static ADAPTED_RESOURCES: LazyLock>>> = LazyLock::new(|| Mutex::new(BTreeMap::new())); +static ADAPTERS: LazyLock>>> = LazyLock::new(|| RwLock::new(BTreeMap::new())); +static RESOURCES: LazyLock>>> = LazyLock::new(|| RwLock::new(BTreeMap::new())); +static EXTENSIONS: LazyLock>> = LazyLock::new(|| RwLock::new(BTreeMap::new())); +static ADAPTED_RESOURCES: LazyLock>>> = LazyLock::new(|| RwLock::new(BTreeMap::new())); #[derive(Clone, Serialize, Deserialize, JsonSchema)] pub enum ImportedManifest { diff --git a/dsc_lib/src/util.rs b/dsc_lib/src/util.rs index c8a40dfc4..1d7e4dac9 100644 --- a/dsc_lib/src/util.rs +++ b/dsc_lib/src/util.rs @@ -216,28 +216,28 @@ fn get_settings_policy_file_path() -> String #[macro_export] macro_rules! locked_is_empty { ($lockable:expr) => {{ - $lockable.lock().unwrap().is_empty() + $lockable.read().unwrap().is_empty() }}; } #[macro_export] macro_rules! locked_extend { ($lockable:expr, $items:expr) => {{ - $lockable.lock().unwrap().extend($items); + $lockable.write().unwrap().extend($items); }}; } #[macro_export] macro_rules! locked_clone { ($lockable:expr) => {{ - $lockable.lock().unwrap().clone() + $lockable.read().unwrap().clone() }}; } #[macro_export] macro_rules! locked_get { ($lockable:expr, $key:expr) => {{ - if let Some(v) = $lockable.lock().unwrap().get($key) { + if let Some(v) = $lockable.read().unwrap().get($key) { Some(v.clone()) } else { None