Question

I don't think the title describes the situation exactly. I apologize for the poor description.

I'm trying to write a software system, in C++, that will have a heavy job (scanning the file system for a particular file or for infections, or just copying/moving a file from one place to another). This system has an interactive component which provides an opportunity for the user to pause and resume the operation. Given that we have a user interface, to achieve separation of concerns, I'm planning to have the job done on a separate thread of execution (within the same process) or a different process (if the interface and operation should be separated by process boundaries). I do not want the UI request handling logic creep into the job handler at all. Now, how should I design the system ? What (synchronization) primitives should I use here ?

I'm new to asynchronous programming. I understand that, the data structures to maintain (progress, current item in progress, checking integrity while resuming, part of job that is completed yet) will purely depend on the application. However, is there a general way to approach this problem ? I'm looking for a platform independent approach.

I assuming this is arguable and there may not be one correct answer, but any suggestions would help.

Was it helpful?

Solution

I've seen this done with a job processing class that runs on its own thread. It looked something like this:

class JobQueue {
public:
    JobQueue();
    ~JobQueue();

    void EnqueueJob(Job newJob);
    void ResumeProcessing();
    void PauseProcessing();
    bool IsProcessing();

private:
    std::queue<Job> jobs;
    bool processing {false}; // May need more states, but keeping it simple here
    mutex jobQueueMutex;

    void ProcessLoop();
};

The mutex can be another type of primitive if that's more convenient/efficient for your needs. But a typical implementation would be something like this:

void JobQueue::EnqueueJob(Job newJob)
{
    std::lock_guard<std::mutex> lk(jobQueueMutex);

    jobs.push_back(newJob);
}

void JobQueue::ResumeProcessing()
{
    std::lock_guard<std::mutex> lk(jobQueueMutex);

    processing = true;
}

void JobQueue::PauseProcessing()
{
    std::lock_guard<std::mutex> lk(jobQueueMutex);

    processing = false;
}

bool IsProcessing()
{
    std::lock_guard<std::mutex> lk(jobQueueMutex);

    return isProcessing;
}

void JobQueue::ProcessLoop()
{
    while (1)
    {
        std::unique_lock<std::mutex> lk(jobQueueMutex);
        Job* nextJob = nullptr;
        if (jobs.size() > 0)
        {
            nextJob = jobs.pop_front();
        }
        lk.unlock();

        if (nextJob != nullptr)
        {
            nextJob->Process();
        }

        while (!isProcessing) {} // Probably don't want to busy wait, just an example. Could use semaphore or other mechanism
    }
}

The idea here is that you would call EnqueueJob() on the UI thread or any other thread. You can pause, resume, or check whether processing is occurring on any thread as well. The ProcessLoop() function would run on the processing thread.

Licensed under: CC-BY-SA with attribution
scroll top