mirror of
https://github.com/Grace-Solutions/PSMinIO.git
synced 2026-08-18 08:33:16 +00:00
Create comprehensive PowerShell threading documentation and patterns
� PERMANENT THREADING KNOWLEDGE BASE:
✅ CREATED COMPREHENSIVE DOCUMENTATION:
• docs/POWERSHELL-THREADING-RULES.md - Complete threading guide
• Enhanced ThreadSafeProgressCollector with usage examples
• Added to project structure documentation
• Saved to AI memory for future reference
� CRITICAL RULES DOCUMENTED:
• PowerShell cmdlets can ONLY call Write-* methods from main thread
• Background threads must QUEUE updates, never call Write-* directly
• ProcessQueuedUpdates() must ONLY be called from main thread
• Common mistakes and error symptoms clearly identified
� DESIGN PATTERNS PROVIDED:
• Periodic processing pattern (every 1 second)
• Completion-based processing pattern
• Manual processing points pattern
• Correct Task.WaitAll usage with timeouts
� IMPLEMENTATION CHECKLIST:
• Pre-implementation checklist for new cmdlets
• Debugging tips and error identification
• Code examples for correct and incorrect patterns
• Testing guidelines for threading compliance
� PREVENTS FUTURE REGRESSIONS:
• Clear documentation of the 'golden rule'
• Examples of common threading violations
• Patterns for all background operation types
• Reference for all future PowerShell module development
This ensures we never repeat the same threading mistakes!
This commit is contained in:
@@ -0,0 +1,175 @@
|
||||
# PowerShell Cmdlet Threading Rules and Design Patterns
|
||||
|
||||
## 🚨 CRITICAL THREADING RULE
|
||||
|
||||
**PowerShell cmdlets can ONLY call Write-Progress, Write-Verbose, Write-Object, Write-Error from the main cmdlet thread.**
|
||||
|
||||
**❌ NEVER call these methods from:**
|
||||
- Background threads (`Task.Run`, `Task.Factory.StartNew`)
|
||||
- Async callbacks (`async/await` continuations)
|
||||
- Timer callbacks
|
||||
- Event handlers from other threads
|
||||
- Any thread other than the main cmdlet thread
|
||||
|
||||
**✅ ONLY call these methods from:**
|
||||
- `BeginProcessing()` override
|
||||
- `ProcessRecord()` override
|
||||
- `EndProcessing()` override
|
||||
- Main cmdlet thread execution
|
||||
|
||||
## 🔧 DESIGN PATTERN: ThreadSafeProgressCollector
|
||||
|
||||
### Problem
|
||||
When performing background operations (file uploads, downloads, processing), you need to report progress from background threads, but PowerShell doesn't allow direct calls to Write-Progress from those threads.
|
||||
|
||||
### Solution
|
||||
Use the **ThreadSafeProgressCollector** pattern:
|
||||
|
||||
```csharp
|
||||
// Background thread - QUEUE updates (thread-safe)
|
||||
_progressCollector.QueueProgressUpdate(activityId, "Processing", "Status", percentage);
|
||||
_progressCollector.QueueVerboseMessage("Processing file: {0}", fileName);
|
||||
|
||||
// Main thread - PROCESS queued updates
|
||||
_progressCollector.ProcessQueuedUpdates(); // Only call from main thread!
|
||||
```
|
||||
|
||||
### Implementation Pattern
|
||||
|
||||
```csharp
|
||||
public class MyLongRunningCmdlet : PSCmdlet
|
||||
{
|
||||
private ThreadSafeProgressCollector _progressCollector;
|
||||
|
||||
protected override void BeginProcessing()
|
||||
{
|
||||
_progressCollector = new ThreadSafeProgressCollector(this);
|
||||
}
|
||||
|
||||
protected override void ProcessRecord()
|
||||
{
|
||||
// Start background work
|
||||
var tasks = new List<Task>();
|
||||
|
||||
for (int i = 0; i < workItems.Count; i++)
|
||||
{
|
||||
var task = Task.Run(() =>
|
||||
{
|
||||
// ✅ CORRECT: Queue from background thread
|
||||
_progressCollector.QueueProgressUpdate(1, "Processing", $"Item {i}", progress);
|
||||
_progressCollector.QueueVerboseMessage("Processing item {0}", i);
|
||||
|
||||
// ❌ WRONG: Never call directly from background thread
|
||||
// WriteProgress(...); // This will cause threading errors!
|
||||
// WriteVerbose(...); // This will cause threading errors!
|
||||
});
|
||||
tasks.Add(task);
|
||||
}
|
||||
|
||||
// ✅ CORRECT: Process updates from main thread with periodic updates
|
||||
while (!Task.WaitAll(tasks.ToArray(), 1000)) // 1 second intervals
|
||||
{
|
||||
_progressCollector.ProcessQueuedUpdates(); // Safe on main thread
|
||||
}
|
||||
|
||||
// Final processing
|
||||
_progressCollector.ProcessQueuedUpdates();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 🚨 COMMON MISTAKES TO AVOID
|
||||
|
||||
### ❌ Mistake 1: Direct calls from background threads
|
||||
```csharp
|
||||
Task.Run(() =>
|
||||
{
|
||||
WriteProgress(...); // THREADING ERROR!
|
||||
WriteVerbose(...); // THREADING ERROR!
|
||||
});
|
||||
```
|
||||
|
||||
### ❌ Mistake 2: ProcessQueuedUpdates from background threads
|
||||
```csharp
|
||||
Task.Run(() =>
|
||||
{
|
||||
_progressCollector.QueueProgressUpdate(...);
|
||||
_progressCollector.ProcessQueuedUpdates(); // THREADING ERROR!
|
||||
});
|
||||
```
|
||||
|
||||
### ❌ Mistake 3: Async/await callbacks
|
||||
```csharp
|
||||
await SomeAsyncOperation().ContinueWith(task =>
|
||||
{
|
||||
WriteProgress(...); // THREADING ERROR!
|
||||
});
|
||||
```
|
||||
|
||||
## ✅ CORRECT PATTERNS
|
||||
|
||||
### Pattern 1: Periodic Processing
|
||||
```csharp
|
||||
var tasks = StartBackgroundTasks();
|
||||
while (!Task.WaitAll(tasks, 1000))
|
||||
{
|
||||
_progressCollector.ProcessQueuedUpdates(); // Every 1 second
|
||||
}
|
||||
_progressCollector.ProcessQueuedUpdates(); // Final update
|
||||
```
|
||||
|
||||
### Pattern 2: Completion-based Processing
|
||||
```csharp
|
||||
var tasks = StartBackgroundTasks();
|
||||
Task.WaitAll(tasks);
|
||||
_progressCollector.ProcessQueuedUpdates(); // After completion
|
||||
```
|
||||
|
||||
### Pattern 3: Manual Processing Points
|
||||
```csharp
|
||||
foreach (var item in items)
|
||||
{
|
||||
ProcessItemInBackground(item); // Queues updates
|
||||
_progressCollector.ProcessQueuedUpdates(); // Process after each item
|
||||
}
|
||||
```
|
||||
|
||||
## 🎯 ERROR SYMPTOMS
|
||||
|
||||
If you violate these rules, you'll see errors like:
|
||||
- "The WriteObject and WriteError methods cannot be called from outside the overrides..."
|
||||
- "WriteProgress can only be called from within the same thread"
|
||||
- Cmdlet failures with threading exceptions
|
||||
- Progress bars not updating or appearing
|
||||
|
||||
## 📋 CHECKLIST FOR NEW CMDLETS
|
||||
|
||||
Before implementing background operations:
|
||||
|
||||
- [ ] Are you using ThreadSafeProgressCollector?
|
||||
- [ ] Are all Write* calls from main thread only?
|
||||
- [ ] Are you calling ProcessQueuedUpdates() only from main thread?
|
||||
- [ ] Do you have periodic progress processing for long operations?
|
||||
- [ ] Have you tested with verbose output enabled?
|
||||
- [ ] Have you tested with progress bars enabled?
|
||||
|
||||
## 🔧 DEBUGGING TIPS
|
||||
|
||||
1. **Enable verbose logging** to see threading violations
|
||||
2. **Test with progress bars** - they're most sensitive to threading issues
|
||||
3. **Use Task.WaitAll with timeouts** for periodic processing
|
||||
4. **Never call ProcessQueuedUpdates from callbacks**
|
||||
5. **Queue everything from background threads, process from main thread**
|
||||
|
||||
## 📚 RELATED PATTERNS
|
||||
|
||||
- **File Upload/Download**: Use periodic processing during Task.WaitAll
|
||||
- **Multipart Operations**: Queue from parallel tasks, process periodically
|
||||
- **Long-running Operations**: Process updates every 1-2 seconds
|
||||
- **Collection Processing**: Process after each item or batch
|
||||
|
||||
## 🎯 REMEMBER
|
||||
|
||||
**The golden rule: Background threads QUEUE, main thread PROCESSES.**
|
||||
|
||||
This pattern ensures PowerShell cmdlet compliance while providing real-time progress updates to users.
|
||||
Reference in New Issue
Block a user