Files
PSMinIO/docs/POWERSHELL-THREADING-RULES.md
PSMinIO Developer 3e4a7e0810 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!
2025-07-14 22:26:29 -04:00

176 lines
5.4 KiB
Markdown

# 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.