#include #include "timermul.h" #include #include #include // {9149C445-3C30-4e04-8433-5A518ED0FDDE} const GUID uioptions_guid = { 0x9149c445, 0x3c30, 0x4e04, { 0x84, 0x33, 0x5a, 0x51, 0x8e, 0xd0, 0xfd, 0xde } }; PtrListQuickSorted servers_tid; PtrListQuickSorted 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;idata == data) { removeFromWheel(t); timers.removeByPos(i); delete t; return; } } for (i=0;idata == 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 *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); curslice = 0; distributeAll(); } void TimerMultiplexer::distributeAll() { for (int i=0;ims; int slice = delay / resolution + curslice; PtrList *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 *slice = getSlice(curslice); ASSERT(slice != NULL); // mark them clean int i; for (i=0;igetNumItems();i++) slice->enumItem(i)->flag = 0; // run events int n; do { n = 0; for (i=0;igetNumItems();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 *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 *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 *slice = getSlice(i); for (int j=0;jgetNumItems();j++) { if (slice->enumItem(j) == t) { slice->removeByPos(j); j--; } } } } void TimerMultiplexer::removeFromLowPrecision(MultiplexedTimer *t) { for (int i=0;inexttick < 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