mirror of
https://github.com/WinampDesktop/winamp.git
synced 2024-09-24 15:54:12 +00:00
371 lines
9.4 KiB
C++
371 lines
9.4 KiB
C++
|
#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
|