syncevolution/src/syncevo/timeout.h

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