#include #include "./async_executor.hpp" namespace lsp::scheduler { namespace { constexpr const char* kLogTag = "AsyncExecutor"; // Helper for formatting durations template std::string FormatDuration(Duration duration) { auto ms = std::chrono::duration_cast(duration).count(); if (ms < 1000) return std::to_string(ms) + "ms"; else return std::to_string(ms / 1000.0) + "s"; } } // ============= AsyncExecutor Implementation ============= AsyncExecutor::AsyncExecutor(size_t concurrency) : executor_(concurrency) { spdlog::info("{}: Initialized with {} threads (hardware: {})", kLogTag, concurrency, std::thread::hardware_concurrency()); } AsyncExecutor::~AsyncExecutor() { spdlog::info("{}: Shutting down...", kLogTag); LogStatus(); WaitAll(); spdlog::info("{}: Destroyed", kLogTag); } void AsyncExecutor::Submit(const std::string& task_id, TaskFunc task, Callback callback) { auto submit_start = Clock::now(); spdlog::debug("[{}] Submitting task", task_id); auto context = std::make_shared(); context->callback = callback; bool replaced_existing = RegisterTask(task_id, context); LogTaskStart(task_id, replaced_existing); try { executor_.async([this, task_id, task = std::move(task), context]() { ExecuteTask(task_id, std::move(task), context); }); auto duration = GetElapsedTime(submit_start); spdlog::debug("[{}] Submitted in {}", task_id, FormatDuration(duration)); } catch (const std::exception& e) { spdlog::error("[{}] Failed to submit: {}", task_id, e.what()); UnregisterTask(task_id); stats_.IncrementFailed(); throw; } } bool AsyncExecutor::Cancel(const std::string& task_id) { bool cancelled = MarkTaskCancelled(task_id); if (cancelled) spdlog::info("[{}] Cancelled successfully", task_id); else spdlog::debug("[{}] Cancel requested but task not found", task_id); return cancelled; } void AsyncExecutor::WaitAll() { spdlog::info("{}: Waiting for all tasks...", kLogTag); auto wait_start = Clock::now(); executor_.wait_for_all(); auto duration = GetElapsedTime(wait_start); spdlog::info("{}: All tasks completed in {}", kLogTag, FormatDuration(duration)); LogStatus(); } size_t AsyncExecutor::GetRunningTaskCount() const { std::lock_guard lock(mutex_); return running_tasks_.size(); } AsyncExecutor::Statistics AsyncExecutor::GetStatistics() const { std::lock_guard lock(mutex_); return stats_.GetSnapshot(running_tasks_.size()); } void AsyncExecutor::ExecuteTask(const std::string& task_id, TaskFunc task, TaskContextPtr context) { auto task_start = Clock::now(); spdlog::info("[{}] Task execution started", task_id); try { // Check if already cancelled if (context->cancelled.load()) { spdlog::debug("[{}] Task was pre-cancelled", task_id); stats_.IncrementCancelled(); LogTaskCompletion(task_id, task_start, false); UnregisterTask(task_id); return; } // Execute the task spdlog::debug("[{}] Executing task function", task_id); auto result = task(); // Check if cancelled during execution if (context->cancelled.load()) { spdlog::debug("[{}] Task cancelled during execution", task_id); stats_.IncrementCancelled(); } else if (result) { spdlog::trace("[{}] Task produced {} bytes result", task_id, result->size()); if (context->callback && result.has_value()) context->callback(result.value()); stats_.IncrementCompleted(); } else { spdlog::warn("[{}] Task completed with no result", task_id); stats_.IncrementCompleted(); } LogTaskCompletion(task_id, task_start, true); } catch (const std::exception& e) { spdlog::error("[{}] Task failed: {}", task_id, e.what()); stats_.IncrementFailed(); LogTaskCompletion(task_id, task_start, false); } catch (...) { spdlog::error("[{}] Task failed with unknown exception", task_id); stats_.IncrementFailed(); LogTaskCompletion(task_id, task_start, false); } UnregisterTask(task_id); // Periodic status logging if (stats_.completed.load() % kStatusLogInterval == 0) LogStatus(); } bool AsyncExecutor::RegisterTask(const std::string& task_id, TaskContextPtr context) { std::lock_guard lock(mutex_); bool replaced = false; auto it = running_tasks_.find(task_id); if (it != running_tasks_.end()) { spdlog::warn("[{}] Cancelling existing task", task_id); it->second->cancelled.store(true); stats_.IncrementCancelled(); replaced = true; } running_tasks_[task_id] = context; stats_.IncrementSubmitted(); spdlog::debug("[{}] Registered (running: {}, total: {})", task_id, running_tasks_.size(), stats_.submitted.load()); return replaced; } void AsyncExecutor::UnregisterTask(const std::string& task_id) { std::lock_guard lock(mutex_); auto it = running_tasks_.find(task_id); if (it != running_tasks_.end()) { auto lifetime = GetElapsedTime(it->second->start_time); spdlog::trace("[{}] Task lifetime: {}", task_id, FormatDuration(lifetime)); running_tasks_.erase(it); spdlog::debug("[{}] Unregistered (remaining: {})", task_id, running_tasks_.size()); } else { spdlog::warn("[{}] Attempted to unregister non-existent task", task_id); } } bool AsyncExecutor::MarkTaskCancelled(const std::string& task_id) { std::lock_guard lock(mutex_); auto it = running_tasks_.find(task_id); if (it != running_tasks_.end()) { it->second->cancelled.store(true); return true; } return false; } void AsyncExecutor::LogTaskStart(const std::string& task_id, bool replaced_existing) const { auto stats = GetStatistics(); if (replaced_existing) spdlog::info("[{}] Submitted (replaced existing) - Running: {}, Total: {}", task_id, stats.running, stats.submitted); else spdlog::debug("[{}] Submitted - Running: {}, Total: {}", task_id, stats.running, stats.submitted); } void AsyncExecutor::LogTaskCompletion(const std::string& task_id, TimePoint start_time, bool success) const { auto duration = GetElapsedTime(start_time); if (success) spdlog::info("[{}] Completed in {}", task_id, FormatDuration(duration)); else spdlog::warn("[{}] Failed/cancelled after {}", task_id, FormatDuration(duration)); } std::chrono::milliseconds AsyncExecutor::GetElapsedTime(TimePoint start_time) const { return std::chrono::duration_cast(Clock::now() - start_time); } void AsyncExecutor::LogStatus() const { // if (spdlog::get_level() > spdlog::level::debug) // return; // 一次性获取所有需要的数据 std::vector>> active_tasks; Statistics stats; { std::lock_guard lock(mutex_); stats = stats_.GetSnapshot(running_tasks_.size()); for (const auto& [id, context] : running_tasks_) active_tasks.emplace_back(id, std::make_pair(context->start_time, context->cancelled.load())); } auto total = stats.submitted; std::ostringstream status_output; status_output << "\n"; status_output << " ╭────────────────────────────────────────────╮\n"; status_output << " │ 🔔 AsyncExecutor Dashboard │\n"; status_output << " ├────────────────────────────────────────────┤\n"; status_output << " │ │\n"; status_output << " │ 📊 SYSTEM METRICS │\n"; status_output << " │ ────────────────────────────────────── │\n"; status_output << fmt::format(" │ 📤 Submitted ▶ {:>6} total │\n", stats.submitted); status_output << fmt::format(" │ 🚀 Running ▶ {:>6} tasks ({:>5.1f}%) │\n", stats.running, total > 0 ? (stats.running * 100.0 / total) : 0); status_output << fmt::format(" │ ✅ Completed ▶ {:>6} tasks ({:>5.1f}%) │\n", stats.completed, total > 0 ? (stats.completed * 100.0 / total) : 0); status_output << fmt::format(" │ ❌ Failed ▶ {:>6} tasks ({:>5.1f}%) │\n", stats.failed, total > 0 ? (stats.failed * 100.0 / total) : 0); status_output << fmt::format(" │ 🥏 Cancelled ▶ {:>6} tasks ({:>5.1f}%) │\n", stats.cancelled, total > 0 ? (stats.cancelled * 100.0 / total) : 0); status_output << " │ │\n"; if (stats.running > 0) { status_output << " │ 🎯 ACTIVE TASKS │\n"; status_output << " │ ────────────────────────────────────── │\n"; for (const auto& [id, info] : active_tasks) { auto [start_time, is_cancelled] = info; auto runtime = GetElapsedTime(start_time); auto status_icon = is_cancelled ? "🔴" : "🟢"; auto short_id = id.length() > 20 ? id.substr(0, 20) + "…" : id; status_output << fmt::format(" │ {} │ {:22} │ ⏱️ {:<8}│\n", status_icon, short_id, FormatDuration(runtime)); } } status_output << " ╰────────────────────────────────────────────╯"; spdlog::debug("{}", status_output.str()); } }