mirror of
https://github.com/Grace-Solutions/PSMinIO.git
synced 2026-07-26 14:58:13 +00:00
3e4a7e0810
� 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!
176 lines
5.4 KiB
Markdown
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.
|