0

Background

I'm currently developing a program using C++11 for the raspberry pi. The basic design (relevant to this question) is:

  • I have a main loop that's awaiting commands from an external source.
  • In the main loop I create an agent (object that's running in a separate thread) which sleeps until something is added to its queue, in which case it awakens, processes this item, and then checks if there are any items in the queue again before going back to sleep (this process repeats if there is more to process)

In the "processing" of the item, I am simply enabling/disabling GPIO pins one at a time for X amount of seconds.

Processing pseudo-code:

for (Pin pin : pins)
{
    set_pin(pin, HIGH);
    this_thread::sleep_for(chrono::seconds(x))
    set_pin(pin, LOW);
    this_thread::sleep_for(chrono::seconds(y))
}

Obviously, 99.999% of the time of this thread is going to be spent asleep (the only time it's executing code is when it's setting pin outputs (no data is touched here)

Question

How should I go about canceling the processing of the current item from the main thread? I don't want to kill the thread, ever, I just want it to return to it's run loop to process the next item in the queue (or go back to sleep).

I can think of ways to do this, I would just like to hear several ideas from the community and choose the best solution.

Additional code

This is the class running in a separate thread doing the processing of items in the queue. schedule->RunSchedule(schedule) is the call to the function described by the pseudo-code above.

ScheduleThread.cpp

#include "ScheduleThread.h"

ScheduleThread::ScheduleThread()
    : thread(&ScheduleThread::run, this)
{
}

ScheduleThread::~ScheduleThread() {
    // TODO Auto-generated destructor stub
}

void ScheduleThread::QueueSchedule(Schedule *schedule)
{
    lock_guard<mutex> lock(m);
    schedule_queue.push(schedule);
    stateChangedSema.post();
}

bool ScheduleThread::scheduler()
{
    if (!schedule_queue.empty())
    {
        Schedule *schedule = schedule_queue.front();
        schedule->RunSchedule();
        schedule_queue.pop();
        return true;
    }
    return false;
}

void ScheduleThread::run()
{
    for(;;)
    {
        stateChangedSema.wait();
        while (scheduler());
    }
}

Thanks in advance for any help.

Community
  • 1
  • 1
Andrew
  • 638
  • 1
  • 6
  • 10
  • Unrelated red flag: You're using mutex `m` to protect `schedule_queue` in `QueueSchedule`, but not in `scheduler`. My data race sense is tingling. – Casey Jul 13 '13 at 06:56
  • If `schedule_queue` is a single-producer-single-consumer queue and you're using the mutex to synchronize multiple producers, then my data-race-sense is wrong and needs fixing. – Casey Jul 13 '13 at 07:04

3 Answers3

0

I don't know if I understood well what you're trying to do, but if you want to communicate with a certain thread from the main thread you may simply set a flag (declared as a global variable) and make the threads consult this flag in order to change their behavior as you desire. For instance, you may add this variable to the while statement that keeps executing your scheduler() function. Other idea may involve the use of condition variables.

Hope it helps.

Str1101
  • 859
  • 2
  • 12
  • 22
0

Look into Condition variables

Instead of "sleeping", the thread blocks on the condition variable, waking up when it's signalled. The blocking can be a time-out - so if the condvar times out, the thread can do one thing (go round the loop again, for instance), and if the condvar is signalled, it can do something else.

Pay attention to the wait_for warnings about spurious waking up.

Pseudo-ish code might be:

// assumes the condvar is triggered when cancellation is reqd.

if(condvar.wait_for( lock, std::chrono::seconds( x ) ) != std::cv_status::timeout)
    return;
set_pin(pin, HIGH);
if(condvar.wait_for( lock, std::chrono::seconds( y ) ) != std::cv_status::timeout)
    return;
set_pin(pin, LOW);

Or have I misunderstood what you're after?

SteveLove
  • 3,137
  • 15
  • 17
0

Hi I have an example that will help you understand how condition variable work. You declare two threads (two infinite loops).

  • One that takes user input and signals to the other thread that one input is ready to be processed
  • The other one that processes it and signals that he's done

Here is the code

#include <thread>
#include <chrono>
#include <mutex>
#include <iostream>
#include <string>
#include <condition_variable>
#include <atomic>
using namespace std;

//this simulates any action from the user (use it for your pin for example)
int GetUserName()
{
    while (true)
    {
        cout<<"Enter your name " << endl;
        cin>> UserName;
        NewName=true;//one new name is ready to be processed
        cv.notify_one();

        // Wait until the naame has been processed
        {
            std::unique_lock<std::mutex> lk(m);
            cv.wait(lk, []{return (NewName==false);});
        }   
    }
    return 0;
}

//this reacts to any action of the user, processes the data and signals that he's done
int ProcessName()
{
    while (true)
    {
            //waiting for one data to be processed
        {
            std::unique_lock<std::mutex> lk(m);
            cv.wait(lk, []{return (NewName==true);});
        }
        cout<<"Hey "+UserName<<"!"<<endl;
        NewName=false;//sets to flase because the data are processed
        cv.notify_one();//I have processed the data, the user can input something else
    }
    return 0;
}

Tell me if that helps, o if you have any questions/remarks

Gabriel
  • 3,564
  • 1
  • 27
  • 49