diff options
Diffstat (limited to 'Src/timer/timermul.cpp')
-rw-r--r-- | Src/timer/timermul.cpp | 370 |
1 files changed, 370 insertions, 0 deletions
diff --git a/Src/timer/timermul.cpp b/Src/timer/timermul.cpp new file mode 100644 index 00000000..6e7213a9 --- /dev/null +++ b/Src/timer/timermul.cpp @@ -0,0 +1,370 @@ +#include <bfc/platform/platform.h> +#include "timermul.h" + +#include <api.h> +#include <api/config/items/attribs.h> +#include <api/config/items/cfgitem.h> + + // {9149C445-3C30-4e04-8433-5A518ED0FDDE} + const GUID uioptions_guid = + { 0x9149c445, 0x3c30, 0x4e04, { 0x84, 0x33, 0x5a, 0x51, 0x8e, 0xd0, 0xfd, 0xde } }; + +PtrListQuickSorted<MultiplexerServer, MultiplexerServerComparatorTID> servers_tid; +PtrListQuickSorted<MultiplexerServer, MultiplexerServerComparatorTID> servers_mux; + +TimerMultiplexer::TimerMultiplexer() { + timerset = 0; + nslices = 0; + resolution = -1; + check_resolution = true; + client = NULL; + curslice = 0; + running_timer = NULL; + uioptions = NULL; + justexited = 0; + firstevent = 1; + resetTimer(50); // initial, is changed for config value on first event +} + +TimerMultiplexer::~TimerMultiplexer() { + doShutdown(); +} + +void TimerMultiplexer::setClient(TimerMultiplexerClient *_client) { + client = _client; +} + +void TimerMultiplexer::addTimer(int ms, void *data) { + //if (ms < 0) { DebugString("Timer with negative delay set, ignored coz the time machine service isn't ready yet\n"); } + MultiplexedTimer *t = new MultiplexedTimer(ms, data); + if (ms >= MAX_TIMER_DELAY) { + lptimers.addItem(t); + t->nexttick = Wasabi::Std::getTickCount() + t->ms; + } else { + timers.addItem(t); + if (nslices > 0) + distribute(t); + } +} + +void TimerMultiplexer::removeTimer(void *data) { + + if (running_timer && running_timer->data == data) + running_timer = NULL; + + int i; + for (i=0;i<timers.getNumItems();i++) { + MultiplexedTimer *t = timers.enumItem(i); + if (t->data == data) { + removeFromWheel(t); + timers.removeByPos(i); + delete t; + return; + } + } + for (i=0;i<lptimers.getNumItems();i++) { + MultiplexedTimer *t = lptimers.enumItem(i); + if (t->data == data) { + removeFromLowPrecision(t); + delete t; + return; + } + } +} + +void TimerMultiplexer::setResolution(int ms) { + resolution = ms; +} + +void TimerMultiplexer::shutdown() { + doShutdown(); +} + +void TimerMultiplexer::doShutdown() { + timers.deleteAll(); + wheel.deleteAll(); + lptimers.deleteAll(); + if (timerset) { + MultiplexerServer *s = servers_mux.findItem((const wchar_t *)this); + if (s) { +#ifdef WIN32 + KillTimer(NULL, s->getId()); +#elif defined(LINUX) + +#endif + } + timerset = 0; + } +} + +void TimerMultiplexer::checkResolution(DWORD now) { + if (check_resolution == true) + { + if (WASABI_API_CONFIG) + { + if (uioptions == NULL) + { + uioptions = WASABI_API_CONFIG->config_getCfgItemByGuid(uioptions_guid); + if (uioptions) + { + ifc_dependent *ui_change = uioptions->getDependencyPtr(); + ui_change->dependent_regViewer(this, 1); + } + } + check_resolution = uioptions?false:true; + int nresolution = uioptions ? _intVal(uioptions, L"Multiplexed timers resolution") : DEF_RES; + + nresolution = MAX(10, MIN(MAX_TIMER_DELAY/LOW_RES_DIV, nresolution)); + if (nresolution != resolution) { + resetTimer(nresolution); + resolution = nresolution; + resetWheel(); + } + } + } +} + +VOID CALLBACK timerMultiplexerServerProc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime) { + MultiplexerServer *s = servers_tid.findItem((const wchar_t *)&idEvent); + if (s) s->getMultiplexer()->onServerTimer(); +} + +void TimerMultiplexer::resetTimer(int newresolution) { + if (timerset) { + MultiplexerServer *s = servers_mux.findItem((const wchar_t *)this); + if (s) + KillTimer(NULL, s->getId()); + } + + // linux port implements settimer + UINT_PTR id = SetTimer(NULL, 0, newresolution, timerMultiplexerServerProc); + MultiplexerServer *s = servers_mux.findItem((const wchar_t *)this); + if (!s) { + s = new MultiplexerServer(this, (UINT)id); + servers_mux.addItem(s); + servers_tid.addItem(s); + } else { + s->setId(id); + servers_tid.sort(); + } + timerset = 1; +} + +PtrList<MultiplexedTimer> *TimerMultiplexer::getSlice(int n) { + ASSERT(nslices > 0); + return wheel.enumItem(n % nslices); +} + +void TimerMultiplexer::resetWheel() { + + wheel.deleteAll(); + + nslices = MAX_TIMER_DELAY / resolution; + + for (int i=0;i<nslices;i++) + wheel.addItem(new PtrList< MultiplexedTimer >); + + curslice = 0; + distributeAll(); +} + +void TimerMultiplexer::distributeAll() { + for (int i=0;i<timers.getNumItems();i++) { + distribute(timers.enumItem(i)); + } +} + +void TimerMultiplexer::distribute(MultiplexedTimer *t) { + ASSERT(t != NULL); + + int delay = t->ms; + + int slice = delay / resolution + curslice; + PtrList<MultiplexedTimer> *l = getSlice(slice); + + ASSERT(l != NULL); + + l->addItem(t); +} + +void TimerMultiplexer::onServerTimer() { + + justexited = 0; + + DWORD now = Wasabi::Std::getTickCount(); + + checkResolution(now); + + runCurSlice(now); + + if ((curslice % (nslices/LOW_RES_DIV)) == 0) { // execute low precision timers every MAX_TIMER_DELAY/LOW_RES_DIV + runLowPrecisionTimers(now); + } + + if (!justexited) { + curslice++; + curslice %= nslices; + } + + justexited = 1; + + if (firstevent) { + firstevent = 0; + checkResolution(Wasabi::Std::getTickCount()); + } +} + +void TimerMultiplexer::runCurSlice(DWORD now) { + //DebugString("Running slice %d\n", curslice); + PtrList<MultiplexedTimer> *slice = getSlice(curslice); + ASSERT(slice != NULL); + + // mark them clean + int i; + for (i=0;i<slice->getNumItems();i++) + slice->enumItem(i)->flag = 0; + + // run events + int n; + do { + n = 0; + for (i=0;i<slice->getNumItems();i++) { + MultiplexedTimer *t = slice->enumItem(i); + if (t == NULL) break; // do not remove this line even if you think it's useless + // t might have been removed by a previous runTimer in this slice, so see if it's still here and if not, ignore + if (!timers.haveItem(t)) { slice->removeItem(t); i--; continue; } + if (t->flag == 1) continue; + t->flag = 1; + int lastdelay = MAX(0, (int)(now - t->lastmscount)); + DWORD last = t->lastmscount; + if (last == 0) last = now; + t->lastmscount = now; + t->lastdelay = lastdelay; + running_timer = t; + runTimer(now, last, t, slice, i); +// ----------------------------------------------------------------------- +// WARNING +// +// below this line, you can no longer assume that t is pointing at valid +// memory, because runTimer can eventually call removeTimer +// ----------------------------------------------------------------------- + n++; + } + } while (n > 0); +} + +void TimerMultiplexer::runTimer(DWORD now, DWORD last, MultiplexedTimer *t, PtrList<MultiplexedTimer> *slice, int pos) { + + int nextslice = curslice + t->ms / resolution; + int spent = now - last; + int lost = spent - t->ms; + + if (lost > 0) { + t->lost += (float)lost / (float)t->ms; + } + + PtrList<MultiplexedTimer> *next = getSlice(nextslice); + ASSERT(next != NULL); + + if (slice == next) { + nextslice++; + next = getSlice(nextslice); + } + + slice->removeByPos(pos); + next->addItem(t); + + int skip = (int)t->lost; + t->lost -= (int)t->lost; + if (client) { + client->onMultiplexedTimer(t->data, skip, t->lastdelay); +// ----------------------------------------------------------------------- +// WARNING +// +// below this line, you can no longer assume that t is pointing at valid +// memory, because onMultiplexedTimer can eventually call removeTimer +// ----------------------------------------------------------------------- + } +} + +void TimerMultiplexer::removeFromWheel(MultiplexedTimer *t) { + for (int i=0;i<nslices;i++) { + PtrList<MultiplexedTimer> *slice = getSlice(i); + for (int j=0;j<slice->getNumItems();j++) { + if (slice->enumItem(j) == t) { + slice->removeByPos(j); + j--; + } + } + } +} + +void TimerMultiplexer::removeFromLowPrecision(MultiplexedTimer *t) { + for (int i=0;i<lptimers.getNumItems();i++) { + if (lptimers.enumItem(i) == t) { + lptimers.removeByPos(i); + i--; + } + } +} + +void TimerMultiplexer::runLowPrecisionTimers(DWORD now) { + int restart; + do { + restart = 0; + for (int i=0;i<lptimers.getNumItems();i++) { + MultiplexedTimer *t = lptimers.enumItem(i); + if (t->nexttick < now) { + if (client) { + running_timer = t; + t->lost += (now - t->nexttick) / t->ms; + int skip = (int)t->lost; + t->lost -= skip; // remove integer part + DWORD last = t->lastmscount; + t->lastdelay = now-last; + t->lastmscount = now; + t->nexttick = t->nexttick+(t->ms)*(skip+1); + client->onMultiplexedTimer(t->data, skip, t->lastdelay); +// ----------------------------------------------------------------------- +// WARNING +// +// below this line, you can no longer assume that t is pointing at valid +// memory, because onMultiplexedTimer can eventually call removeTimer +// ----------------------------------------------------------------------- + } + if (running_timer == NULL) { // onMultiplexedTimer called removeTimer + restart =1; + break; + } + } + } + } while (restart); +} + + +int TimerMultiplexer::getNumTimers() { + return timers.getNumItems(); +} + +int TimerMultiplexer::getNumTimersLP() { + return lptimers.getNumItems(); +} + +int TimerMultiplexer::dependentViewer_callback(ifc_dependent *item, const GUID *classguid, int cb, intptr_t param1, intptr_t param2 , void *ptr, size_t ptrlen) +{ + if (param1 == CfgItem::Event_ATTRIBUTE_CHANGED) + { + check_resolution=true; + } + else if (param1 == CfgItem::Event_ATTRIBUTE_REMOVED) + { + uioptions=0; + } + return 1; +} + +#define CBCLASS TimerMultiplexer +START_DISPATCH; +CB(DEPENDENTVIEWER_CALLBACK, dependentViewer_callback) +END_DISPATCH; +#undef CBCLASS |