ResidualVM logo ResidualVM website - Forums - Contact us BuildBot - Doxygen - Wiki curved edge

default-timer.cpp

Go to the documentation of this file.
00001 /* ScummVM - Graphic Adventure Engine
00002  *
00003  * ScummVM is the legal property of its developers, whose names
00004  * are too numerous to list here. Please refer to the COPYRIGHT
00005  * file distributed with this source distribution.
00006  *
00007  * This program is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU General Public License
00009  * as published by the Free Software Foundation; either version 2
00010  * of the License, or (at your option) any later version.
00011  *
00012  * This program is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  * GNU General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU General Public License
00018  * along with this program; if not, write to the Free Software
00019  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00020  *
00021  */
00022 
00023 #include "common/scummsys.h"
00024 #include "backends/timer/default/default-timer.h"
00025 #include "common/util.h"
00026 #include "common/system.h"
00027 
00028 struct TimerSlot {
00029     Common::TimerManager::TimerProc callback;
00030     void *refCon;
00031     Common::String id;
00032     uint32 interval;    // in microseconds
00033 
00034     uint32 nextFireTime;    // in milliseconds
00035     uint32 nextFireTimeMicro;   // microseconds part of nextFire
00036 
00037     TimerSlot *next;
00038 };
00039 
00040 void insertPrioQueue(TimerSlot *head, TimerSlot *newSlot) {
00041     // The head points to a fake anchor TimerSlot; this common
00042     // trick allows us to get rid of many special cases.
00043 
00044     const uint32 nextFireTime = newSlot->nextFireTime;
00045     TimerSlot *slot = head;
00046     newSlot->next = 0;
00047 
00048     // Insert the new slot into the sorted list of already scheduled
00049     // timers in such a way that the list stays sorted...
00050     while (true) {
00051         assert(slot);
00052         if (slot->next == 0 || nextFireTime < slot->next->nextFireTime) {
00053             newSlot->next = slot->next;
00054             slot->next = newSlot;
00055             return;
00056         }
00057         slot = slot->next;
00058     }
00059 }
00060 
00061 
00062 DefaultTimerManager::DefaultTimerManager() :
00063     _head(0) {
00064 
00065     _head = new TimerSlot();
00066     memset(_head, 0, sizeof(TimerSlot));
00067 }
00068 
00069 DefaultTimerManager::~DefaultTimerManager() {
00070     Common::StackLock lock(_mutex);
00071 
00072     TimerSlot *slot = _head;
00073     while (slot) {
00074         TimerSlot *next = slot->next;
00075         delete slot;
00076         slot = next;
00077     }
00078     _head = 0;
00079 }
00080 
00081 void DefaultTimerManager::handler() {
00082     Common::StackLock lock(_mutex);
00083 
00084     uint32 curTime = g_system->getMillis(true);
00085 
00086     // Repeat as long as there is a TimerSlot that is scheduled to fire.
00087     TimerSlot *slot = _head->next;
00088     while (slot && slot->nextFireTime < curTime) {
00089         // Remove the slot from the priority queue
00090         _head->next = slot->next;
00091 
00092         // Update the fire time and reinsert the TimerSlot into the priority
00093         // queue.
00094         assert(slot->interval > 0);
00095         slot->nextFireTime += (slot->interval / 1000);
00096         slot->nextFireTimeMicro += (slot->interval % 1000);
00097         if (slot->nextFireTimeMicro > 1000) {
00098             slot->nextFireTime += slot->nextFireTimeMicro / 1000;
00099             slot->nextFireTimeMicro %= 1000;
00100         }
00101         insertPrioQueue(_head, slot);
00102 
00103         // Invoke the timer callback
00104         assert(slot->callback);
00105         slot->callback(slot->refCon);
00106 
00107         // Look at the next scheduled timer
00108         slot = _head->next;
00109     }
00110 }
00111 
00112 bool DefaultTimerManager::installTimerProc(TimerProc callback, int32 interval, void *refCon, const Common::String &id) {
00113     assert(interval > 0);
00114     Common::StackLock lock(_mutex);
00115 
00116     if (_callbacks.contains(id)) {
00117         if (_callbacks[id] != callback) {
00118             error("Different callbacks are referred by same name (%s)", id.c_str());
00119         }
00120     }
00121     TimerSlotMap::const_iterator i;
00122 
00123     for (i = _callbacks.begin(); i != _callbacks.end(); ++i) {
00124         if (i->_value == callback) {
00125             error("Same callback added twice (old name: %s, new name: %s)", i->_key.c_str(), id.c_str());
00126         }
00127     }
00128     _callbacks[id] = callback;
00129 
00130     TimerSlot *slot = new TimerSlot;
00131     slot->callback = callback;
00132     slot->refCon = refCon;
00133     slot->id = id;
00134     slot->interval = interval;
00135     slot->nextFireTime = g_system->getMillis() + interval / 1000;
00136     slot->nextFireTimeMicro = interval % 1000;
00137     slot->next = 0;
00138 
00139     insertPrioQueue(_head, slot);
00140 
00141     return true;
00142 }
00143 
00144 void DefaultTimerManager::removeTimerProc(TimerProc callback) {
00145     Common::StackLock lock(_mutex);
00146 
00147     TimerSlot *slot = _head;
00148 
00149     while (slot->next) {
00150         if (slot->next->callback == callback) {
00151             TimerSlot *next = slot->next->next;
00152             delete slot->next;
00153             slot->next = next;
00154         } else {
00155             slot = slot->next;
00156         }
00157     }
00158 
00159     // We need to remove all names referencing the timer proc here.
00160     //
00161     // Else we run into troubles, when the client code removes and readds timer
00162     // callbacks.
00163     //
00164     // Another issues occurs when one plays a game with ALSA as music driver,
00165     // does RTL and starts a different engine game with ALSA as music driver.
00166     // In this case the MPU401 code will add different timer procs with the
00167     // same name, resulting in two different callbacks added with the same
00168     // name and causing installTimerProc to error out.
00169     // A good test case is running a SCUMM with ALSA output and then a KYRA
00170     // game for example.
00171     for (TimerSlotMap::iterator i = _callbacks.begin(), end = _callbacks.end(); i != end; ++i) {
00172         if (i->_value == callback)
00173             _callbacks.erase(i);
00174     }
00175 }


Generated on Sat Mar 16 2019 05:01:24 for ResidualVM by doxygen 1.7.1
curved edge   curved edge