Initial community commit
This commit is contained in:
parent
537bcbc862
commit
fc06254474
16440 changed files with 4239995 additions and 2 deletions
370
Src/timer/timermul.cpp
Normal file
370
Src/timer/timermul.cpp
Normal file
|
@ -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
|
Loading…
Add table
Add a link
Reference in a new issue