ESP32: Count backwards asynchronously - c++

I've written a little program which counts back from 5 to 0 and does a println afterwards. I've wrapped this a little bit, but please let me show my code:
Main.ino
#include "MyObject.h"
#include <string>
using namespace std;
MyObjekt *myObject;
void setup() {
Serial.begin(115200);
string trigger = "triggering";
myObject = new MyObject(trigger);
}
void loop(){}
MyObject.h
#ifndef MYOBJECT_H
#define MYOBJECT_H
#include <string>
using namespace std;
class MyObject{
public:
string field;
MyObject(string trigger);
string GetField(){ return field; }
void SetField(string trigger);
};
#endif
MyObject.cpp
#include "MyObject.h"
#include <string>
using namespace std;
#include "Timer.h"
MyObject::MyObject(string trigger){
SetField(trigger);
}
void MyObject::SetField(string trigger){
field = trigger;
auto f = []() {std::cout << "---------------- I waited to print! ----------------\n"; };
Timer t1{10000,f};
}
Timer.h
#include <iostream>
#include <chrono>
#include <thread>
#include <functional>
#include <mutex>
#include <condition_variable>
class Timer {
public:
Timer(size_t time, const std::function<void(void)>& f) : time{std::chrono::milliseconds{time}}, f{f} {}
~Timer() {wait_thread.join();}
private:
void wait_then_call()
{
std::unique_lock<std::mutex> lck{mtx};
for(int i{5}; i > 0; --i) {
//std::cout << "Thread " << wait_thread.get_id() << " countdown at: " << '\t' << i << '\n';
cv.wait_for(lck, time / 10);
}
f();
}
std::mutex mtx;
std::condition_variable cv{};
std::chrono::milliseconds time;
std::function <void(void)> f;
std::thread wait_thread{[this]() {wait_then_call();}};
};
Unfortately this blocks the main thread, so nothing other (like another println) is done during this. Is it somehow possible to do this countdown in the background and only do the println (f ) in the foreground (in other words: listening while doing work, if background-println is detected/sent to listener, execute, then listen again and continue with work)?
Would be really happy about every answer and help effort. Sorry if for my Problems expressing myself, i hope it became somehow clear what I am trying to achieve^^
Best regards

It looks like you're on Arduino. Using the arduino-timer library, the code should look something similar:
#include <arduino-timer.h>
const size_t TIMER_INTERVAL_MS = 1000;
volatile int counter = 5;
auto timer = timer_create_default();
void setup() {
timer.every(TIMER_INTERVAL_MS, [](void*) -> bool { counter--; return true; });
}
void loop() {
timer.tick();
if (counter <= 0) {
Serial.println("Counter is 0");
counter = 5; // Reset counter
}
Sorry, can't validate or run the code as I don't have an Arduino setup ready to go. But you should get the point.
You can also do more complex solutions like ask the timer peripheral for an interrupt; or set up timer service in the RTOS (assuming you've upgraded to one). The basic principle is the same.

First of all, don't use keyword new if you never call delete, as it would cause memory leak. What do you want can be achieved with two ways:
1. Use Counter
This is still run on the loopTask, but it is let other code to run.
long lastMillis = 0;
long interval = 1000;
long counter = 5;
void setup() {
Serial.begin(115200);
}
void loop() {
if (millis() - lastMillis > interval && counter >= 0) {
lastMillis = millis();
Serial.println(counter--);
}
//Other code would still run
}
2. Create Another Task
This would be completely asynchronous, even if you are calling delay() on the other task, the code on the loopTask would still run.
int counter = 5;
int interval = 1000;
void vTask(void *param) {
while (counter >= 0) {
delay(1000);
Serial.println(counter--);
}
vTaskDelete(NULL);
}
void setup() {
Serial.begin(115200);
xTaskCreate(vTask, "vTask", 4096, NULL, 1, NULL);
}
void loop() {}

Related

thread pooling in c++ - how to end the program

I've implemented thread pooling following the answer of Kerrek SB in this question.
I've implemented MPMC queue for the functions and vector threads for the threads.
Everything worked perfectly, except that I don't know how to terminate the program, in the end if I just do thread.join since the thread is still waiting for more tasks to do, it will not join and the main thread will not continue.
Any idea how to end the program correctly?
For completeness, this is my code:
function_pool.h
#pragma once
#include <queue>
#include <functional>
#include <mutex>
#include <condition_variable>
class Function_pool
{
private:
std::queue<std::function<void()>> m_function_queue;
std::mutex m_lock;
std::condition_variable m_data_condition;
public:
Function_pool();
~Function_pool();
void push(std::function<void()> func);
std::function<void()> pop();
};
function_pool.cpp
#include "function_pool.h"
Function_pool::Function_pool() : m_function_queue(), m_lock(), m_data_condition()
{
}
Function_pool::~Function_pool()
{
}
void Function_pool::push(std::function<void()> func)
{
std::unique_lock<std::mutex> lock(m_lock);
m_function_queue.push(func);
// when we send the notification immediately, the consumer will try to
get the lock , so unlock asap
lock.unlock();
m_data_condition.notify_one();
}
std::function<void()> Function_pool::pop()
{
std::unique_lock<std::mutex> lock(m_lock);
m_data_condition.wait(lock, [this]() {return !m_function_queue.empty();
});
auto func = m_function_queue.front();
m_function_queue.pop();
return func;
// Lock will be released
}
main.cpp
#include "function_pool.h"
#include <string>
#include <iostream>
#include <mutex>
#include <functional>
#include <thread>
#include <vector>
Function_pool func_pool;
void example_function()
{
std::cout << "bla" << std::endl;
}
void infinite_loop_func()
{
while (true)
{
std::function<void()> func = func_pool.pop();
func();
}
}
int main()
{
std::cout << "stating operation" << std::endl;
int num_threads = std::thread::hardware_concurrency();
std::cout << "number of threads = " << num_threads << std::endl;
std::vector<std::thread> thread_pool;
for (int i = 0; i < num_threads; i++)
{
thread_pool.push_back(std::thread(infinite_loop_func));
}
//here we should send our functions
func_pool.push(example_function);
for (int i = 0; i < thread_pool.size(); i++)
{
thread_pool.at(i).join();
}
int i;
std::cin >> i;
}
Your problem is located in infinite_loop_func, which is an infinite loop and by result doesn't terminate. I've read the previous answer which suggests throwing an exception, however, I don't like it since exceptions should not be used for the regular control flow.
The best way to solve this is to explicitly deal with the stop condition. For example:
std::atomic<bool> acceptsFunctions;
Adding this to the function pool allows you to clearly have state and to assert that no new functions being added when you destruct.
std::optional<std::function<void()>> Function_pool::pop()
Returning an empty optional (or function in C++14 and before), allows you to deal with an empty queue. You have to, as condition_variable can do spurious wakeups.
With this, m_data_condition.notify_all() can be used to wake all threads.
Finally we have to fix the infinite loop as it doesn't cover overcommitment and at the same time allows you to execute all functions still in the queue:
while (func_pool.acceptsFunctions || func_pool.containsFunctions())
{
auto f = func_pool.pop();
If (!f)
{
func_pool.m_data_condition.wait_for(1s);
continue;
}
auto &function = *f;
function ();
}
I'll leave it up to you to implement containsFunctions() and clean up the code (infinite_loop_func as member function?) Note that with a counter, you could even deal with background task being spawned.
You can always use a specific exception type to signal to infinite_loop_func that it should return...
class quit_worker_exception: public std::exception {};
Then change infinite_loop_func to...
void infinite_loop_func ()
{
while (true) {
std::function<void()> func = func_pool.pop();
try {
func();
}
catch (quit_worker_exception &ex) {
return;
}
}
}
With the above changes you could then use (in main)...
/*
* Enqueue `thread_pool.size()' function objects whose sole job is
* to throw an instance of `quit_worker_exception' when invoked.
*/
for (int i = 0; i < thread_pool.size(); i++)
func_pool.push([](){ throw quit_worker_exception(); });
/*
* Now just wait for each worker to terminate having received its
* quit_worker_exception.
*/
for (int i = 0; i < thread_pool.size(); i++)
thread_pool.at(i).join();
Each instance of infinite_loop_func will dequeue one function object which, when called, throws a quit_worker_exception causing it to return.
Follwoing [JVApen](https://stackoverflow.com/posts/51382714/revisions) suggestion, I copy my code in case anyone will want a working code:
function_pool.h
#pragma once
#include <queue>
#include <functional>
#include <mutex>
#include <condition_variable>
#include <atomic>
#include <cassert>
class Function_pool
{
private:
std::queue<std::function<void()>> m_function_queue;
std::mutex m_lock;
std::condition_variable m_data_condition;
std::atomic<bool> m_accept_functions;
public:
Function_pool();
~Function_pool();
void push(std::function<void()> func);
void done();
void infinite_loop_func();
};
function_pool.cpp
#include "function_pool.h"
Function_pool::Function_pool() : m_function_queue(), m_lock(), m_data_condition(), m_accept_functions(true)
{
}
Function_pool::~Function_pool()
{
}
void Function_pool::push(std::function<void()> func)
{
std::unique_lock<std::mutex> lock(m_lock);
m_function_queue.push(func);
// when we send the notification immediately, the consumer will try to get the lock , so unlock asap
lock.unlock();
m_data_condition.notify_one();
}
void Function_pool::done()
{
std::unique_lock<std::mutex> lock(m_lock);
m_accept_functions = false;
lock.unlock();
// when we send the notification immediately, the consumer will try to get the lock , so unlock asap
m_data_condition.notify_all();
//notify all waiting threads.
}
void Function_pool::infinite_loop_func()
{
std::function<void()> func;
while (true)
{
{
std::unique_lock<std::mutex> lock(m_lock);
m_data_condition.wait(lock, [this]() {return !m_function_queue.empty() || !m_accept_functions; });
if (!m_accept_functions && m_function_queue.empty())
{
//lock will be release automatically.
//finish the thread loop and let it join in the main thread.
return;
}
func = m_function_queue.front();
m_function_queue.pop();
//release the lock
}
func();
}
}
main.cpp
#include "function_pool.h"
#include <string>
#include <iostream>
#include <mutex>
#include <functional>
#include <thread>
#include <vector>
Function_pool func_pool;
class quit_worker_exception : public std::exception {};
void example_function()
{
std::cout << "bla" << std::endl;
}
int main()
{
std::cout << "stating operation" << std::endl;
int num_threads = std::thread::hardware_concurrency();
std::cout << "number of threads = " << num_threads << std::endl;
std::vector<std::thread> thread_pool;
for (int i = 0; i < num_threads; i++)
{
thread_pool.push_back(std::thread(&Function_pool::infinite_loop_func, &func_pool));
}
//here we should send our functions
for (int i = 0; i < 50; i++)
{
func_pool.push(example_function);
}
func_pool.done();
for (unsigned int i = 0; i < thread_pool.size(); i++)
{
thread_pool.at(i).join();
}
}

c++ Run every 10 minute

I want this program run every start(call) 10min.
But I did not find a solution how-to call(start) Program every 10 minutes, on c++ code (man.exe).
I would like to use the code in visual studio 2013
int runevery() {
system("start man.exe");
return true;
}
Call:
#ifdef MAN_RUN
runevery();
#endif
Thank you for your help in advance!
You can create another thread that executes that function periodically until stopped. Example:
#include <mutex>
#include <chrono>
#include <thread>
#include <iostream>
#include <functional>
#include <condition_variable>
class PeriodicAction {
std::mutex m_;
std::condition_variable c_;
bool stop_ = false;
std::function<void()> const f_;
std::chrono::seconds const initial_delay_;
std::chrono::seconds const delay_;
std::thread thread_;
bool wait(std::chrono::seconds delay) {
std::unique_lock<std::mutex> lock(m_);
c_.wait_for(lock, delay, [this]() { return stop_; });
return !stop_;
}
void thread_fn() {
for(auto delay = initial_delay_; this->wait(delay); delay = delay_)
f_();
}
public:
PeriodicAction(std::chrono::seconds initial_delay,
std::chrono::seconds delay,
std::function<void()> f)
: f_(move(f))
, initial_delay_(initial_delay)
, delay_(delay)
, thread_(&PeriodicAction::thread_fn, this)
{}
~PeriodicAction() {
this->stop();
thread_.join();
}
void stop() {
{
std::unique_lock<std::mutex> lock(m_);
stop_ = true;
}
c_.notify_one();
}
};
char const* now_c_str() {
auto time_t = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
return std::ctime(&time_t);
}
int main(int ac, char**) {
using namespace std::literals::chrono_literals;
// Print current time for the next 5 seconds and then terminate.
PeriodicAction a(0s, 1s, []() { std::cout << now_c_str(); });
std::this_thread::sleep_for(5s);
}
Applying to your case:
PeriodicAction a(0s, 600s, [](){ system("start man.exe"); });
I don't think this is a good idea, but it's easy to achieve like this:
#include <thread>
#include <chrono>
int main()
{
while (true)
{
std::this_thread::sleep_for(std::chrono::minutes(10));
system("man.exe");
}
}
I still think as per my earlier comment that a scheduled task on Windows would be better behaved and easier configurable.

C++: creating an event repeatedly called by timer

I wanted to redo a Conway's Game of Life thing I did in Java, but this time use ncurses and C++. Obviously I need a timed event so I can run out the simulation at a rate which it can be viewed. Turns out it is a hell of a lot harder to make a timed event in C++ than it is in Java. I am not as experienced in C++ than I am in Java. I have already looked around online, and what I found led me to the code I have below. Upon executing it produces no result in the terminal. What exactly am I doing wrong?
main.cpp:
#include <iostream>
#include <functional>
#include <chrono>
#include <future>
#include <cstdio>
using namespace std;
class callBackTimer //no idea how this works, got it from Stack Overflow thread
{
public:
callBackTimer()
:_execute(false)
{}
void start(int interval, std::function<void(void)> func)
{
_execute = true;
std::thread([=]()
{
while (_execute)
{
func();
std::this_thread::sleep_for(
std::chrono::milliseconds(interval));
}
}).detach();
}
void stop()
{
_execute = false;
}
private:
bool _execute;
};
void timerExec()
{
cout << "SNAFU" << endl;
}
int main(int argc, const char * argv[])
{
callBackTimer timer; //declare the timer
std::function<void(void)> exec = timerExec; //declare a pointer to timerExec
timer.start(25, std::bind(exec)); //start the timer
return 0;
}
You need to wait for the thread to finish what it is doing, which is usually accomplished by calling join(). So maybe something like this:
#include <iostream>
#include <functional>
#include <chrono>
#include <future>
#include <cstdio>
using namespace std;
class callBackTimer //no idea how this works, got it from Stack Overflow thread
{
public:
callBackTimer()
:_execute(false)
{}
void setup(int interval, std::function<void(void)> func)
{
_execute = true;
thread = std::thread([=]()
{
// while (_execute)
for (int steps = 0; steps < 100; ++steps)
{
func();
std::this_thread::sleep_for(std::chrono::milliseconds(interval));
}
});
}
void stop()
{
_execute = false;
}
void run()
{
thread.join();
}
private:
bool _execute;
std::thread thread;
};
void timerExec()
{
cout << "SNAFU" << endl;
}
int main(int argc, const char * argv[])
{
callBackTimer timer; //declare the timer
std::function<void(void)> exec = timerExec; //declare a pointer to timerExec
timer.setup(25, std::bind(exec)); //start the timer
timer.run();
return 0;
}
Calling detach() is OK, but you have to make main() wait for the thread manually. You also need a condition to break out of the while loop, otherwise it will go on forever. Hope that helps!

How to create timer events using C++ 11?

How to create timer events using C++ 11?
I need something like: “Call me after 1 second from now”.
Is there any library?
Made a simple implementation of what I believe to be what you want to achieve. You can use the class later with the following arguments:
int (milliseconds to wait until to run the code)
bool (if true it returns instantly and runs the code after specified time on another thread)
variable arguments (exactly what you'd feed to std::bind)
You can change std::chrono::milliseconds to std::chrono::nanoseconds or microseconds for even higher precision and add a second int and a for loop to specify for how many times to run the code.
Here you go, enjoy:
#include <functional>
#include <chrono>
#include <future>
#include <cstdio>
class later
{
public:
template <class callable, class... arguments>
later(int after, bool async, callable&& f, arguments&&... args)
{
std::function<typename std::result_of<callable(arguments...)>::type()> task(std::bind(std::forward<callable>(f), std::forward<arguments>(args)...));
if (async)
{
std::thread([after, task]() {
std::this_thread::sleep_for(std::chrono::milliseconds(after));
task();
}).detach();
}
else
{
std::this_thread::sleep_for(std::chrono::milliseconds(after));
task();
}
}
};
void test1(void)
{
return;
}
void test2(int a)
{
printf("%i\n", a);
return;
}
int main()
{
later later_test1(1000, false, &test1);
later later_test2(1000, false, &test2, 101);
return 0;
}
Outputs after two seconds:
101
The asynchronous solution from Edward:
create new thread
sleep in that thread
do the task in that thread
is simple and might just work for you.
I would also like to give a more advanced version which has these advantages:
no thread startup overhead
only a single extra thread per process required to handle all timed tasks
This might be in particular useful in large software projects where you have many task executed repetitively in your process and you care about resource usage (threads) and also startup overhead.
Idea: Have one service thread which processes all registered timed tasks. Use boost io_service for that.
Code similar to:
http://www.boost.org/doc/libs/1_65_1/doc/html/boost_asio/tutorial/tuttimer2/src.html
#include <cstdio>
#include <boost/asio.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
int main()
{
boost::asio::io_service io;
boost::asio::deadline_timer t(io, boost::posix_time::seconds(1));
t.async_wait([](const boost::system::error_code& /*e*/){
printf("Printed after 1s\n"); });
boost::asio::deadline_timer t2(io, boost::posix_time::seconds(1));
t2.async_wait([](const boost::system::error_code& /*e*/){
printf("Printed after 1s\n"); });
// both prints happen at the same time,
// but only a single thread is used to handle both timed tasks
// - namely the main thread calling io.run();
io.run();
return 0;
}
Use RxCpp,
std::cout << "Waiting..." << std::endl;
auto values = rxcpp::observable<>::timer<>(std::chrono::seconds(1));
values.subscribe([](int v) {std::cout << "Called after 1s." << std::endl;});
This is the code I have so far:
I am using VC++ 2012 (no variadic templates)
//header
#include <thread>
#include <mutex>
#include <condition_variable>
#include <vector>
#include <chrono>
#include <memory>
#include <algorithm>
template<class T>
class TimerThread
{
typedef std::chrono::high_resolution_clock clock_t;
struct TimerInfo
{
clock_t::time_point m_TimePoint;
T m_User;
template <class TArg1>
TimerInfo(clock_t::time_point tp, TArg1 && arg1)
: m_TimePoint(tp)
, m_User(std::forward<TArg1>(arg1))
{
}
template <class TArg1, class TArg2>
TimerInfo(clock_t::time_point tp, TArg1 && arg1, TArg2 && arg2)
: m_TimePoint(tp)
, m_User(std::forward<TArg1>(arg1), std::forward<TArg2>(arg2))
{
}
};
std::unique_ptr<std::thread> m_Thread;
std::vector<TimerInfo> m_Timers;
std::mutex m_Mutex;
std::condition_variable m_Condition;
bool m_Sort;
bool m_Stop;
void TimerLoop()
{
for (;;)
{
std::unique_lock<std::mutex> lock(m_Mutex);
while (!m_Stop && m_Timers.empty())
{
m_Condition.wait(lock);
}
if (m_Stop)
{
return;
}
if (m_Sort)
{
//Sort could be done at insert
//but probabily this thread has time to do
std::sort(m_Timers.begin(),
m_Timers.end(),
[](const TimerInfo & ti1, const TimerInfo & ti2)
{
return ti1.m_TimePoint > ti2.m_TimePoint;
});
m_Sort = false;
}
auto now = clock_t::now();
auto expire = m_Timers.back().m_TimePoint;
if (expire > now) //can I take a nap?
{
auto napTime = expire - now;
m_Condition.wait_for(lock, napTime);
//check again
auto expire = m_Timers.back().m_TimePoint;
auto now = clock_t::now();
if (expire <= now)
{
TimerCall(m_Timers.back().m_User);
m_Timers.pop_back();
}
}
else
{
TimerCall(m_Timers.back().m_User);
m_Timers.pop_back();
}
}
}
template<class T, class TArg1>
friend void CreateTimer(TimerThread<T>& timerThread, int ms, TArg1 && arg1);
template<class T, class TArg1, class TArg2>
friend void CreateTimer(TimerThread<T>& timerThread, int ms, TArg1 && arg1, TArg2 && arg2);
public:
TimerThread() : m_Stop(false), m_Sort(false)
{
m_Thread.reset(new std::thread(std::bind(&TimerThread::TimerLoop, this)));
}
~TimerThread()
{
m_Stop = true;
m_Condition.notify_all();
m_Thread->join();
}
};
template<class T, class TArg1>
void CreateTimer(TimerThread<T>& timerThread, int ms, TArg1 && arg1)
{
{
std::unique_lock<std::mutex> lock(timerThread.m_Mutex);
timerThread.m_Timers.emplace_back(TimerThread<T>::TimerInfo(TimerThread<T>::clock_t::now() + std::chrono::milliseconds(ms),
std::forward<TArg1>(arg1)));
timerThread.m_Sort = true;
}
// wake up
timerThread.m_Condition.notify_one();
}
template<class T, class TArg1, class TArg2>
void CreateTimer(TimerThread<T>& timerThread, int ms, TArg1 && arg1, TArg2 && arg2)
{
{
std::unique_lock<std::mutex> lock(timerThread.m_Mutex);
timerThread.m_Timers.emplace_back(TimerThread<T>::TimerInfo(TimerThread<T>::clock_t::now() + std::chrono::milliseconds(ms),
std::forward<TArg1>(arg1),
std::forward<TArg2>(arg2)));
timerThread.m_Sort = true;
}
// wake up
timerThread.m_Condition.notify_one();
}
//sample
#include <iostream>
#include <string>
void TimerCall(int i)
{
std::cout << i << std::endl;
}
int main()
{
std::cout << "start" << std::endl;
TimerThread<int> timers;
CreateTimer(timers, 2000, 1);
CreateTimer(timers, 5000, 2);
CreateTimer(timers, 100, 3);
std::this_thread::sleep_for(std::chrono::seconds(5));
std::cout << "end" << std::endl;
}
If you are on Windows, you can use the CreateThreadpoolTimer function to schedule a callback without needing to worry about thread management and without blocking the current thread.
template<typename T>
static void __stdcall timer_fired(PTP_CALLBACK_INSTANCE, PVOID context, PTP_TIMER timer)
{
CloseThreadpoolTimer(timer);
std::unique_ptr<T> callable(reinterpret_cast<T*>(context));
(*callable)();
}
template <typename T>
void call_after(T callable, long long delayInMs)
{
auto state = std::make_unique<T>(std::move(callable));
auto timer = CreateThreadpoolTimer(timer_fired<T>, state.get(), nullptr);
if (!timer)
{
throw std::runtime_error("Timer");
}
ULARGE_INTEGER due;
due.QuadPart = static_cast<ULONGLONG>(-(delayInMs * 10000LL));
FILETIME ft;
ft.dwHighDateTime = due.HighPart;
ft.dwLowDateTime = due.LowPart;
SetThreadpoolTimer(timer, &ft, 0 /*msPeriod*/, 0 /*msWindowLength*/);
state.release();
}
int main()
{
auto callback = []
{
std::cout << "in callback\n";
};
call_after(callback, 1000);
std::cin.get();
}
I'm looking for a simple solution and everything I found is too long and complicated. After reading the documentation, I found that this can be done in just a few lines of code.
This question may be old but can beneficial to future researchers.
Example: Set isContinue to false if you want to stop the thread.
#include <chrono>
#include <thread>
volatile bool isContinue = true;
void NameOfYourFunction(){
while(continue){
std::this_thread::sleep_for(std::chrono::milliseconds(1000)); //sleep for 1 seconds
//do something here after every 1 seconds...
}
}
int main(){
std::thread your_thread(NameOfYourFunction); // Register your `YourFunction`.
your_thread.detach(); // this will be non-blocking thread.
//your_thread.join(); // this will be blocking thread.
}
use detach() or join() depending on your situation.
When using detach(), the execution main thread continues running.
When using join(), the execution main thread pauses and waits until
the new thread ends.

Implementing an event timer using boost::asio

The sample code looks long, but actually it's not so complicated :-)
What I'm trying to do is, when a user calls EventTimer.Start(), it will execute the callback handler (which is passed into the ctor) every interval milliseconds for repeatCount times.
You just need to look at the function EventTimer::Stop()
#include <iostream>
#include <string>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/thread.hpp>
#include <boost/function.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <ctime>
#include <sys/timeb.h>
#include <Windows.h>
std::string CurrentDateTimeTimestampMilliseconds() {
double ms = 0.0; // Milliseconds
struct timeb curtime;
ftime(&curtime);
ms = (double) (curtime.millitm);
char timestamp[128];
time_t now = time(NULL);
struct tm *tp = localtime(&now);
sprintf(timestamp, "%04d%02d%02d-%02d%02d%02d.%03.0f",
tp->tm_year + 1900, tp->tm_mon + 1, tp->tm_mday, tp->tm_hour, tp->tm_min, tp->tm_sec, ms);
return std::string(timestamp);
}
class EventTimer
{
public:
static const int kDefaultInterval = 1000;
static const int kMinInterval = 1;
static const int kDefaultRepeatCount = 1;
static const int kInfiniteRepeatCount = -1;
static const int kDefaultOffset = 10;
public:
typedef boost::function<void()> Handler;
EventTimer(Handler handler = NULL)
: interval(kDefaultInterval),
repeatCount(kDefaultRepeatCount),
handler(handler),
timer(io),
exeCount(-1)
{
}
virtual ~EventTimer()
{
}
void SetInterval(int value)
{
// if (value < 1)
// throw std::exception();
interval = value;
}
void SetRepeatCount(int value)
{
// if (value < 1)
// throw std::exception();
repeatCount = value;
}
bool Running() const
{
return exeCount >= 0;
}
void Start()
{
io.reset(); // I don't know why I have to put io.reset here,
// since it's already been called in Stop()
exeCount = 0;
timer.expires_from_now(boost::posix_time::milliseconds(interval));
timer.async_wait(boost::bind(&EventTimer::EventHandler, this));
io.run();
}
void Stop()
{
if (Running())
{
// How to reset everything when stop is called???
//io.stop();
timer.cancel();
io.reset();
exeCount = -1; // Reset
}
}
private:
virtual void EventHandler()
{
// Execute the requested operation
//if (handler != NULL)
// handler();
std::cout << CurrentDateTimeTimestampMilliseconds() << ": exeCount = " << exeCount + 1 << std::endl;
// Check if one more time of handler execution is required
if (repeatCount == kInfiniteRepeatCount || ++exeCount < repeatCount)
{
timer.expires_at(timer.expires_at() + boost::posix_time::milliseconds(interval));
timer.async_wait(boost::bind(&EventTimer::EventHandler, this));
}
else
{
Stop();
std::cout << CurrentDateTimeTimestampMilliseconds() << ": Stopped" << std::endl;
}
}
private:
int interval; // Milliseconds
int repeatCount; // Number of times to trigger the EventHandler
int exeCount; // Number of executed times
boost::asio::io_service io;
boost::asio::deadline_timer timer;
Handler handler;
};
int main()
{
EventTimer etimer;
etimer.SetInterval(1000);
etimer.SetRepeatCount(1);
std::cout << CurrentDateTimeTimestampMilliseconds() << ": Started" << std::endl;
etimer.Start();
// boost::thread thrd1(boost::bind(&EventTimer::Start, &etimer));
Sleep(3000); // Keep the main thread active
etimer.SetInterval(2000);
etimer.SetRepeatCount(1);
std::cout << CurrentDateTimeTimestampMilliseconds() << ": Started again" << std::endl;
etimer.Start();
// boost::thread thrd2(boost::bind(&EventTimer::Start, &etimer));
Sleep(5000); // Keep the main thread active
}
/* Current Output:
20110520-125506.781: Started
20110520-125507.781: exeCount = 1
20110520-125507.781: Stopped
20110520-125510.781: Started again
*/
/* Expected Output (timestamp might be slightly different with some offset)
20110520-125506.781: Started
20110520-125507.781: exeCount = 1
20110520-125507.781: Stopped
20110520-125510.781: Started again
20110520-125512.781: exeCount = 1
20110520-125512.781: Stopped
*/
I don't know why that my second time of calling to EventTimer::Start() does not work at all. My questions are:
What should I do in
EventTimer::Stop() in order to reset
everything so that next time of
calling Start() will work?
Is there anything else I have to modify?
If I use another thread to start the EventTimer::Start() (see the commented code in the main function), when does the thread actually exit?
Thanks.
Peter
As Sam hinted, depending on what you're attempting to accomplish, most of the time it is considered a design error to stop an io_service. You do not need to stop()/reset() the io_service in order to reschedule a timer.
Normally you would leave a thread or thread pool running attatched to an io_service and then you would schedule whatever event you need with the io_service. With the io_service machinery in place, leave it up to the io_service to dispatch your scheduled work as requested and then you only have to work with the events or work requests that you schedule with the io_service.
It's not entirely clear to me what you are trying to accomplish, but there's a couple of things that are incorrect in the code you have posted.
io_service::reset() should only be invoked after a previous invocation of io_service::run() was stopped or ran out of work as the documentation describes.
you should not need explicit calls to Sleep(), the call to io_service::run() will block as long as it has work to do.
I figured it out, but I don't know why that I have to put io.reset() in Start(), since it's already been called in Stop().
See the updated code in the post.