claude-mpm 5.4.59__py3-none-any.whl → 5.4.64__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of claude-mpm might be problematic. Click here for more details.
- claude_mpm/VERSION +1 -1
- claude_mpm/agents/CLAUDE_MPM_TEACHER_OUTPUT_STYLE.md +5 -0
- claude_mpm/cli/startup.py +14 -9
- claude_mpm/scripts/start_activity_logging.py +0 -0
- claude_mpm/services/agents/deployment/agent_template_builder.py +8 -0
- claude_mpm/skills/bundled/collaboration/brainstorming/SKILL.md +79 -0
- claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/SKILL.md +178 -0
- claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/references/agent-prompts.md +577 -0
- claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/references/coordination-patterns.md +467 -0
- claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/references/examples.md +537 -0
- claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/references/troubleshooting.md +730 -0
- claude_mpm/skills/bundled/collaboration/git-worktrees.md +317 -0
- claude_mpm/skills/bundled/collaboration/requesting-code-review/SKILL.md +112 -0
- claude_mpm/skills/bundled/collaboration/requesting-code-review/references/code-reviewer-template.md +146 -0
- claude_mpm/skills/bundled/collaboration/requesting-code-review/references/review-examples.md +412 -0
- claude_mpm/skills/bundled/collaboration/stacked-prs.md +251 -0
- claude_mpm/skills/bundled/collaboration/writing-plans/SKILL.md +81 -0
- claude_mpm/skills/bundled/collaboration/writing-plans/references/best-practices.md +362 -0
- claude_mpm/skills/bundled/collaboration/writing-plans/references/plan-structure-templates.md +312 -0
- claude_mpm/skills/bundled/debugging/root-cause-tracing/SKILL.md +152 -0
- claude_mpm/skills/bundled/debugging/root-cause-tracing/references/advanced-techniques.md +668 -0
- claude_mpm/skills/bundled/debugging/root-cause-tracing/references/examples.md +587 -0
- claude_mpm/skills/bundled/debugging/root-cause-tracing/references/integration.md +438 -0
- claude_mpm/skills/bundled/debugging/root-cause-tracing/references/tracing-techniques.md +391 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/CREATION-LOG.md +119 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/SKILL.md +148 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/references/anti-patterns.md +483 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/references/examples.md +452 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/references/troubleshooting.md +449 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/references/workflow.md +411 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/test-academic.md +14 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/test-pressure-1.md +58 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/test-pressure-2.md +68 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/test-pressure-3.md +69 -0
- claude_mpm/skills/bundled/debugging/verification-before-completion/SKILL.md +131 -0
- claude_mpm/skills/bundled/debugging/verification-before-completion/references/gate-function.md +325 -0
- claude_mpm/skills/bundled/debugging/verification-before-completion/references/integration-and-workflows.md +490 -0
- claude_mpm/skills/bundled/debugging/verification-before-completion/references/red-flags-and-failures.md +425 -0
- claude_mpm/skills/bundled/debugging/verification-before-completion/references/verification-patterns.md +499 -0
- claude_mpm/skills/bundled/infrastructure/env-manager/INTEGRATION.md +611 -0
- claude_mpm/skills/bundled/infrastructure/env-manager/README.md +596 -0
- claude_mpm/skills/bundled/infrastructure/env-manager/SKILL.md +260 -0
- claude_mpm/skills/bundled/infrastructure/env-manager/examples/nextjs-env-structure.md +315 -0
- claude_mpm/skills/bundled/infrastructure/env-manager/references/frameworks.md +436 -0
- claude_mpm/skills/bundled/infrastructure/env-manager/references/security.md +433 -0
- claude_mpm/skills/bundled/infrastructure/env-manager/references/synchronization.md +452 -0
- claude_mpm/skills/bundled/infrastructure/env-manager/references/troubleshooting.md +404 -0
- claude_mpm/skills/bundled/infrastructure/env-manager/references/validation.md +420 -0
- claude_mpm/skills/bundled/main/artifacts-builder/SKILL.md +86 -0
- claude_mpm/skills/bundled/main/internal-comms/SKILL.md +43 -0
- claude_mpm/skills/bundled/main/internal-comms/examples/3p-updates.md +47 -0
- claude_mpm/skills/bundled/main/internal-comms/examples/company-newsletter.md +65 -0
- claude_mpm/skills/bundled/main/internal-comms/examples/faq-answers.md +30 -0
- claude_mpm/skills/bundled/main/internal-comms/examples/general-comms.md +16 -0
- claude_mpm/skills/bundled/main/mcp-builder/SKILL.md +160 -0
- claude_mpm/skills/bundled/main/mcp-builder/reference/design_principles.md +412 -0
- claude_mpm/skills/bundled/main/mcp-builder/reference/evaluation.md +602 -0
- claude_mpm/skills/bundled/main/mcp-builder/reference/mcp_best_practices.md +915 -0
- claude_mpm/skills/bundled/main/mcp-builder/reference/node_mcp_server.md +916 -0
- claude_mpm/skills/bundled/main/mcp-builder/reference/python_mcp_server.md +752 -0
- claude_mpm/skills/bundled/main/mcp-builder/reference/workflow.md +1237 -0
- claude_mpm/skills/bundled/main/skill-creator/SKILL.md +189 -0
- claude_mpm/skills/bundled/main/skill-creator/references/best-practices.md +500 -0
- claude_mpm/skills/bundled/main/skill-creator/references/creation-workflow.md +464 -0
- claude_mpm/skills/bundled/main/skill-creator/references/examples.md +619 -0
- claude_mpm/skills/bundled/main/skill-creator/references/progressive-disclosure.md +437 -0
- claude_mpm/skills/bundled/main/skill-creator/references/skill-structure.md +231 -0
- claude_mpm/skills/bundled/php/espocrm-development/SKILL.md +170 -0
- claude_mpm/skills/bundled/php/espocrm-development/references/architecture.md +602 -0
- claude_mpm/skills/bundled/php/espocrm-development/references/common-tasks.md +821 -0
- claude_mpm/skills/bundled/php/espocrm-development/references/development-workflow.md +742 -0
- claude_mpm/skills/bundled/php/espocrm-development/references/frontend-customization.md +726 -0
- claude_mpm/skills/bundled/php/espocrm-development/references/hooks-and-services.md +764 -0
- claude_mpm/skills/bundled/php/espocrm-development/references/testing-debugging.md +831 -0
- claude_mpm/skills/bundled/pm/pm-delegation-patterns/SKILL.md +167 -0
- claude_mpm/skills/bundled/pm/pm-git-file-tracking/SKILL.md +113 -0
- claude_mpm/skills/bundled/pm/pm-pr-workflow/SKILL.md +124 -0
- claude_mpm/skills/bundled/pm/pm-ticketing-integration/SKILL.md +154 -0
- claude_mpm/skills/bundled/pm/pm-verification-protocols/SKILL.md +198 -0
- claude_mpm/skills/bundled/react/flexlayout-react.md +742 -0
- claude_mpm/skills/bundled/rust/desktop-applications/SKILL.md +226 -0
- claude_mpm/skills/bundled/rust/desktop-applications/references/architecture-patterns.md +901 -0
- claude_mpm/skills/bundled/rust/desktop-applications/references/native-gui-frameworks.md +901 -0
- claude_mpm/skills/bundled/rust/desktop-applications/references/platform-integration.md +775 -0
- claude_mpm/skills/bundled/rust/desktop-applications/references/state-management.md +937 -0
- claude_mpm/skills/bundled/rust/desktop-applications/references/tauri-framework.md +770 -0
- claude_mpm/skills/bundled/rust/desktop-applications/references/testing-deployment.md +961 -0
- claude_mpm/skills/bundled/tauri/tauri-async-patterns.md +495 -0
- claude_mpm/skills/bundled/tauri/tauri-build-deploy.md +599 -0
- claude_mpm/skills/bundled/tauri/tauri-command-patterns.md +535 -0
- claude_mpm/skills/bundled/tauri/tauri-error-handling.md +613 -0
- claude_mpm/skills/bundled/tauri/tauri-event-system.md +648 -0
- claude_mpm/skills/bundled/tauri/tauri-file-system.md +673 -0
- claude_mpm/skills/bundled/tauri/tauri-frontend-integration.md +767 -0
- claude_mpm/skills/bundled/tauri/tauri-performance.md +669 -0
- claude_mpm/skills/bundled/tauri/tauri-state-management.md +573 -0
- claude_mpm/skills/bundled/tauri/tauri-testing.md +384 -0
- claude_mpm/skills/bundled/tauri/tauri-window-management.md +628 -0
- claude_mpm/skills/bundled/testing/condition-based-waiting/SKILL.md +119 -0
- claude_mpm/skills/bundled/testing/condition-based-waiting/references/patterns-and-implementation.md +253 -0
- claude_mpm/skills/bundled/testing/test-driven-development/SKILL.md +145 -0
- claude_mpm/skills/bundled/testing/test-driven-development/references/anti-patterns.md +543 -0
- claude_mpm/skills/bundled/testing/test-driven-development/references/examples.md +741 -0
- claude_mpm/skills/bundled/testing/test-driven-development/references/integration.md +470 -0
- claude_mpm/skills/bundled/testing/test-driven-development/references/philosophy.md +458 -0
- claude_mpm/skills/bundled/testing/test-driven-development/references/workflow.md +639 -0
- claude_mpm/skills/bundled/testing/test-quality-inspector/SKILL.md +458 -0
- claude_mpm/skills/bundled/testing/test-quality-inspector/examples/example-inspection-report.md +411 -0
- claude_mpm/skills/bundled/testing/test-quality-inspector/references/assertion-quality.md +317 -0
- claude_mpm/skills/bundled/testing/test-quality-inspector/references/inspection-checklist.md +270 -0
- claude_mpm/skills/bundled/testing/test-quality-inspector/references/red-flags.md +436 -0
- claude_mpm/skills/bundled/testing/testing-anti-patterns/SKILL.md +140 -0
- claude_mpm/skills/bundled/testing/testing-anti-patterns/references/completeness-anti-patterns.md +572 -0
- claude_mpm/skills/bundled/testing/testing-anti-patterns/references/core-anti-patterns.md +411 -0
- claude_mpm/skills/bundled/testing/testing-anti-patterns/references/detection-guide.md +569 -0
- claude_mpm/skills/bundled/testing/testing-anti-patterns/references/tdd-connection.md +695 -0
- claude_mpm/skills/bundled/testing/webapp-testing/SKILL.md +184 -0
- claude_mpm/skills/bundled/testing/webapp-testing/decision-tree.md +459 -0
- claude_mpm/skills/bundled/testing/webapp-testing/playwright-patterns.md +479 -0
- claude_mpm/skills/bundled/testing/webapp-testing/reconnaissance-pattern.md +687 -0
- claude_mpm/skills/bundled/testing/webapp-testing/server-management.md +758 -0
- claude_mpm/skills/bundled/testing/webapp-testing/troubleshooting.md +868 -0
- {claude_mpm-5.4.59.dist-info → claude_mpm-5.4.64.dist-info}/METADATA +1 -1
- {claude_mpm-5.4.59.dist-info → claude_mpm-5.4.64.dist-info}/RECORD +128 -11
- {claude_mpm-5.4.59.dist-info → claude_mpm-5.4.64.dist-info}/WHEEL +0 -0
- {claude_mpm-5.4.59.dist-info → claude_mpm-5.4.64.dist-info}/entry_points.txt +0 -0
- {claude_mpm-5.4.59.dist-info → claude_mpm-5.4.64.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-5.4.59.dist-info → claude_mpm-5.4.64.dist-info}/licenses/LICENSE-FAQ.md +0 -0
- {claude_mpm-5.4.59.dist-info → claude_mpm-5.4.64.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,495 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: tauri-async-patterns
|
|
3
|
+
description: Advanced async patterns in Tauri including long-running tasks, background work, cancellation, and concurrent operations
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
category: development
|
|
6
|
+
author: Claude MPM Team
|
|
7
|
+
license: MIT
|
|
8
|
+
progressive_disclosure:
|
|
9
|
+
entry_point:
|
|
10
|
+
summary: "Async mastery: long-running operations with progress, background tasks, cancellation patterns, concurrent processing"
|
|
11
|
+
when_to_use: "Implementing downloads, file processing, background sync, or any long-running async operations"
|
|
12
|
+
quick_start: "1. Use tokio::spawn for background work 2. Emit progress events 3. Implement cancellation with channels 4. Handle graceful shutdown"
|
|
13
|
+
context_limit: 500
|
|
14
|
+
tags:
|
|
15
|
+
- tauri
|
|
16
|
+
- async
|
|
17
|
+
- tokio
|
|
18
|
+
- concurrency
|
|
19
|
+
- background-tasks
|
|
20
|
+
requires_tools: []
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
# Tauri Async Patterns
|
|
24
|
+
|
|
25
|
+
## Long-Running Operations with Progress
|
|
26
|
+
|
|
27
|
+
```rust
|
|
28
|
+
use tokio::time::{sleep, Duration};
|
|
29
|
+
|
|
30
|
+
#[tauri::command]
|
|
31
|
+
async fn long_download(
|
|
32
|
+
url: String,
|
|
33
|
+
window: tauri::Window,
|
|
34
|
+
) -> Result<String, String> {
|
|
35
|
+
let total_chunks = 100;
|
|
36
|
+
|
|
37
|
+
for chunk in 0..total_chunks {
|
|
38
|
+
// Simulate download
|
|
39
|
+
sleep(Duration::from_millis(50)).await;
|
|
40
|
+
|
|
41
|
+
// Emit progress
|
|
42
|
+
window.emit("download-progress", serde_json::json!({
|
|
43
|
+
"current": chunk + 1,
|
|
44
|
+
"total": total_chunks,
|
|
45
|
+
"percentage": ((chunk + 1) as f64 / total_chunks as f64) * 100.0
|
|
46
|
+
})).map_err(|e| e.to_string())?;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
window.emit("download-complete", url.clone())
|
|
50
|
+
.map_err(|e| e.to_string())?;
|
|
51
|
+
|
|
52
|
+
Ok(format!("Downloaded: {}", url))
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Background Tasks
|
|
57
|
+
|
|
58
|
+
### Spawning Background Work
|
|
59
|
+
|
|
60
|
+
```rust
|
|
61
|
+
#[tauri::command]
|
|
62
|
+
async fn start_background_sync(
|
|
63
|
+
app: tauri::AppHandle,
|
|
64
|
+
) -> Result<(), String> {
|
|
65
|
+
// Spawn task that outlives command
|
|
66
|
+
tokio::spawn(async move {
|
|
67
|
+
loop {
|
|
68
|
+
// Perform sync
|
|
69
|
+
match perform_sync().await {
|
|
70
|
+
Ok(_) => {
|
|
71
|
+
if let Some(window) = app.get_window("main") {
|
|
72
|
+
let _ = window.emit("sync-complete", ());
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
Err(e) => {
|
|
76
|
+
log::error!("Sync failed: {}", e);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Wait before next sync
|
|
81
|
+
tokio::time::sleep(Duration::from_secs(300)).await;
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
Ok(())
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### Task Management State
|
|
90
|
+
|
|
91
|
+
```rust
|
|
92
|
+
use std::sync::Arc;
|
|
93
|
+
use tokio::sync::Mutex;
|
|
94
|
+
use std::collections::HashMap;
|
|
95
|
+
|
|
96
|
+
pub struct TaskManager {
|
|
97
|
+
tasks: Arc<Mutex<HashMap<String, TaskHandle>>>,
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
struct TaskHandle {
|
|
101
|
+
cancel_tx: tokio::sync::mpsc::Sender<()>,
|
|
102
|
+
status: Arc<Mutex<TaskStatus>>,
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
#[derive(Clone)]
|
|
106
|
+
enum TaskStatus {
|
|
107
|
+
Running,
|
|
108
|
+
Completed,
|
|
109
|
+
Cancelled,
|
|
110
|
+
Failed(String),
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
impl TaskManager {
|
|
114
|
+
pub async fn spawn_task<F>(&self, id: String, task: F) -> String
|
|
115
|
+
where
|
|
116
|
+
F: Future<Output = Result<(), String>> + Send + 'static,
|
|
117
|
+
{
|
|
118
|
+
let (cancel_tx, mut cancel_rx) = tokio::sync::mpsc::channel::<()>(1);
|
|
119
|
+
let status = Arc::new(Mutex::new(TaskStatus::Running));
|
|
120
|
+
let status_clone = status.clone();
|
|
121
|
+
|
|
122
|
+
tokio::spawn(async move {
|
|
123
|
+
tokio::select! {
|
|
124
|
+
result = task => {
|
|
125
|
+
let mut s = status_clone.lock().await;
|
|
126
|
+
*s = match result {
|
|
127
|
+
Ok(_) => TaskStatus::Completed,
|
|
128
|
+
Err(e) => TaskStatus::Failed(e),
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
_ = cancel_rx.recv() => {
|
|
132
|
+
let mut s = status_clone.lock().await;
|
|
133
|
+
*s = TaskStatus::Cancelled;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
self.tasks.lock().await.insert(id.clone(), TaskHandle {
|
|
139
|
+
cancel_tx,
|
|
140
|
+
status,
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
id
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
pub async fn cancel_task(&self, id: &str) -> Result<(), String> {
|
|
147
|
+
let mut tasks = self.tasks.lock().await;
|
|
148
|
+
if let Some(handle) = tasks.remove(id) {
|
|
149
|
+
handle.cancel_tx.send(()).await.ok();
|
|
150
|
+
Ok(())
|
|
151
|
+
} else {
|
|
152
|
+
Err(format!("Task '{}' not found", id))
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
pub async fn get_status(&self, id: &str) -> Option<TaskStatus> {
|
|
157
|
+
let tasks = self.tasks.lock().await;
|
|
158
|
+
if let Some(handle) = tasks.get(id) {
|
|
159
|
+
Some(handle.status.lock().await.clone())
|
|
160
|
+
} else {
|
|
161
|
+
None
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
## Cancellation Patterns
|
|
168
|
+
|
|
169
|
+
### Using tokio::select! for Cancellation
|
|
170
|
+
|
|
171
|
+
```rust
|
|
172
|
+
#[tauri::command]
|
|
173
|
+
async fn cancellable_operation(
|
|
174
|
+
state: tauri::State<'_, TaskManager>,
|
|
175
|
+
) -> Result<String, String> {
|
|
176
|
+
let task_id = uuid::Uuid::new_v4().to_string();
|
|
177
|
+
|
|
178
|
+
state.spawn_task(task_id.clone(), async move {
|
|
179
|
+
for i in 0..1000 {
|
|
180
|
+
// Check if cancelled via select!
|
|
181
|
+
tokio::time::sleep(Duration::from_millis(10)).await;
|
|
182
|
+
|
|
183
|
+
// Do work
|
|
184
|
+
process_item(i).await?;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
Ok(())
|
|
188
|
+
}).await;
|
|
189
|
+
|
|
190
|
+
Ok(task_id)
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
#[tauri::command]
|
|
194
|
+
async fn cancel_operation(
|
|
195
|
+
task_id: String,
|
|
196
|
+
state: tauri::State<'_, TaskManager>,
|
|
197
|
+
) -> Result<(), String> {
|
|
198
|
+
state.cancel_task(&task_id).await
|
|
199
|
+
}
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
### Manual Cancellation Token
|
|
203
|
+
|
|
204
|
+
```rust
|
|
205
|
+
use std::sync::atomic::{AtomicBool, Ordering};
|
|
206
|
+
|
|
207
|
+
#[tauri::command]
|
|
208
|
+
async fn start_with_token(
|
|
209
|
+
state: tauri::State<'_, AppState>,
|
|
210
|
+
window: tauri::Window,
|
|
211
|
+
) -> Result<String, String> {
|
|
212
|
+
let task_id = uuid::Uuid::new_v4().to_string();
|
|
213
|
+
let cancel_flag = Arc::new(AtomicBool::new(false));
|
|
214
|
+
|
|
215
|
+
// Store cancel flag
|
|
216
|
+
state.cancel_flags.insert(task_id.clone(), cancel_flag.clone());
|
|
217
|
+
|
|
218
|
+
let task_id_clone = task_id.clone();
|
|
219
|
+
tokio::spawn(async move {
|
|
220
|
+
for i in 0..1000 {
|
|
221
|
+
// Check cancellation
|
|
222
|
+
if cancel_flag.load(Ordering::Relaxed) {
|
|
223
|
+
window.emit("task-cancelled", task_id_clone).ok();
|
|
224
|
+
break;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// Do work
|
|
228
|
+
process_item(i).await.ok();
|
|
229
|
+
}
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
Ok(task_id)
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
#[tauri::command]
|
|
236
|
+
fn cancel_with_token(
|
|
237
|
+
task_id: String,
|
|
238
|
+
state: tauri::State<'_, AppState>,
|
|
239
|
+
) -> Result<(), String> {
|
|
240
|
+
if let Some(flag) = state.cancel_flags.get(&task_id) {
|
|
241
|
+
flag.store(true, Ordering::Relaxed);
|
|
242
|
+
Ok(())
|
|
243
|
+
} else {
|
|
244
|
+
Err("Task not found".to_string())
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
## Concurrent Operations
|
|
250
|
+
|
|
251
|
+
### Parallel Processing
|
|
252
|
+
|
|
253
|
+
```rust
|
|
254
|
+
use futures::stream::{self, StreamExt};
|
|
255
|
+
|
|
256
|
+
#[tauri::command]
|
|
257
|
+
async fn process_files_parallel(
|
|
258
|
+
files: Vec<String>,
|
|
259
|
+
window: tauri::Window,
|
|
260
|
+
) -> Result<Vec<String>, String> {
|
|
261
|
+
let results = stream::iter(files)
|
|
262
|
+
.map(|file| {
|
|
263
|
+
let window = window.clone();
|
|
264
|
+
async move {
|
|
265
|
+
let result = process_file(&file).await;
|
|
266
|
+
|
|
267
|
+
window.emit("file-processed", file.clone()).ok();
|
|
268
|
+
|
|
269
|
+
result
|
|
270
|
+
}
|
|
271
|
+
})
|
|
272
|
+
.buffer_unordered(4) // Process 4 at a time
|
|
273
|
+
.collect::<Vec<_>>()
|
|
274
|
+
.await;
|
|
275
|
+
|
|
276
|
+
let successes: Vec<String> = results
|
|
277
|
+
.into_iter()
|
|
278
|
+
.filter_map(|r| r.ok())
|
|
279
|
+
.collect();
|
|
280
|
+
|
|
281
|
+
Ok(successes)
|
|
282
|
+
}
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
### Bounded Concurrency
|
|
286
|
+
|
|
287
|
+
```rust
|
|
288
|
+
use tokio::sync::Semaphore;
|
|
289
|
+
|
|
290
|
+
#[tauri::command]
|
|
291
|
+
async fn batch_download(
|
|
292
|
+
urls: Vec<String>,
|
|
293
|
+
max_concurrent: usize,
|
|
294
|
+
) -> Result<Vec<String>, String> {
|
|
295
|
+
let semaphore = Arc::new(Semaphore::new(max_concurrent));
|
|
296
|
+
let mut handles = Vec::new();
|
|
297
|
+
|
|
298
|
+
for url in urls {
|
|
299
|
+
let permit = semaphore.clone().acquire_owned().await.unwrap();
|
|
300
|
+
|
|
301
|
+
let handle = tokio::spawn(async move {
|
|
302
|
+
let result = download_file(&url).await;
|
|
303
|
+
drop(permit); // Release permit
|
|
304
|
+
result
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
handles.push(handle);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
let results = futures::future::join_all(handles).await;
|
|
311
|
+
|
|
312
|
+
Ok(results.into_iter()
|
|
313
|
+
.filter_map(|r| r.ok().and_then(|r| r.ok()))
|
|
314
|
+
.collect())
|
|
315
|
+
}
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
## Stream Processing
|
|
319
|
+
|
|
320
|
+
```rust
|
|
321
|
+
use tokio::io::{AsyncBufReadExt, BufReader};
|
|
322
|
+
|
|
323
|
+
#[tauri::command]
|
|
324
|
+
async fn stream_log_file(
|
|
325
|
+
filepath: String,
|
|
326
|
+
window: tauri::Window,
|
|
327
|
+
) -> Result<(), String> {
|
|
328
|
+
let file = tokio::fs::File::open(filepath)
|
|
329
|
+
.await
|
|
330
|
+
.map_err(|e| e.to_string())?;
|
|
331
|
+
|
|
332
|
+
let reader = BufReader::new(file);
|
|
333
|
+
let mut lines = reader.lines();
|
|
334
|
+
|
|
335
|
+
while let Some(line) = lines.next_line()
|
|
336
|
+
.await
|
|
337
|
+
.map_err(|e| e.to_string())? {
|
|
338
|
+
|
|
339
|
+
window.emit("log-line", line)
|
|
340
|
+
.map_err(|e| e.to_string())?;
|
|
341
|
+
|
|
342
|
+
// Throttle to avoid overwhelming frontend
|
|
343
|
+
tokio::time::sleep(Duration::from_millis(10)).await;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
window.emit("log-complete", ())
|
|
347
|
+
.map_err(|e| e.to_string())?;
|
|
348
|
+
|
|
349
|
+
Ok(())
|
|
350
|
+
}
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
## Timeout Patterns
|
|
354
|
+
|
|
355
|
+
```rust
|
|
356
|
+
use tokio::time::timeout;
|
|
357
|
+
|
|
358
|
+
#[tauri::command]
|
|
359
|
+
async fn operation_with_timeout(
|
|
360
|
+
duration_secs: u64,
|
|
361
|
+
) -> Result<String, String> {
|
|
362
|
+
let operation = async {
|
|
363
|
+
long_running_task().await
|
|
364
|
+
};
|
|
365
|
+
|
|
366
|
+
match timeout(Duration::from_secs(duration_secs), operation).await {
|
|
367
|
+
Ok(Ok(result)) => Ok(result),
|
|
368
|
+
Ok(Err(e)) => Err(format!("Operation failed: {}", e)),
|
|
369
|
+
Err(_) => Err("Operation timed out".to_string()),
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
## Graceful Shutdown
|
|
375
|
+
|
|
376
|
+
```rust
|
|
377
|
+
use tokio::sync::broadcast;
|
|
378
|
+
|
|
379
|
+
pub struct ShutdownManager {
|
|
380
|
+
shutdown_tx: broadcast::Sender<()>,
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
impl ShutdownManager {
|
|
384
|
+
pub fn new() -> Self {
|
|
385
|
+
let (shutdown_tx, _) = broadcast::channel(1);
|
|
386
|
+
Self { shutdown_tx }
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
pub fn subscribe(&self) -> broadcast::Receiver<()> {
|
|
390
|
+
self.shutdown_tx.subscribe()
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
pub fn shutdown(&self) {
|
|
394
|
+
let _ = self.shutdown_tx.send(());
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
#[tauri::command]
|
|
399
|
+
async fn start_service(
|
|
400
|
+
state: tauri::State<'_, ShutdownManager>,
|
|
401
|
+
) -> Result<(), String> {
|
|
402
|
+
let mut shutdown_rx = state.subscribe();
|
|
403
|
+
|
|
404
|
+
tokio::spawn(async move {
|
|
405
|
+
loop {
|
|
406
|
+
tokio::select! {
|
|
407
|
+
_ = tokio::time::sleep(Duration::from_secs(1)) => {
|
|
408
|
+
// Do work
|
|
409
|
+
perform_service_work().await;
|
|
410
|
+
}
|
|
411
|
+
_ = shutdown_rx.recv() => {
|
|
412
|
+
log::info!("Service shutting down");
|
|
413
|
+
cleanup().await;
|
|
414
|
+
break;
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
Ok(())
|
|
421
|
+
}
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
## Best Practices
|
|
425
|
+
|
|
426
|
+
1. **Always emit progress** - For operations >1 second
|
|
427
|
+
2. **Implement cancellation** - For long-running tasks
|
|
428
|
+
3. **Use tokio::spawn** - For true background work
|
|
429
|
+
4. **Limit concurrency** - Use Semaphore for bounded parallelism
|
|
430
|
+
5. **Add timeouts** - Prevent hung operations
|
|
431
|
+
6. **Handle graceful shutdown** - Clean up resources
|
|
432
|
+
7. **Throttle events** - Don't overwhelm frontend with updates
|
|
433
|
+
8. **Use select!** - For cancellation and timeouts
|
|
434
|
+
9. **Store task handles** - Enable management and cancellation
|
|
435
|
+
10. **Test async paths** - Verify cancellation and timeouts work
|
|
436
|
+
|
|
437
|
+
## Common Pitfalls
|
|
438
|
+
|
|
439
|
+
❌ **Blocking async runtime**:
|
|
440
|
+
```rust
|
|
441
|
+
// WRONG
|
|
442
|
+
std::thread::sleep(Duration::from_secs(1));
|
|
443
|
+
|
|
444
|
+
// CORRECT
|
|
445
|
+
tokio::time::sleep(Duration::from_secs(1)).await;
|
|
446
|
+
```
|
|
447
|
+
|
|
448
|
+
❌ **Forgetting to spawn**:
|
|
449
|
+
```rust
|
|
450
|
+
// WRONG - command waits for completion
|
|
451
|
+
async fn start_background() {
|
|
452
|
+
background_work().await; // Blocks command
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
// CORRECT - spawn to background
|
|
456
|
+
async fn start_background() {
|
|
457
|
+
tokio::spawn(async {
|
|
458
|
+
background_work().await;
|
|
459
|
+
});
|
|
460
|
+
}
|
|
461
|
+
```
|
|
462
|
+
|
|
463
|
+
❌ **No cancellation mechanism**:
|
|
464
|
+
```rust
|
|
465
|
+
// WRONG - can't stop once started
|
|
466
|
+
tokio::spawn(async {
|
|
467
|
+
loop {
|
|
468
|
+
work().await;
|
|
469
|
+
sleep(Duration::from_secs(1)).await;
|
|
470
|
+
}
|
|
471
|
+
});
|
|
472
|
+
|
|
473
|
+
// CORRECT - cancellable
|
|
474
|
+
let (tx, mut rx) = tokio::sync::mpsc::channel(1);
|
|
475
|
+
tokio::spawn(async move {
|
|
476
|
+
loop {
|
|
477
|
+
tokio::select! {
|
|
478
|
+
_ = work() => {}
|
|
479
|
+
_ = rx.recv() => break,
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
});
|
|
483
|
+
```
|
|
484
|
+
|
|
485
|
+
## Summary
|
|
486
|
+
|
|
487
|
+
- **tokio::spawn** for background tasks
|
|
488
|
+
- **tokio::select!** for cancellation
|
|
489
|
+
- **Progress events** for long operations
|
|
490
|
+
- **Semaphore** for bounded concurrency
|
|
491
|
+
- **timeout()** to prevent hung operations
|
|
492
|
+
- **broadcast** for shutdown signals
|
|
493
|
+
- **Stream processing** for large data
|
|
494
|
+
- **Task management** with cancel tokens
|
|
495
|
+
- **Graceful cleanup** on shutdown
|