123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370 |
- #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
|