Back to Index

ProgressMonitor

Inherits from Task

Contents

The ProgressMonitor class is a specialized task that tracks and responds to progress events across multiple tasks. It provides a central mechanism for monitoring task execution progress, consolidating progress information, and generating notifications when significant milestones are reached.

Features

Class Interface

class ProgressMonitor : public Task {
public:
    // Constructor
    ProgressMonitor(float milestoneStep = 0.25f);
    
    // Event handlers
    void onProgress(const ArgumentPack& args);
    void onTaskStarted();
    void onTaskFinished();
    void onTaskError(const ArgumentPack& args);
    
    // Configuration
    void setTaskCount(int count);
    void setMilestoneStep(float step);
    void resetProgress();
    
    // Status methods
    float getOverallProgress() const;
    int getStartedTaskCount() const;
    int getCompletedTaskCount() const;
    int getFailedTaskCount() const;
    bool isComplete() const;
    
    // Milestone management
    void addCustomMilestone(float milestone, const std::string& name);
    void clearCustomMilestones();
};

Signals

ProgressMonitor emits the following signals:

Signal Description Arguments
progressUpdated When overall progress changes overallProgress, completedTasks, totalTasks
milestoneReached When a progress milestone is reached milestoneName, milestoneValue, overallProgress
allTasksComplete When all tasks have completed totalTasks, successCount, failureCount, totalTimeMs
summary When a progress summary is generated taskCount, completedCount, failedCount, overallProgress

Usage Examples

Basic Progress Monitoring

Monitor Multiple Tasks
// Create a progress monitor
auto progressMonitor = std::make_shared<ProgressMonitor>();

// Set the total number of tasks to monitor
progressMonitor->setTaskCount(5);

// Connect to milestone signals
progressMonitor->connectData("milestoneReached", [](const ArgumentPack& args) {
    std::string milestoneName = args.get<std::string>(0);
    float progress = args.get<float>(2);
    
    std::cout << "Milestone reached: " << milestoneName 
              << " (" << (progress * 100) << "%)" << std::endl;
});

// Connect to completion signal
progressMonitor->connectData("allTasksComplete", [](const ArgumentPack& args) {
    int totalTasks = args.get<int>(0);
    int successCount = args.get<int>(1);
    int64_t totalTimeMs = args.get<int64_t>(3);
    
    std::cout << "All tasks completed: " << successCount << "/" << totalTasks
              << " succeeded in " << totalTimeMs << "ms" << std::endl;
});

// Connect tasks to the monitor
for (auto& task : tasks) {
    // Connect task signals to monitor handlers
    task->connectSimple("started", progressMonitor.get(), &ProgressMonitor::onTaskStarted);
    task->connectSimple("finished", progressMonitor.get(), &ProgressMonitor::onTaskFinished);
    task->connectData("error", progressMonitor.get(), &ProgressMonitor::onTaskError);
    task->connectData("progress", progressMonitor.get(), &ProgressMonitor::onProgress);
}

// Add custom milestones if needed
progressMonitor->addCustomMilestone(0.5f, "Halfway Point");
progressMonitor->addCustomMilestone(0.9f, "Almost Complete");

// Get current progress information
float currentProgress = progressMonitor->getOverallProgress();
int completedTasks = progressMonitor->getCompletedTaskCount();

With ThreadPool Integration

Monitoring ThreadPool Progress
// Create a thread pool and progress monitor
auto pool = std::make_shared<ThreadPool>();
auto monitor = std::make_shared<ProgressMonitor>();

// Add tasks to the pool
for (int i = 0; i < 10; i++) {
    auto task = pool->createAndAdd<MyTask>("Task " + std::to_string(i), i * 100);
    
    // Connect task signals to the monitor
    task->connectSimple("started", monitor.get(), &ProgressMonitor::onTaskStarted);
    task->connectSimple("finished", monitor.get(), &ProgressMonitor::onTaskFinished);
    task->connectData("error", monitor.get(), &ProgressMonitor::onTaskError);
    task->connectData("progress", monitor.get(), &ProgressMonitor::onProgress);
}

// Set the expected task count
monitor->setTaskCount(10);

// Connect to milestone signals for updates
monitor->connectData("milestoneReached", [](const ArgumentPack& args) {
    std::string milestoneName = args.get<std::string>(0);
    float progress = args.get<float>(2);
    
    // Update UI with progress
    updateProgressBar(progress);
    showMilestoneNotification(milestoneName, progress);
});

// Connect to completion signal for final status
monitor->connectData("allTasksComplete", [](const ArgumentPack& args) {
    int totalTasks = args.get<int>(0);
    int successCount = args.get<int>(1);
    int failureCount = args.get<int>(2);
    int64_t totalTimeMs = args.get<int64_t>(3);
    
    // Show completion dialog
    showCompletionDialog(successCount, failureCount, totalTimeMs);
});

// Run all tasks
pool->exec();

Custom Progress Monitoring

Custom Milestones and Weighted Progress
// Create a progress monitor with custom milestone step
auto monitor = std::make_shared<ProgressMonitor>(0.1f); // 10% steps

// Add specific named milestones
monitor->addCustomMilestone(0.25f, "Quarter Complete");
monitor->addCustomMilestone(0.5f, "Halfway Point");
monitor->addCustomMilestone(0.75f, "Three-quarters Complete");
monitor->addCustomMilestone(0.95f, "Almost Finished");
monitor->addCustomMilestone(1.0f, "Complete");

// Connect milestone signal to UI updates
monitor->connectData("milestoneReached", [](const ArgumentPack& args) {
    std::string milestoneName = args.get<std::string>(0);
    float milestoneValue = args.get<float>(1);
    
    // Update UI
    updateProgressUI(milestoneValue, milestoneName);
    
    // Log the milestone
    std::cout << "Milestone: " << milestoneName << " at " 
              << (milestoneValue * 100) << "%" << std::endl;
});

// Connect progress updates for continuous UI feedback
monitor->connectData("progressUpdated", [](const ArgumentPack& args) {
    float progress = args.get<float>(0);
    updateProgressBar(progress);
});

// Set up tasks with different weights
// Complex tasks will contribute more to overall progress
setTasksWithWeights(monitor);

How It Works

The ProgressMonitor operates through the following mechanisms:

1. Task Registration

2. Progress Tracking

3. Milestone Detection

4. Task Lifecycle Monitoring

5. Completion Detection

Progress Tracking Internals
void ProgressMonitor::onProgress(const ArgumentPack& args) {
    // Extract progress value from args (typically 0.0 to 1.0)
    float taskProgress = args.get<float>(0);
    
    // Get task ID or use a default identifier
    std::string taskId = "task_" + std::to_string(m_taskProgressMap.size());
    if (args.size() > 1) {
        taskId = args.get<std::string>(1);
    }
    
    // Update progress for this task
    m_taskProgressMap[taskId] = taskProgress;
    
    // Calculate aggregate progress
    calculateOverallProgress();
    
    // Check if we've crossed any milestones
    checkMilestones();
}

Progress Calculation Methods

ProgressMonitor supports multiple methods for calculating overall progress:

1. Average Progress (default)

Average Progress Calculation
float ProgressMonitor::calculateAverageProgress() const {
    if (m_taskProgressMap.empty()) {
        return 0.0f;
    }
    
    float totalProgress = 0.0f;
    
    // Sum all task progress values
    for (const auto& [taskId, progress] : m_taskProgressMap) {
        totalProgress += progress;
    }
    
    // Return average progress
    return totalProgress / m_taskProgressMap.size();
}

2. Weighted Progress

Weighted Progress Calculation
float ProgressMonitor::calculateWeightedProgress() const {
    if (m_taskWeights.empty()) {
        // Fall back to average if no weights defined
        return calculateAverageProgress();
    }
    
    float totalProgress = 0.0f;
    float totalWeight = 0.0f;
    
    // Sum weighted progress values
    for (const auto& [taskId, progress] : m_taskProgressMap) {
        float weight = m_taskWeights.count(taskId) ? m_taskWeights.at(taskId) : 1.0f;
        totalProgress += progress * weight;
        totalWeight += weight;
    }
    
    // Return weighted average
    return totalWeight > 0.0f ? totalProgress / totalWeight : 0.0f;
}

3. Completion Counting

Task Completion Progress
float ProgressMonitor::calculateCompletionProgress() const {
    // Count completed tasks (progress == 1.0)
    int completedCount = 0;
    for (const auto& [taskId, progress] : m_taskProgressMap) {
        if (progress >= 0.99f) { // Allow for floating-point imprecision
            completedCount++;
        }
    }
    
    // Return proportion of completed tasks
    return m_totalTaskCount > 0 ? 
        static_cast<float>(completedCount) / m_totalTaskCount : 0.0f;
}

Use Cases

ProgressMonitor is particularly useful for:

1. Complex Operations

Tracking progress across multi-stage operations such as:

2. UI Progress Bars

Providing accurate progress information to users during lengthy operations:

3. Batch Processing

Monitoring and reporting on batch job progress:

4. ETL Workflows

Tracking data extraction, transformation, and loading progress:

5. Installation/Update Processes

Monitoring multi-step installation processes:

Milestone Management

ProgressMonitor provides flexible milestone configuration:

Standard Milestones

By default, ProgressMonitor tracks these milestones:

Custom Milestones

Application-specific milestones can be added:

Adding Custom Milestones
// Configure milestones for detailed tracking
void setupDetailedMilestones(std::shared_ptr<ProgressMonitor> monitor) {
    // Clear default milestones
    monitor->clearCustomMilestones();
    
    // Add fine-grained milestones
    monitor->addCustomMilestone(0.10f, "Initial Setup");
    monitor->addCustomMilestone(0.20f, "Data Validation");
    monitor->addCustomMilestone(0.30f, "Processing Started");
    monitor->addCustomMilestone(0.40f, "Preliminary Results");
    monitor->addCustomMilestone(0.50f, "Halfway Point");
    monitor->addCustomMilestone(0.60f, "Secondary Processing");
    monitor->addCustomMilestone(0.70f, "Optimization Phase");
    monitor->addCustomMilestone(0.80f, "Final Calculations");
    monitor->addCustomMilestone(0.90f, "Verification Stage");
    monitor->addCustomMilestone(0.95f, "Finalizing Results");
    monitor->addCustomMilestone(1.00f, "Complete");
}

Milestone Step Configuration

The default milestone interval can be adjusted:

Changing Milestone Steps
// Create monitor with 10% milestone steps
auto monitor = std::make_shared<ProgressMonitor>(0.1f);

// Or adjust after creation
monitor->setMilestoneStep(0.05f);  // 5% steps (very fine-grained)

Task Lifecycle Monitoring

ProgressMonitor tracks the full lifecycle of tasks:

Task Started

Task Start Tracking
void ProgressMonitor::onTaskStarted() {
    m_startedTaskCount++;
    
    // Record start time if this is the first task
    if (m_startedTaskCount == 1) {
        m_startTime = std::chrono::steady_clock::now();
    }
    
    // Emit status update
    ArgumentPack args;
    args.add<int>(m_startedTaskCount);
    args.add<int>(m_totalTaskCount);
    emit("taskStarted", args);
}

Task Finished

Task Completion Tracking
void ProgressMonitor::onTaskFinished() {
    m_completedTaskCount++;
    
    // Check if all tasks are now complete
    if (isComplete()) {
        // Calculate total execution time
        auto now = std::chrono::steady_clock::now();
        auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(
            now - m_startTime).count();
        
        // Emit completion signal with summary data
        ArgumentPack args;
        args.add<int>(m_totalTaskCount);
        args.add<int>(m_completedTaskCount);
        args.add<int>(m_failedTaskCount);
        args.add<int64_t>(duration);
        emit("allTasksComplete", args);
    }
}

Task Failed

Task Failure Tracking
void ProgressMonitor::onTaskError(const ArgumentPack& args) {
    m_failedTaskCount++;
    
    // Extract error message if available
    std::string errorMsg = "Unknown error";
    if (!args.empty()) {
        errorMsg = args.get<std::string>(0);
    }
    
    // Log the failure
    ArgumentPack errorArgs;
    errorArgs.add<std::string>(errorMsg);
    errorArgs.add<int>(m_failedTaskCount);
    emit("taskFailed", errorArgs);
    
    // Check if all tasks are now complete (success + failure = total)
    if (isComplete()) {
        onTaskFinished(); // Reuse completion logic
    }
}

Thread Safety

ProgressMonitor is designed to be thread-safe:

Note: While the ProgressMonitor itself is thread-safe, be careful to ensure that UI updates triggered by its signals are performed in a thread-safe manner appropriate to your UI framework.

Best Practices

Implementation Details

Implementation Note: ProgressMonitor is designed to have minimal impact on task performance. It only performs calculations when progress is reported, not continuously polling.