162 lines
4.7 KiB
C++
162 lines
4.7 KiB
C++
/*
|
|
* Copyright (C) 2011 Intel Corporation
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) version 3.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
|
* 02110-1301 USA
|
|
*/
|
|
|
|
#ifndef TIMEOUT_H
|
|
#define TIMEOUT_H
|
|
|
|
#include <syncevo/SmartPtr.h>
|
|
#include <syncevo/util.h>
|
|
|
|
#include <boost/utility.hpp>
|
|
#include <boost/bind.hpp>
|
|
|
|
#include <syncevo/declarations.h>
|
|
SE_BEGIN_CXX
|
|
|
|
/**
|
|
* Utility class which makes it easier to work with g_timeout_add_seconds().
|
|
* Instantiate this class with a specific callback. Use boost::bind()
|
|
* to attach specific parameters to that callback. Then activate
|
|
* the timeout. Destructing this class will automatically remove
|
|
* the timeout and thus ensure that it doesn't trigger without
|
|
* valid parameters.
|
|
*
|
|
* This class is thread-safe. If called by a thread different from the
|
|
* main thread, the callback will happen inside the main thread. Use
|
|
* g_main_context_wakeup() to ensure that the main thread notices the
|
|
* new callback right away.
|
|
*/
|
|
class Timeout : boost::noncopyable
|
|
{
|
|
guint m_tag;
|
|
boost::function<bool ()> m_callback;
|
|
|
|
public:
|
|
enum {
|
|
PRIORITY_HIGH = G_PRIORITY_HIGH,
|
|
PRIORITY_DEFAULT = G_PRIORITY_DEFAULT,
|
|
PRIORITY_HIGH_IDLE = G_PRIORITY_HIGH_IDLE,
|
|
PRIORITY_DEFAULT_IDLE = G_PRIORITY_DEFAULT_IDLE,
|
|
PRIORITY_LOW = G_PRIORITY_LOW
|
|
};
|
|
|
|
Timeout() :
|
|
m_tag(0)
|
|
{
|
|
}
|
|
|
|
~Timeout()
|
|
{
|
|
if (m_tag) {
|
|
g_source_remove(m_tag);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* call the callback at regular intervals until it returns false
|
|
*
|
|
* @param seconds a value < 0 runs the function as soon as the process is idle,
|
|
* otherwise in the specified amount of time
|
|
*/
|
|
void activate(int seconds,
|
|
const boost::function<bool ()> &callback,
|
|
int priority = G_PRIORITY_DEFAULT)
|
|
{
|
|
deactivate();
|
|
|
|
m_callback = callback;
|
|
m_tag = seconds < 0 ?
|
|
g_idle_add(triggered, static_cast<gpointer>(this)) :
|
|
g_timeout_add_seconds(seconds, triggered, static_cast<gpointer>(this));
|
|
if (!m_tag) {
|
|
SE_THROW("g_timeout_add_seconds() or g_idle_add() failed");
|
|
}
|
|
}
|
|
|
|
void activate(const boost::function<bool ()> &idleCallback,
|
|
int priority = G_PRIORITY_DEFAULT_IDLE)
|
|
{
|
|
activate(-1, idleCallback, priority);
|
|
}
|
|
|
|
/**
|
|
* invoke the callback once
|
|
*/
|
|
void runOnce(int seconds,
|
|
const boost::function<void ()> &callback,
|
|
int priority = G_PRIORITY_DEFAULT)
|
|
{
|
|
activate(seconds, boost::bind(&Timeout::once, callback), priority);
|
|
}
|
|
void runOnce(const boost::function<void ()> &idleCallback,
|
|
int priority = G_PRIORITY_DEFAULT)
|
|
{
|
|
runOnce(-1, idleCallback, priority);
|
|
}
|
|
|
|
/**
|
|
* stop calling the callback, drop callback
|
|
*/
|
|
void deactivate()
|
|
{
|
|
if (m_tag) {
|
|
g_source_remove(m_tag);
|
|
m_tag = 0;
|
|
}
|
|
m_callback = 0;
|
|
}
|
|
|
|
/** true iff active */
|
|
operator bool () const { return m_tag != 0; }
|
|
|
|
private:
|
|
static gboolean triggered(gpointer data) throw ()
|
|
{
|
|
Timeout *me = static_cast<Timeout *>(data);
|
|
bool runAgain = false;
|
|
uint tag = me->m_tag;
|
|
try {
|
|
// Be extra careful and don't trigger a deactivated callback.
|
|
if (me->m_callback) {
|
|
runAgain = me->m_callback();
|
|
}
|
|
} catch (...) {
|
|
// Something unexpected went wrong, can only shut down.
|
|
Exception::handle(HANDLE_EXCEPTION_FATAL);
|
|
}
|
|
if (!runAgain && // Returning false will automatically deactivate the source, remember that.
|
|
me->m_tag == tag // Beware that the callback may have already reused the Timeout instance.
|
|
// In that case, we must not reset the new tag and callback.
|
|
) {
|
|
me->m_tag = 0;
|
|
me->m_callback = 0;
|
|
}
|
|
return runAgain;
|
|
}
|
|
|
|
static bool once(const boost::function<void ()> &callback) {
|
|
callback();
|
|
return false;
|
|
}
|
|
};
|
|
|
|
SE_END_CXX
|
|
|
|
#endif // TIMEOUT_H
|