diff --git a/.gitignore b/.gitignore index 1443cd3..e262e92 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ # Ignore Build +.vscode/ /docfx_project /src/AdvChkSys/bin /src/AdvChkSys/obj diff --git a/src/AdvChkSys/Dependencies/ChunkDependencyTracker.cs b/src/AdvChkSys/Dependencies/ChunkDependencyTracker.cs index e498e8a..0b3be04 100644 --- a/src/AdvChkSys/Dependencies/ChunkDependencyTracker.cs +++ b/src/AdvChkSys/Dependencies/ChunkDependencyTracker.cs @@ -14,21 +14,36 @@ namespace AdvChkSys.Dependencies /// public enum DependencyType { + /// + /// Indicates a neighboring chunk relationship. + /// Neighbor, + + /// + /// Indicates a reference relationship between chunks. + /// Reference, + + /// + /// Indicates an update relationship between chunks. + /// Update, + + /// + /// Indicates a custom dependency relationship. + /// Custom } - + // Dictionary to track dependencies: source chunk -> (target chunk, dependency type) private readonly Dictionary> _dependencies = new(); - + // Dictionary to track dependents: target chunk -> (source chunk, dependency type) private readonly Dictionary> _dependents = new(); - + // Lock object for thread safety private readonly object _lock = new(); - + /// /// Registers a dependency between two chunks. /// @@ -36,7 +51,7 @@ namespace AdvChkSys.Dependencies { if (source == null || target == null) return; - + lock (_lock) { // Add to dependencies @@ -46,7 +61,7 @@ namespace AdvChkSys.Dependencies _dependencies[source] = targetSet; } targetSet.Add((target, type)); - + // Add to dependents if (!_dependents.TryGetValue(target, out var sourceSet)) { @@ -56,7 +71,7 @@ namespace AdvChkSys.Dependencies sourceSet.Add((source, type)); } } - + /// /// Removes a dependency between two chunks. /// @@ -64,7 +79,7 @@ namespace AdvChkSys.Dependencies { if (source == null || target == null) return; - + lock (_lock) { // Remove from dependencies @@ -76,7 +91,7 @@ namespace AdvChkSys.Dependencies _dependencies.Remove(source); } } - + // Remove from dependents if (_dependents.TryGetValue(target, out var sourceSet)) { @@ -88,7 +103,7 @@ namespace AdvChkSys.Dependencies } } } - + /// /// Gets all chunks that depend on the specified chunk. /// @@ -96,14 +111,14 @@ namespace AdvChkSys.Dependencies { if (chunk == null) return new List(); - + lock (_lock) { if (!_dependents.TryGetValue(chunk, out var sourceSet)) { return new List(); } - + var result = new List(sourceSet.Count); foreach (var (source, _) in sourceSet) { @@ -112,7 +127,7 @@ namespace AdvChkSys.Dependencies return result; } } - + /// /// Gets all chunks that the specified chunk depends on. /// @@ -120,14 +135,14 @@ namespace AdvChkSys.Dependencies { if (chunk == null) return new List(); - + lock (_lock) { if (!_dependencies.TryGetValue(chunk, out var targetSet)) { return new List(); } - + var result = new List(targetSet.Count); foreach (var (target, _) in targetSet) { @@ -136,7 +151,7 @@ namespace AdvChkSys.Dependencies return result; } } - + /// /// Gets all chunks that depend on the specified chunk with their dependency types. /// @@ -144,14 +159,14 @@ namespace AdvChkSys.Dependencies { if (chunk == null) return new List<(IChunk, DependencyType)>(); - + lock (_lock) { if (!_dependents.TryGetValue(chunk, out var sourceDependents)) { return new List<(IChunk, DependencyType)>(); } - + var result = new List<(IChunk, DependencyType)>(sourceDependents.Count); foreach (var (source, type) in sourceDependents) { @@ -160,7 +175,7 @@ namespace AdvChkSys.Dependencies return result; } } - + /// /// Gets all chunks that the specified chunk depends on with their dependency types. /// @@ -168,14 +183,14 @@ namespace AdvChkSys.Dependencies { if (chunk == null) return new List<(IChunk, DependencyType)>(); - + lock (_lock) { if (!_dependencies.TryGetValue(chunk, out var targetSet)) { return new List<(IChunk, DependencyType)>(); } - + var result = new List<(IChunk, DependencyType)>(targetSet.Count); foreach (var (target, type) in targetSet) { @@ -184,7 +199,7 @@ namespace AdvChkSys.Dependencies return result; } } - + /// /// Checks if a dependency exists between source and target chunks. /// @@ -192,14 +207,14 @@ namespace AdvChkSys.Dependencies { if (source == null || target == null) return false; - + lock (_lock) { if (!_dependencies.TryGetValue(source, out var targetSet)) { return false; } - + foreach (var (t, _) in targetSet) { if (t.Equals(target)) @@ -207,11 +222,11 @@ namespace AdvChkSys.Dependencies return true; } } - + return false; } } - + /// /// Gets the dependency type between source and target chunks, or null if no dependency exists. /// @@ -219,14 +234,14 @@ namespace AdvChkSys.Dependencies { if (source == null || target == null) return null; - + lock (_lock) { if (!_dependencies.TryGetValue(source, out var targetSet)) { return null; } - + foreach (var (t, type) in targetSet) { if (t.Equals(target)) @@ -234,11 +249,11 @@ namespace AdvChkSys.Dependencies return type; } } - + return null; } } - + /// /// Clears all dependencies for a specific chunk. /// @@ -246,7 +261,7 @@ namespace AdvChkSys.Dependencies { if (chunk == null) return; - + lock (_lock) { // Remove all dependencies where this chunk is the source @@ -265,7 +280,7 @@ namespace AdvChkSys.Dependencies } _dependencies.Remove(chunk); } - + // Remove all dependencies where this chunk is the target if (_dependents.TryGetValue(chunk, out var sourceSets)) { @@ -284,7 +299,7 @@ namespace AdvChkSys.Dependencies } } } - + /// /// Gets all chunks that have any dependencies. /// @@ -295,7 +310,7 @@ namespace AdvChkSys.Dependencies return new HashSet(_dependencies.Keys); } } - + /// /// Gets all chunks that have any dependents. /// diff --git a/src/AdvChkSys/Loading/ChunkLoadingPriority.cs b/src/AdvChkSys/Loading/ChunkLoadingPriority.cs index f0dc8bf..4d44ef1 100644 --- a/src/AdvChkSys/Loading/ChunkLoadingPriority.cs +++ b/src/AdvChkSys/Loading/ChunkLoadingPriority.cs @@ -18,13 +18,32 @@ namespace AdvChkSys.Loading /// public enum Priority { + /// + /// Immediate priority - process as soon as possible + /// Immediate, + + /// + /// High priority - process after immediate requests + /// High, + + /// + /// Normal priority - standard processing order + /// Normal, + + /// + /// Low priority - process after normal requests + /// Low, + + /// + /// Background priority - process when system is idle + /// Background } - + /// /// Represents a chunk loading request with priority. /// @@ -34,47 +53,47 @@ namespace AdvChkSys.Loading /// The X coordinate of the chunk. /// public int X { get; } - + /// /// The Y coordinate of the chunk. /// public int Y { get; } - + /// /// The Z coordinate of the chunk (optional, for 3D chunks). /// public int Z { get; } - + /// /// The priority of this loading request. /// public Priority LoadPriority { get; } - + /// /// The width of the chunk. /// public int Width { get; } - + /// /// The height of the chunk. /// public int Height { get; } - + /// /// The depth of the chunk (for 3D chunks). /// public int Depth { get; } - + /// /// Timestamp when the request was created. /// public DateTime Timestamp { get; } - + /// /// Unique identifier for this request. /// public Guid RequestId { get; } - + /// /// Creates a new 2D chunk loading request. /// @@ -90,7 +109,7 @@ namespace AdvChkSys.Loading Timestamp = DateTime.UtcNow; RequestId = Guid.NewGuid(); } - + /// /// Creates a new 3D chunk loading request. /// @@ -107,39 +126,39 @@ namespace AdvChkSys.Loading RequestId = Guid.NewGuid(); } } - + // Priority queues for each priority level private readonly Dictionary> _requestQueues = new(); - + // Lookup for fast cancellation and status checks private readonly Dictionary _requestLookup = new(); - + // Active tasks to prevent duplicate loading private readonly Dictionary<(int X, int Y, int Z), Task> _activeTasks = new(); - + // Synchronization private readonly SemaphoreSlim _queueSemaphore = new(1, 1); private readonly SemaphoreSlim _processSemaphore; private readonly CancellationTokenSource _cancellationSource = new(); - + // Processing state private bool _isProcessing; private Task? _processingTask; - + // Configuration private readonly int _maxConcurrentLoads; private readonly TimeSpan _requestTimeout; - + /// /// Event raised when a chunk load request is completed. /// public event EventHandler? RequestCompleted; - + /// /// Event raised when a chunk load request fails. /// public event EventHandler<(ChunkLoadRequest Request, Exception Exception)>? RequestFailed; - + /// /// Initializes a new instance of the ChunkLoadingPriority class. /// @@ -150,14 +169,14 @@ namespace AdvChkSys.Loading _maxConcurrentLoads = maxConcurrentLoads; _requestTimeout = TimeSpan.FromSeconds(requestTimeoutSeconds); _processSemaphore = new SemaphoreSlim(maxConcurrentLoads, maxConcurrentLoads); - + // Initialize queues for each priority level foreach (Priority priority in Enum.GetValues(typeof(Priority))) { _requestQueues[priority] = new Queue(); } } - + /// /// Enqueues a chunk load request with the specified priority. /// @@ -168,7 +187,7 @@ namespace AdvChkSys.Loading EnqueueRequest(request); return request; } - + /// /// Enqueues a 3D chunk load request with the specified priority. /// @@ -179,7 +198,7 @@ namespace AdvChkSys.Loading EnqueueRequest(request); return request; } - + /// /// Enqueues a pre-created chunk load request. /// @@ -194,11 +213,11 @@ namespace AdvChkSys.Loading { return; // Already loading this chunk } - + // Add to appropriate queue _requestQueues[request.LoadPriority].Enqueue(request); _requestLookup[request.RequestId] = request; - + // Start processing if not already running EnsureProcessingStarted(); } @@ -206,14 +225,14 @@ namespace AdvChkSys.Loading { _queueSemaphore.Release(); } - + // For immediate priority, wait for processing to start if (request.LoadPriority == Priority.Immediate) { TriggerImmediateProcessing(); } } - + /// /// Cancels a pending chunk load request. /// @@ -237,7 +256,7 @@ namespace AdvChkSys.Loading _queueSemaphore.Release(); } } - + /// /// Cancels all pending chunk load requests. /// @@ -257,7 +276,7 @@ namespace AdvChkSys.Loading _queueSemaphore.Release(); } } - + /// /// Starts the processing of chunk load requests if not already running. /// @@ -265,11 +284,11 @@ namespace AdvChkSys.Loading { if (_isProcessing) return; - + _isProcessing = true; _processingTask = Task.Run(ProcessRequestsAsync); } - + /// /// Triggers immediate processing of high-priority requests. /// @@ -278,7 +297,7 @@ namespace AdvChkSys.Loading // This is a hint to the processor to check for immediate requests now // We don't need to do anything special as the processor checks immediate first } - + /// /// Main processing loop for chunk load requests. /// @@ -287,13 +306,13 @@ namespace AdvChkSys.Loading while (!_cancellationSource.IsCancellationRequested) { ChunkLoadRequest? request = null; - + // Get the next request from the highest priority queue await _queueSemaphore.WaitAsync(); try { request = DequeueNextRequest(); - + if (request == null) { // No requests to process, pause briefly @@ -302,7 +321,7 @@ namespace AdvChkSys.Loading await Task.Delay(50); continue; } - + // Mark this chunk as being processed var key = (request.X, request.Y, request.Z); if (_activeTasks.ContainsKey(key)) @@ -310,7 +329,7 @@ namespace AdvChkSys.Loading // Already processing this chunk, skip continue; } - + // Create a task for this request but don't start it yet var loadTask = ProcessRequestAsync(request); _activeTasks[key] = loadTask; @@ -322,10 +341,10 @@ namespace AdvChkSys.Loading _queueSemaphore.Release(); } } - + // Wait for a processing slot await _processSemaphore.WaitAsync(); - + // Start the task and continue without waiting _ = Task.Run(async () => { @@ -336,7 +355,7 @@ namespace AdvChkSys.Loading finally { _processSemaphore.Release(); - + // Remove from active tasks await _queueSemaphore.WaitAsync(); try @@ -351,7 +370,7 @@ namespace AdvChkSys.Loading }); } } - + /// /// Dequeues the next request from the highest priority queue. /// @@ -361,30 +380,30 @@ namespace AdvChkSys.Loading foreach (Priority priority in Enum.GetValues(typeof(Priority))) { var queue = _requestQueues[priority]; - + while (queue.Count > 0) { var request = queue.Dequeue(); - + // Skip if the request has been canceled if (!_requestLookup.ContainsKey(request.RequestId)) continue; - + // Skip if the request has timed out if (DateTime.UtcNow - request.Timestamp > _requestTimeout) { _requestLookup.Remove(request.RequestId); continue; } - + // Valid request found return request; } } - + return null; // No valid requests found } - + /// /// Processes a single chunk load request. /// @@ -394,7 +413,7 @@ namespace AdvChkSys.Loading { // This would be integrated with your chunk manager // For now, we'll just simulate the loading with a delay - + // Simulate different loading times based on priority int delayMs = request.LoadPriority switch { @@ -405,9 +424,9 @@ namespace AdvChkSys.Loading Priority.Background => 1000, _ => 200 }; - + await Task.Delay(delayMs); - + // Notify completion RequestCompleted?.Invoke(this, request); } @@ -430,7 +449,7 @@ namespace AdvChkSys.Loading } } } - + /// /// Gets the number of pending requests for each priority level. /// @@ -451,7 +470,7 @@ namespace AdvChkSys.Loading _queueSemaphore.Release(); } } - + /// /// Gets the total number of pending requests. /// @@ -472,14 +491,14 @@ namespace AdvChkSys.Loading _queueSemaphore.Release(); } } - + /// /// Stops processing and releases resources. /// public async Task ShutdownAsync() { _cancellationSource.Cancel(); - + if (_processingTask != null) { try @@ -495,12 +514,12 @@ namespace AdvChkSys.Loading // Ignore other exceptions during shutdown } } - + _queueSemaphore.Dispose(); _processSemaphore.Dispose(); _cancellationSource.Dispose(); } - + /// /// Creates a ChunkLoadingPriority instance with default settings. /// @@ -508,7 +527,7 @@ namespace AdvChkSys.Loading { return new ChunkLoadingPriority(); } - + /// /// Creates a ChunkLoadingPriority instance optimized for high throughput. /// @@ -518,7 +537,7 @@ namespace AdvChkSys.Loading maxConcurrentLoads: Environment.ProcessorCount * 2, requestTimeoutSeconds: 60); } - + /// /// Creates a ChunkLoadingPriority instance optimized for low latency. /// @@ -528,7 +547,7 @@ namespace AdvChkSys.Loading maxConcurrentLoads: Math.Max(2, Environment.ProcessorCount / 2), requestTimeoutSeconds: 15); } - + /// /// Integrates with a chunk manager to process load requests. /// @@ -539,7 +558,7 @@ namespace AdvChkSys.Loading // Replace the ProcessRequestAsync method with one that uses the chunk manager // This is a simplified example - in a real implementation, you'd need to handle // both 2D and 3D chunk managers and properly cast the interface - + RequestCompleted += (sender, request) => { // The chunk has been loaded, you might want to do something with it @@ -547,7 +566,7 @@ namespace AdvChkSys.Loading // Additional processing if needed }; } - + /// /// Gets the estimated time until a request with the given priority would be processed. /// @@ -558,7 +577,7 @@ namespace AdvChkSys.Loading try { int totalHigherPriorityRequests = 0; - + // Count requests with higher or equal priority foreach (Priority p in Enum.GetValues(typeof(Priority))) { @@ -567,13 +586,13 @@ namespace AdvChkSys.Loading totalHigherPriorityRequests += _requestQueues[p].Count; } } - + // If no requests or no active tasks, return 0 if (totalHigherPriorityRequests == 0 || _activeTasks.Count == 0) { return 0; } - + // Estimate based on current processing rate // This is a very simple estimate and could be improved with actual metrics int averageProcessingTimeMs = priority switch @@ -585,10 +604,10 @@ namespace AdvChkSys.Loading Priority.Background => 1000, _ => 200 }; - + // Calculate how many batches of concurrent requests we'll need int batches = (int)Math.Ceiling(totalHigherPriorityRequests / (double)_maxConcurrentLoads); - + return batches * averageProcessingTimeMs; } finally @@ -596,7 +615,7 @@ namespace AdvChkSys.Loading _queueSemaphore.Release(); } } - + /// /// Adjusts the priority of an existing request. /// @@ -605,7 +624,7 @@ namespace AdvChkSys.Loading { // Note: This is not an efficient operation as we don't directly modify the queue // Instead, we mark the request for priority change when it's processed - + _queueSemaphore.Wait(); try { @@ -616,26 +635,26 @@ namespace AdvChkSys.Loading { // Remove from lookup (it will be skipped when dequeued) _requestLookup.Remove(requestId); - + // Create a new request with the same parameters but higher priority var newRequest = request.Depth > 1 ? new ChunkLoadRequest(request.X, request.Y, request.Z, request.Width, request.Height, request.Depth, newPriority) : new ChunkLoadRequest(request.X, request.Y, request.Width, request.Height, newPriority); - + // Add the new request _requestQueues[newPriority].Enqueue(newRequest); _requestLookup[newRequest.RequestId] = newRequest; - + // If upgrading to immediate, trigger processing if (newPriority == Priority.Immediate) { TriggerImmediateProcessing(); } } - + return true; } - + return false; } finally @@ -643,7 +662,7 @@ namespace AdvChkSys.Loading _queueSemaphore.Release(); } } - + /// /// Gets statistics about the current state of the chunk loading system. /// @@ -659,13 +678,13 @@ namespace AdvChkSys.Loading ["IsProcessing"] = _isProcessing, ["MaxConcurrentLoads"] = _maxConcurrentLoads }; - + // Add queue sizes for each priority foreach (Priority priority in Enum.GetValues(typeof(Priority))) { stats[$"Queue_{priority}"] = _requestQueues[priority].Count; } - + return stats; } finally @@ -674,4 +693,4 @@ namespace AdvChkSys.Loading } } } -} \ No newline at end of file +} diff --git a/src/AdvChkSys/Threading/ChunkThreadingManager.cs b/src/AdvChkSys/Threading/ChunkThreadingManager.cs index 515e370..b424c9d 100644 --- a/src/AdvChkSys/Threading/ChunkThreadingManager.cs +++ b/src/AdvChkSys/Threading/ChunkThreadingManager.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -14,17 +13,17 @@ namespace AdvChkSys.Threading { private static ChunkThreadingManager? _instance; private static readonly object _instanceLock = new(); - + // Thread safety utilities private readonly ChunkThreadSafetyManager _threadSafety; private readonly ChunkAsyncLock _asyncLock; - + // Operation queue private readonly ChunkOperationQueue _operationQueue; - + // Cancellation for shutdown private readonly CancellationTokenSource _shutdownCts = new(); - + /// /// Gets the singleton instance of the ChunkThreadingManager. /// @@ -42,7 +41,7 @@ namespace AdvChkSys.Threading return _instance; } } - + /// /// Initializes a new instance of the ChunkThreadingManager class. /// @@ -50,29 +49,29 @@ namespace AdvChkSys.Threading { _threadSafety = new ChunkThreadSafetyManager( ChunkThreadingConfiguration.LockCleanupIntervalMinutes); - + _asyncLock = new ChunkAsyncLock( ChunkThreadingConfiguration.LockCleanupIntervalMinutes); - + _operationQueue = new ChunkOperationQueue( ChunkThreadingConfiguration.DefaultMaxDegreeOfParallelism); } - + /// /// Gets the thread safety manager for synchronous locking. /// public ChunkThreadSafetyManager ThreadSafety => _threadSafety; - + /// /// Gets the async lock manager for asynchronous locking. /// public ChunkAsyncLock AsyncLock => _asyncLock; - + /// /// Gets the operation queue for sequential chunk operations. /// public ChunkOperationQueue OperationQueue => _operationQueue; - + /// /// Acquires an exclusive lock on a chunk. /// @@ -82,7 +81,7 @@ namespace AdvChkSys.Threading { return _threadSafety.AcquireLock(chunk); } - + /// /// Acquires a read lock on a chunk. /// @@ -92,7 +91,7 @@ namespace AdvChkSys.Threading { return _threadSafety.AcquireReadLock(chunk); } - + /// /// Acquires a write lock on a chunk. /// @@ -102,7 +101,7 @@ namespace AdvChkSys.Threading { return _threadSafety.AcquireWriteLock(chunk); } - + /// /// Acquires a lock on a chunk asynchronously. /// @@ -113,7 +112,7 @@ namespace AdvChkSys.Threading { return _asyncLock.LockAsync(chunk, cancellationToken); } - + /// /// Enqueues an operation to be performed on a chunk. /// @@ -124,7 +123,7 @@ namespace AdvChkSys.Threading { return _operationQueue.EnqueueOperationAsync(chunk, operation); } - + /// /// Runs an action with a lock on the chunk. /// @@ -135,7 +134,7 @@ namespace AdvChkSys.Threading using var lockObj = AcquireLock(chunk); action(); } - + /// /// Runs a function with a lock on the chunk and returns the result. /// @@ -148,7 +147,7 @@ namespace AdvChkSys.Threading using var lockObj = AcquireLock(chunk); return func(); } - + /// /// Runs an async action with a lock on the chunk. /// @@ -160,7 +159,7 @@ namespace AdvChkSys.Threading using var lockObj = await LockAsync(chunk, cancellationToken).ConfigureAwait(false); await action().ConfigureAwait(false); } - + /// /// Runs an async function with a lock on the chunk and returns the result. /// @@ -174,7 +173,7 @@ namespace AdvChkSys.Threading using var lockObj = await LockAsync(chunk, cancellationToken).ConfigureAwait(false); return await func().ConfigureAwait(false); } - + /// /// Runs an action on multiple chunks with proper locking to avoid deadlocks. /// @@ -184,7 +183,7 @@ namespace AdvChkSys.Threading { // Sort chunks by ID to prevent deadlocks var sortedChunks = SortChunksById(chunks); - + // Acquire locks in order var locks = new IDisposable[sortedChunks.Length]; try @@ -193,7 +192,7 @@ namespace AdvChkSys.Threading { locks[i] = AcquireLock(sortedChunks[i]); } - + // Execute the action action(); } @@ -206,7 +205,7 @@ namespace AdvChkSys.Threading } } } - + /// /// Runs an async action on multiple chunks with proper locking to avoid deadlocks. /// @@ -217,7 +216,7 @@ namespace AdvChkSys.Threading { // Sort chunks by ID to prevent deadlocks var sortedChunks = SortChunksById(chunks); - + // Acquire locks in order var locks = new IDisposable[sortedChunks.Length]; try @@ -226,7 +225,7 @@ namespace AdvChkSys.Threading { locks[i] = await LockAsync(sortedChunks[i], cancellationToken).ConfigureAwait(false); } - + // Execute the action await action().ConfigureAwait(false); } @@ -239,7 +238,7 @@ namespace AdvChkSys.Threading } } } - + /// /// Sorts chunks by ID to prevent deadlocks when acquiring multiple locks. /// @@ -248,24 +247,24 @@ namespace AdvChkSys.Threading // Use System.Linq for OrderBy return chunks.OrderBy(c => c.GetHashCode()).ToArray(); } - + /// /// Disposes all resources. /// public void Dispose() { _shutdownCts.Cancel(); - + // Shutdown operation queue _operationQueue.ShutdownAsync().GetAwaiter().GetResult(); - + // Dispose thread safety managers (_threadSafety as IDisposable)?.Dispose(); (_asyncLock as IDisposable)?.Dispose(); - + _shutdownCts.Dispose(); } - + /// /// Resets the singleton instance (for testing). /// diff --git a/src/AdvChkSys/Threading/ChunkThreadingPerformanceMonitor.cs b/src/AdvChkSys/Threading/ChunkThreadingPerformanceMonitor.cs index e4edbb5..cc72bb0 100644 --- a/src/AdvChkSys/Threading/ChunkThreadingPerformanceMonitor.cs +++ b/src/AdvChkSys/Threading/ChunkThreadingPerformanceMonitor.cs @@ -4,7 +4,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Threading; -using System.Threading.Tasks; + namespace AdvChkSys.Threading { @@ -15,19 +15,19 @@ namespace AdvChkSys.Threading { // Performance metrics private readonly ConcurrentDictionary _metrics = new(); - + // Sampling timer private readonly Timer _samplingTimer; - + // Sampling interval private readonly TimeSpan _samplingInterval; - + // Maximum history to keep private readonly int _maxHistory; - + // Whether monitoring is enabled private bool _isEnabled; - + /// /// Initializes a new instance of the ChunkThreadingPerformanceMonitor class. /// @@ -39,7 +39,7 @@ namespace AdvChkSys.Threading _maxHistory = maxHistory; _samplingTimer = new Timer(SamplePerformance, null, Timeout.Infinite, Timeout.Infinite); } - + /// /// Starts monitoring performance. /// @@ -51,7 +51,7 @@ namespace AdvChkSys.Threading _samplingTimer.Change(TimeSpan.Zero, _samplingInterval); } } - + /// /// Stops monitoring performance. /// @@ -63,7 +63,7 @@ namespace AdvChkSys.Threading _samplingTimer.Change(Timeout.Infinite, Timeout.Infinite); } } - + /// /// Samples performance metrics. /// @@ -71,28 +71,28 @@ namespace AdvChkSys.Threading { if (!_isEnabled) return; - + // Sample thread pool information ThreadPool.GetAvailableThreads(out int workerThreads, out int completionPortThreads); ThreadPool.GetMaxThreads(out int maxWorkerThreads, out int maxCompletionPortThreads); - + // Calculate thread pool usage double workerThreadUsage = 1.0 - ((double)workerThreads / maxWorkerThreads); double ioThreadUsage = 1.0 - ((double)completionPortThreads / maxCompletionPortThreads); - + // Update metrics UpdateMetric("ThreadPool.WorkerThreadUsage", workerThreadUsage); UpdateMetric("ThreadPool.IOThreadUsage", ioThreadUsage); - + // Sample operation queue information var operationQueue = ChunkThreadingManager.Instance.OperationQueue; UpdateMetric("OperationQueue.PendingOperations", operationQueue.PendingOperationCount); UpdateMetric("OperationQueue.ActiveOperations", operationQueue.ActiveOperationCount); - + // Sample diagnostics information UpdateMetric("Diagnostics.ActiveOperations", ChunkThreadingDiagnostics.GetActiveOperationCount()); UpdateMetric("Diagnostics.LockContentions", ChunkThreadingDiagnostics.GetTotalLockContentionCount()); - + // Sample process information var process = Process.GetCurrentProcess(); // Fix: Use process.TotalProcessorTime and calculate uptime manually @@ -101,7 +101,7 @@ namespace AdvChkSys.Threading UpdateMetric("Process.Memory", process.WorkingSet64 / 1024.0 / 1024.0); // MB UpdateMetric("Process.Threads", process.Threads.Count); } - + /// /// Updates a performance metric. /// @@ -112,36 +112,36 @@ namespace AdvChkSys.Threading metric = new PerformanceMetric(_maxHistory); _metrics[name] = metric; } - + metric.AddSample(value); } - + /// /// Gets a performance report. /// public Dictionary GetPerformanceReport() { var report = new Dictionary(); - + foreach (var kvp in _metrics) { report[kvp.Key] = kvp.Value.GetReport(); } - + return report; } - + /// /// Gets a formatted performance report. /// public string GetFormattedReport() { var report = new System.Text.StringBuilder(); - + report.AppendLine("=== Chunk Threading Performance Report ==="); report.AppendLine($"Generated: {DateTime.UtcNow:yyyy-MM-dd HH:mm:ss} UTC"); report.AppendLine(); - + var metrics = _metrics.OrderBy(m => m.Key).ToList(); foreach (var kvp in metrics) { @@ -153,10 +153,10 @@ namespace AdvChkSys.Threading report.AppendLine($" Max: {metricReport.Max:F3}"); report.AppendLine(); } - + return report.ToString(); } - + /// /// Disposes resources. /// @@ -165,7 +165,7 @@ namespace AdvChkSys.Threading Stop(); _samplingTimer.Dispose(); } - + /// /// Represents a performance metric with history. /// @@ -173,26 +173,26 @@ namespace AdvChkSys.Threading { private readonly Queue _samples; private readonly int _maxSamples; - + public PerformanceMetric(int maxSamples) { _maxSamples = maxSamples; _samples = new Queue(maxSamples); } - + public void AddSample(double value) { lock (_samples) { _samples.Enqueue(value); - + while (_samples.Count > _maxSamples) { _samples.Dequeue(); } } } - + public PerformanceReport GetReport() { lock (_samples) @@ -201,7 +201,7 @@ namespace AdvChkSys.Threading { return new PerformanceReport(0, 0, 0, 0, Array.Empty()); } - + var samplesArray = _samples.ToArray(); return new PerformanceReport( samplesArray.Last(), @@ -213,7 +213,7 @@ namespace AdvChkSys.Threading } } } - + /// /// Represents a performance report. /// @@ -223,27 +223,35 @@ namespace AdvChkSys.Threading /// The current value. /// public double Current { get; } - + /// /// The average value. /// public double Average { get; } - + /// /// The minimum value. /// public double Min { get; } - + /// /// The maximum value. /// public double Max { get; } - + /// /// The history of values. /// public double[] History { get; } - + + /// + /// + /// + /// + /// + /// + /// + /// public PerformanceReport(double current, double average, double min, double max, double[] history) { Current = current; diff --git a/src/AdvChkSys/Util/MemoryHelper.cs b/src/AdvChkSys/Util/MemoryHelper.cs index d72a330..e7ec776 100644 --- a/src/AdvChkSys/Util/MemoryHelper.cs +++ b/src/AdvChkSys/Util/MemoryHelper.cs @@ -35,8 +35,10 @@ namespace AdvChkSys.Util private static ulong GetAvailableMemoryWindows() { - var memStatus = new MEMORYSTATUSEX(); - memStatus.dwLength = (uint)Marshal.SizeOf(typeof(MEMORYSTATUSEX)); + var memStatus = new MEMORYSTATUSEX + { + dwLength = (uint)Marshal.SizeOf(typeof(MEMORYSTATUSEX)) + }; if (GlobalMemoryStatusEx(ref memStatus)) { return memStatus.ullAvailPhys; @@ -81,21 +83,61 @@ namespace AdvChkSys.Util return 0; } + /// + /// Structure containing memory status information for Windows systems. + /// [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] public struct MEMORYSTATUSEX { + /// + /// The size of the structure, in bytes. You must set this member before calling GlobalMemoryStatusEx. + /// public uint dwLength; + /// + /// A number between 0 and 100 that specifies the approximate percentage of physical memory + /// that is in use (0 indicates no memory use and 100 indicates full memory use). + /// public uint dwMemoryLoad; + /// + /// The total size of physical memory, in bytes. + /// public ulong ullTotalPhys; + /// + /// The amount of physical memory currently available, in bytes. + /// This is the amount of physical memory that can be immediately reused without having to write its contents to disk first. + /// public ulong ullAvailPhys; + /// + /// The current committed memory limit for the system or the current process, whichever is smaller, in bytes. + /// public ulong ullTotalPageFile; + /// + /// The maximum amount of memory the current process can commit, in bytes. + /// This value is equal to or smaller than the system-wide available commit value. + /// public ulong ullAvailPageFile; + /// + /// The size of the user-mode portion of the virtual address space of the calling process, in bytes. + /// public ulong ullTotalVirtual; + /// + /// The amount of unreserved and uncommitted memory currently in the user-mode portion + /// of the virtual address space of the calling process, in bytes. + /// public ulong ullAvailVirtual; + /// + /// Reserved. This value is always 0. + /// public ulong ullAvailExtendedVirtual; } + /// + /// Retrieves information about the system's current usage of both physical and virtual memory. + /// + /// A pointer to a structure that receives the memory status information. + /// If the function succeeds, the return value is true. If the function fails, the return value is false. [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] public static extern bool GlobalMemoryStatusEx(ref MEMORYSTATUSEX lpBuffer); + } } \ No newline at end of file