c++ Implementing Timed Callback function
Many folks have contributed good answers here on the matter, but I will address the question directly, because I had a similar problem a couple of years ago. I could not use Boost for several reasons--I know Boost has excellent use in a lot of open source software. Moreover, I really wanted to understand timers and callbacks specifically as it pertains to Linux based environments. So, I wrote my own.
Fundamentally, I have a Timer
class and a TimerCallback
class. A typical callback, implemented as a inherited class of the TimerCallback
class, will place the operations to be executed upon callback in the triggered ()
method, implemented specifically for the needs.
Per the usual semantics, a Timer
object is associated with a callback object, which presumably contains all the required information needed for the callback to execute. The timer scheduling is managed by one environment-wide timer minheap which has to be maintained in a separate thread/process. This minheap task does only one thing: it minheapifies the minheap of callback events set in the future. A minheap selects the next event to fire in O(1)
and can minheapify the remaining in O(log n)
for n
timer events. It can also insert a new timer event in O(log n)
(Read a gentle introduction to heaps here).
When a timer fires, the minheap scheduler checks if it is a periodic timer, one shot timer or a timer that will execute a specific number of times. Accordingly, the timer object is either removed from the minheap or reinserted back into the minheap with the next execution time. If a timer object is to be removed, then it is removed from the minheap (but the timer object deletion may or may not be left to the task that created it) and the rest of the heap is minheap-ified; i.e., rearranged to satisfy the minheap property.
A working and unit tested implementation is here, and may contain bugs (excerpted from my application), but I thought it may help someone. The implementation is multi-process (fork()
ed-process) based (and also uses pthread
s in the main task (process)), and uses POSIX shared memory and POSIX message queues for communication between the processes.
Implementing timer callback in c
How should you approach this problem?
As with all problems:
- Figure out what you want to do precisely
- Ask yourself how you would do it yourself if you needed to (and if you could)
- Try to formalize previous step as close as possible to single statements sentences, one step at a time.
- Identify what blocks you / what you don't know how to do. Do some research on that.
What you want to do precisely
You want to "create a timed callback". I think this means :
- You have a function
foo
doing some work - You have your program running
- You want to be able to say "From right now, in X miliseconds, call
foo
" anywhere in your program
How would you do this yourself?
I think you would, for example, launch a stopwatch with X miliseconds then keep doing whatever you were doing. When the stopwatch reaches zero, you stop what you do and do the thing needed. Finally, resume what you were doing.
What blocks you?
Judging by your question I think two things block you:
- You need to understand how to do function callbacks in C. See "function pointers"
- You need to understand how to have a timer and do something when the timer reaches zero.
A few google searches will help you with both.
C Timer Callback
SetTimer in Win32 with a TimerProc as the call back.
/* calls TimerProc once every 60000 milliseconds */
SetTimer(NULL, 1, 60000, TimerProc);
C++ callback timer implementation
Rather than using threads you could use std::async
. The following class will process the queued strings in order 4 seconds after the last string is added. Only 1 async task will be launched at a time and std::aysnc
takes care of all the threading for you.
If there are unprocessed items in the queue when the class is destructed then the async task stops without waiting and these items aren't processed (but this would be easy to change if its not your desired behaviour).
#include <iostream>
#include <string>
#include <future>
#include <mutex>
#include <chrono>
#include <queue>
class Batcher
{
public:
Batcher()
: taskDelay( 4 ),
startTime( std::chrono::steady_clock::now() ) // only used for debugging
{
}
void queue( const std::string& value )
{
std::unique_lock< std::mutex > lock( mutex );
std::cout << "queuing '" << value << " at " << std::chrono::duration_cast< std::chrono::milliseconds >( std::chrono::steady_clock::now() - startTime ).count() << "ms\n";
work.push( value );
// increase the time to process the queue to "now + 4 seconds"
timeout = std::chrono::steady_clock::now() + taskDelay;
if ( !running )
{
// launch a new asynchronous task which will process the queue
task = std::async( std::launch::async, [this]{ processWork(); } );
running = true;
}
}
~Batcher()
{
std::unique_lock< std::mutex > lock( mutex );
// stop processing the queue
closing = true;
bool wasRunning = running;
condition.notify_all();
lock.unlock();
if ( wasRunning )
{
// wait for the async task to complete
task.wait();
}
}
private:
std::mutex mutex;
std::condition_variable condition;
std::chrono::seconds taskDelay;
std::chrono::steady_clock::time_point timeout;
std::queue< std::string > work;
std::future< void > task;
bool closing = false;
bool running = false;
std::chrono::steady_clock::time_point startTime;
void processWork()
{
std::unique_lock< std::mutex > lock( mutex );
// loop until std::chrono::steady_clock::now() > timeout
auto wait = timeout - std::chrono::steady_clock::now();
while ( !closing && wait > std::chrono::seconds( 0 ) )
{
condition.wait_for( lock, wait );
wait = timeout - std::chrono::steady_clock::now();
}
if ( !closing )
{
std::cout << "processing queue at " << std::chrono::duration_cast< std::chrono::milliseconds >( std::chrono::steady_clock::now() - startTime ).count() << "ms\n";
while ( !work.empty() )
{
std::cout << work.front() << "\n";
work.pop();
}
std::cout << std::flush;
}
else
{
std::cout << "aborting queue processing at " << std::chrono::duration_cast< std::chrono::milliseconds >( std::chrono::steady_clock::now() - startTime ).count() << "ms with " << work.size() << " remaining items\n";
}
running = false;
}
};
int main()
{
Batcher batcher;
batcher.queue( "test 1" );
std::this_thread::sleep_for( std::chrono::seconds( 1 ) );
batcher.queue( "test 2" );
std::this_thread::sleep_for( std::chrono::seconds( 1 ) );
batcher.queue( "test 3" );
std::this_thread::sleep_for( std::chrono::seconds( 2 ) );
batcher.queue( "test 4" );
std::this_thread::sleep_for( std::chrono::seconds( 5 ) );
batcher.queue( "test 5" );
}
Long precedure in timer's callback
The problem of long duration tasks interleaved with short duration tasks is a fundamental problem of real-time (and near-real-time) systems. Having a separate thread to handle longer running tasks, and delegating longer tasks to a second thread is a good solution. But is your long-running task waiting on io, or just heavy processing?
Should the delay be caused by asynchronous processing (waiting on io, for example), You might split the work into a 'top-half' and a 'bottom-half', where the 'top-half' dispatches the io request, and then schedules a 'bottom-half' to check for the results. Alternatives are to handle the signal from the response, possibly with a promise (really interesting technique).
Should the delay be cause by a processing heavy task, you might still be able to use a similar technique, by forming your compute heavy task in such a way that it 'yields' periodically. Threading is better, but may not be available in some embedded environments.
Related Topics
Can't Downcast Because Class Is Not Polymorphic
How to Count Lines of a File in C++
Default Move Constructor/Assignment and Deleted Copy Constructor/Assignment
Iterating Through a Lua Table from C++
C++11 Range-Based For-Loop Efficiency "Const Auto &I" Versus "Auto I"
Load an Pem Encoded X.509 Certificate into Windows Cryptoapi
Why Isn't 'Std::Initializer_List' Defined as a Literal Type
Name of Process for Active Window in Windows 8/10
Opencv to Use in Memory Buffers or File Pointers
Do Polymorphism or Conditionals Promote Better Design
How to Initialize Static Members in the Header
Opencv Grouprectangles - Getting Grouped and Ungrouped Rectangles
Lambda Expression VS Functor in C++