/// <summary> /// Start monitoring the resources. /// </summary> /// <param name="resourcesToMonitor">The resources to be monitored.</param> /// <param name="callbackAction">Handler to call when any of the resources is uptdated</param> /// <returns><c>true</c> if the operation succeeded; <c>false</c> otherwise.</returns> public bool TryStartMonitoring(IEnumerable <IResource> resourcesToMonitor, ResourceUpdatedHandler callbackAction) { if (!Code.ValidateArgument(resourcesToMonitor, nameof(resourcesToMonitor), TaggingUtilities.ReserveTag(0x238208c9 /* tag_9669j */)) || !Code.ValidateArgument(callbackAction, nameof(callbackAction), TaggingUtilities.ReserveTag(0x238208ca /* tag_9669k */))) { return(false); } IList <IResource> resources = resourcesToMonitor.ToList(); ResourceMonitoringData monitoringData = new ResourceMonitoringData(callbackAction, resources); if (!TryPerformInitialLoad(resources, monitoringData)) { return(false); } if (!m_resourceMonitoringData.TryAdd(callbackAction, monitoringData)) { return(false); } foreach (IResource resource in resources) { if (!resource.IsStatic) { m_monitoredResources.TryAdd(resource, true); } } StartExclusiveAction(); return(true); }
/// <summary> /// Perfroms initial load of the resources which are not yet known to the monitor /// </summary> /// <param name="resources">A list of resources</param> /// <param name="monitoringData">Monitoring data for this initial load</param> private bool TryPerformInitialLoad(IEnumerable <IResource> resources, ResourceMonitoringData monitoringData) { List <IResource> resourcesNotYetSeen = new List <IResource>(); foreach (IResource resource in resources) { if (!m_resourceDetails.TryGetValue(resource, out IResourceDetails details)) { // it is not terrible if we add here a resource that we have actually already loaded // but we would want to reduce the number of such occasions to minimum resourcesNotYetSeen.Add(resource); } } CheckResourcesForUpdates(resourcesNotYetSeen, monitoringData); foreach (IResource resource in resourcesNotYetSeen) { if (!m_resourceDetails.TryGetValue(resource, out IResourceDetails details)) { return(false); } } return(true); }
/// <summary> /// Call all of the handlers who are initerested in any of the updated resources /// or just the specified handler if it should be called /// </summary> /// <param name="updatedResources">Resources that were updated since the last check</param> /// <param name="singleMonitoringData">Single handler to be called if not null</param> private void NotifyHandlers(HashSet <IResource> updatedResources, ResourceMonitoringData singleMonitoringData) { ICollection <ResourceMonitoringData> monitoringCollection = m_resourceMonitoringData.Values; if (singleMonitoringData != null) { monitoringCollection = new List <ResourceMonitoringData> { singleMonitoringData }; } foreach (ResourceMonitoringData monitoringInstance in monitoringCollection) { monitoringInstance.CallHandlerIfNecessary(m_resourceDetails, updatedResources); } }
/// <summary> /// Check resources for updates /// </summary> /// <param name="resources">A list of resources to be checked</param> /// <param name="singleMonitoringData">Signle instance of ResourceMonitoringData to use if present (use class-wide dict instead)</param> private void CheckResourcesForUpdates(IEnumerable <IResource> resources, ResourceMonitoringData singleMonitoringData) { HashSet <IResource> updatedResources = new HashSet <IResource>(); foreach (IResource resource in resources) { IResourceDetails newDetails = GetFileDetails(resource); if (newDetails == null) { ULSLogging.LogTraceTag(0x238208cc /* tag_9669m */, Categories.ConfigurationDataSet, Levels.Warning, "Failed to load resource details for '{0}'", resource.Name); continue; } bool updated; if (m_resourceDetails.TryGetValue(resource, out IResourceDetails currentDetails) && currentDetails != null) { updated = !string.Equals(currentDetails.SHA256Hash, newDetails.SHA256Hash, StringComparison.Ordinal); } else { // it is the first time we load this resource updated = true; } if (updated) { // No explicit synchronization was added here for the following reason: // the worst thing that can happed is two parallel threads consider the same resource // updated and both call the ResourceMonitoringData.CallHandlerIfNecessary // That method on its own ensures in-order execution of handlers m_resourceDetails.AddOrUpdate(resource, newDetails, (res, oldDetails) => newDetails); updatedResources.Add(resource); } } NotifyHandlers(updatedResources, singleMonitoringData); }