/** * @file aicurltimer.cpp * * Copyright (c) 2013, Aleric Inglewood. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * There are special exceptions to the terms and conditions of the GPL as * it is applied to this Source Code. View the full text of the exception * in the file doc/FLOSS-exception.txt in this software distribution. * * CHANGELOG * and additional copyright holders. * * 29/06/2013 * - Initial version, written by Aleric Inglewood @ SL */ // An AICurlTimer object provides a callback API for timer events // by and from the curl thread. It is NOT threadsafe. Everything // has to be called from the curl thread. // // Typical usage: // // AICurlTimer timer; // ... // // In curl thread (after successful construction is guaranteed). // timer.create(5.5, boost::bind(&the_callback, )); // Call the_callback() in 5.5 seconds. // // The callback function is always called by the curl thread. // // If timer.cancel() is called before the timer expires, then the callback // function isn't called. Calling cancel() multiple times is not a problem. // Note: It is not allowed to call cancel() is from the callback function // (and rather pointless). // // The timer object can be reused (by calling create() again), but // only after either the callback function was called, or after cancel() // returned. Most notably, you can call create() again from inside the // callback function to "restart" the timer. // #include "aicurltimer.h" #include "lltimer.h" static U64 const NEVER = ((U64)1) << 60; // The year 36,560,871. //static F64 const AICurlTimer::sClockWidth_1ms = 1000.0 / calc_clock_frequency(); // Time between two clock ticks, in 1ms units. U64 AICurlTimer::sTime_1ms; // Time in 1ms units, set once per select() entry. U64 AICurlTimer::sNextExpiration = NEVER; AICurlTimer::timer_list_type AICurlTimer::sTimerList; void AICurlTimer::create(deltams_type expiration, signal_type::slot_type const& slot) { AIRunningCurlTimer new_timer(expiration, this); llassert(mHandle.mRunningTimer == sTimerList.end()); // Create may only be called when the timer isn't already running. mHandle.init(sTimerList.insert(new_timer), slot); sNextExpiration = sTimerList.begin()->expiration(); } void AICurlTimer::cancel(void) { if (mHandle.mRunningTimer != sTimerList.end()) { sTimerList.erase(mHandle.mRunningTimer); mHandle.mRunningTimer = sTimerList.end(); sNextExpiration = sTimerList.empty() ? NEVER : sTimerList.begin()->expiration(); } } void AICurlTimer::handleExpiration(void) { for(;;) { if (sTimerList.empty()) { // No running timers left. sNextExpiration = NEVER; break; } timer_list_type::iterator running_timer = sTimerList.begin(); sNextExpiration = running_timer->expiration(); if (sNextExpiration > sTime_1ms) { // Didn't expire yet. break; } Handle& handle(running_timer->getTimer()->mHandle); llassert_always(running_timer == handle.mRunningTimer); handle.mRunningTimer = sTimerList.end(); running_timer->do_callback(); // May not throw exceptions. // Erase the timer from the running list. sTimerList.erase(running_timer); } }