diff options
Diffstat (limited to 'Src/Wasabi/api/timer')
-rw-r--r-- | Src/Wasabi/api/timer/api_timer.h | 47 | ||||
-rw-r--r-- | Src/Wasabi/api/timer/osx_timer.cpp | 38 | ||||
-rw-r--r-- | Src/Wasabi/api/timer/osx_timer.h | 17 | ||||
-rw-r--r-- | Src/Wasabi/api/timer/timerclient.cpp | 108 | ||||
-rw-r--r-- | Src/Wasabi/api/timer/timerclient.h | 183 | ||||
-rw-r--r-- | Src/Wasabi/api/timer/timeslicer.cpp | 62 | ||||
-rw-r--r-- | Src/Wasabi/api/timer/timeslicer.h | 64 |
7 files changed, 519 insertions, 0 deletions
diff --git a/Src/Wasabi/api/timer/api_timer.h b/Src/Wasabi/api/timer/api_timer.h new file mode 100644 index 00000000..4bc8cb81 --- /dev/null +++ b/Src/Wasabi/api/timer/api_timer.h @@ -0,0 +1,47 @@ +#ifndef __TIMER_API_H +#define __TIMER_API_H + +// Under linux, the Timer API requires the Linux API + +#include <bfc/dispatch.h> +#include <bfc/platform/platform.h> + +class TimerClient; + +#ifdef _WIN32 +typedef UINT_PTR TimerToken ; +#elif defined(__APPLE__) +typedef EventLoopTimerRef TimerToken; +#else +#error port me! +#endif + +class timer_api : public Dispatchable +{ + public: + TimerToken timer_add(TimerClient *client, intptr_t id, int ms); + void timer_remove(TimerClient *client, TimerToken token); + + enum { + TIMER_API_ADD = 1, + TIMER_API_REMOVE = 11, + }; +}; + +inline TimerToken timer_api::timer_add(TimerClient *client, intptr_t id, int ms) +{ + return _call(TIMER_API_ADD, (TimerToken)0, client, id, ms); +} + +inline void timer_api::timer_remove(TimerClient *client, TimerToken token) +{ + _voidcall(TIMER_API_REMOVE, client, token); +} + +// {3130D81C-AE1F-4954-9765-698473B627B0} +static const GUID timerApiServiceGuid = +{ 0x3130d81c, 0xae1f, 0x4954, { 0x97, 0x65, 0x69, 0x84, 0x73, 0xb6, 0x27, 0xb0 } }; + +extern timer_api *timerApi; + +#endif
\ No newline at end of file diff --git a/Src/Wasabi/api/timer/osx_timer.cpp b/Src/Wasabi/api/timer/osx_timer.cpp new file mode 100644 index 00000000..450c58e1 --- /dev/null +++ b/Src/Wasabi/api/timer/osx_timer.cpp @@ -0,0 +1,38 @@ +#include "osx_timer.h" +#include <api/timer/timerclient.h> + +timer_api *timerApi = NULL; + + +TimerApi::TimerApi() +{ + mainEventLoop = GetMainEventLoop(); +} + +static void WasabiTimerProc(EventLoopTimerRef inTimer, void * inUserData) +{ + TimerClient *client = (TimerClient *)inUserData; + if (client) + client->timerclient_timerCallback(inTimer); +} + + +TimerToken TimerApi::timer_add(TimerClient *client, int id, int ms) +{ + EventLoopTimerRef token; + OSStatus err = InstallEventLoopTimer(mainEventLoop, + (float)ms/1000.0f, + (float)ms/1000.0f, + WasabiTimerProc, + client, + &token); + if (err == noErr) + return token; + else + return 0; +} + +void TimerApi::timer_remove(TimerClient *client, TimerToken token) +{ + RemoveEventLoopTimer(token); +} diff --git a/Src/Wasabi/api/timer/osx_timer.h b/Src/Wasabi/api/timer/osx_timer.h new file mode 100644 index 00000000..9084ef3a --- /dev/null +++ b/Src/Wasabi/api/timer/osx_timer.h @@ -0,0 +1,17 @@ +#ifndef NULLSOFT_WASABI_OSX_TIMER_H +#define NULLSOFT_WASABI_OSX_TIMER_H + +#include <api/timer/api_timer.h> + +class TimerApi : public timer_apiI +{ +public: + TimerApi(); + TimerToken timer_add(TimerClient *client, int id, int ms); + void timer_remove(TimerClient *client, TimerToken); +private: + EventLoopRef mainEventLoop; +}; + + +#endif
\ No newline at end of file diff --git a/Src/Wasabi/api/timer/timerclient.cpp b/Src/Wasabi/api/timer/timerclient.cpp new file mode 100644 index 00000000..e44e03ce --- /dev/null +++ b/Src/Wasabi/api/timer/timerclient.cpp @@ -0,0 +1,108 @@ +#include "api.h" +#include <api/timer/api_timer.h> +#include <api/timer/timerclient.h> + +#define CBCLASS TimerClientI +START_DISPATCH; +// this one doesn't map directly so that we can catch deferredcb timers +VCB(TIMERCLIENT_TIMERCALLBACK, timerclient_handleDeferredCallback); +CB(TIMERCLIENT_GETMASTERCLIENT, timerclient_getMasterClient); +VCB(TIMERCLIENT_ONMASTERMUX, timerclient_onMasterClientMultiplex); +CB(TIMERCLIENT_GETDEPPTR, timerclient_getDependencyPtr); +CB(TIMERCLIENT_GETSKIPPED, timerclient_getSkipped); +VCB(TIMERCLIENT_SETSKIPPED, timerclient_setSkipped); +VCB(TIMERCLIENT_POSTDEFERREDCB , timerclient_postDeferredCallback); +CB(TIMERCLIENT_ONDEFERREDCB , timerclient_onDeferredCallback); +CB(TIMERCLIENT_GETNAME, timerclient_getName); +END_DISPATCH; + +TimerClientI::TimerClientI() + : skipped(0), timerdelay(0), disallowset(0) +{ } + +TimerClientI::~TimerClientI() +{ + disallowset = 1; + if (cbs.getNumItems() > 0) + { + TimerToken token; + if (tokens.reverseGetItem(DEFERREDCB_TIMERID, &token)) // TODO: it would be nice to have a combo get/del, so we don't have to search twice + { + WASABI_API_TIMER->timer_remove(this, token); + tokens.delItem(token); + } + } + cbs.deleteAll(); +} + +int TimerClientI::timerclient_setTimer(intptr_t id, int ms) +{ + if (disallowset) return 0; + ASSERTPR(id > 0, "A timer id cannot be <= 0"); + ASSERTPR(id != DEFERREDCB_TIMERID, "please chose another timer id"); + TimerToken token = WASABI_API_TIMER->timer_add(this, id, ms); + tokens.addItem(token, id); + return 1; +} + +int TimerClientI::timerclient_killTimer(intptr_t id) +{ + TimerToken token; + if (tokens.reverseGetItem(id, &token)) // TODO: it would be nice to have a combo get/del, so we don't have to search twice + { + WASABI_API_TIMER->timer_remove(this, token); + tokens.delItem(token); + } + return 1; +} + +void TimerClientI::timerclient_postDeferredCallback(intptr_t param1, intptr_t param2, int mindelay) +{ + if (disallowset) return ; + for (int i = 0;i < cbs.getNumItems();i++) + { + deferred_callback *cb = cbs.enumItem(i); + if (cb->param1 == param1 && cb->param2 == param2) + { + cbs.removeByPos(i); + break; + } + } + deferred_callback *c = new deferred_callback; + c->origin = this; + c->param1 = param1; + c->param2 = param2; + cbs.addItem(c); + TimerToken token = WASABI_API_TIMER->timer_add(this, DEFERREDCB_TIMERID, MAX(1, mindelay)); + tokens.addItem(token, DEFERREDCB_TIMERID); +} + +void TimerClientI::timerclient_handleDeferredCallback(TimerToken token) +{ + // let deriving class handle it. note we call into here even if + // it's the deferred cb id, since older versions did that too, + // expecting the user to call down on the method. + +#ifdef WIN64 + LPARAM id; +#else + intptr_t id; +#endif + if (tokens.getItem(token, &id)) + { + timerclient_timerCallback((int)id); + + // process our deferred cb id + if (id == DEFERREDCB_TIMERID) + { + WASABI_API_TIMER->timer_remove(this, token); + PtrList<deferred_callback> temp(cbs); + cbs.removeAll(); + foreach(temp) + deferred_callback *c = temp.getfor(); + c->origin->timerclient_onDeferredCallback(c->param1, c->param2); + delete c; + endfor + } + } +} diff --git a/Src/Wasabi/api/timer/timerclient.h b/Src/Wasabi/api/timer/timerclient.h new file mode 100644 index 00000000..52860a01 --- /dev/null +++ b/Src/Wasabi/api/timer/timerclient.h @@ -0,0 +1,183 @@ +#ifndef __TIMER_CLIENT_H +#define __TIMER_CLIENT_H + +#include <bfc/dispatch.h> +#include <bfc/common.h> +#include <bfc/depend.h> +#include <map> + +#define DEFERREDCB_TIMERID -2 + +class TimerClient; + +#ifdef _WIN32 +typedef UINT_PTR TimerToken ; +#elif defined(__APPLE__) +typedef EventLoopTimerRef TimerToken; +#else +#error port me! +#endif + +typedef struct { + TimerClient *origin; + intptr_t param1; + intptr_t param2; +} deferred_callback; + +class NOVTABLE TimerClient : public Dispatchable +{ +protected: + TimerClient() { } + +public: + int timerclient_setTimer(int id, int ms); + int timerclient_killTimer(int id); + void timerclient_postDeferredCallback(intptr_t param1, intptr_t param2=0, int mindelay=0); + int timerclient_onDeferredCallback(intptr_t param1, intptr_t param2); + void timerclient_timerCallback(TimerToken token); + + TimerClient *timerclient_getMasterClient(); + void timerclient_onMasterClientMultiplex(); + api_dependent *timerclient_getDependencyPtr(); + void timerclient_setSkipped(int s); + int timerclient_getSkipped(); + void timerclient_setTimerDelay(int td); + int timerclient_getTimerDelay(); + const wchar_t *timerclient_getName(); + + enum { + TIMERCLIENT_TIMERCALLBACK = 101, + TIMERCLIENT_SETTIMER = 110, + TIMERCLIENT_KILLTIMER = 120, + TIMERCLIENT_GETMASTERCLIENT = 130, + TIMERCLIENT_ONMASTERMUX = 140, + TIMERCLIENT_GETDEPPTR = 150, + TIMERCLIENT_SETSKIPPED = 160, + TIMERCLIENT_GETSKIPPED = 170, + TIMERCLIENT_SETTIMERDELAY = 180, + TIMERCLIENT_GETTIMERDELAY = 190, + TIMERCLIENT_POSTDEFERREDCB = 200, + TIMERCLIENT_ONDEFERREDCB = 210, + TIMERCLIENT_GETNAME = 220, + }; +}; + +inline void TimerClient::timerclient_timerCallback(TimerToken token) { + _voidcall(TIMERCLIENT_TIMERCALLBACK, token); +} + +inline int TimerClient::timerclient_setTimer(int id, int ms) { + return _call(TIMERCLIENT_SETTIMER, 0, id, ms); +} + +inline int TimerClient::timerclient_killTimer(int id) { + return _call(TIMERCLIENT_KILLTIMER, 0, id); +} + +inline TimerClient *TimerClient::timerclient_getMasterClient() { + return _call(TIMERCLIENT_GETMASTERCLIENT, (TimerClient *)NULL); +} + +inline void TimerClient::timerclient_onMasterClientMultiplex() { + _voidcall(TIMERCLIENT_ONMASTERMUX); +} + +inline api_dependent *TimerClient::timerclient_getDependencyPtr() { + return _call(TIMERCLIENT_GETDEPPTR, (api_dependent *)NULL); +} + +inline void TimerClient::timerclient_setSkipped(int s) { + _voidcall(TIMERCLIENT_SETSKIPPED, s); +} + +inline int TimerClient::timerclient_getSkipped() { + return _call(TIMERCLIENT_GETSKIPPED, 0); +} + +inline void TimerClient::timerclient_setTimerDelay(int td) { + _voidcall(TIMERCLIENT_SETTIMERDELAY, td); +} + +inline int TimerClient::timerclient_getTimerDelay() { + return _call(TIMERCLIENT_GETTIMERDELAY, 0); +} + +inline void TimerClient::timerclient_postDeferredCallback(intptr_t param1, intptr_t param2, int mindelay) { + _voidcall(TIMERCLIENT_POSTDEFERREDCB, param1, param2, mindelay); +} + +inline int TimerClient::timerclient_onDeferredCallback(intptr_t param1, intptr_t param2) { + return _call(TIMERCLIENT_ONDEFERREDCB, 0, param1, param2); +} + +inline const wchar_t *TimerClient::timerclient_getName() { + return _call(TIMERCLIENT_GETNAME, (const wchar_t *)NULL); +} + +class NOVTABLE TimerClientI : public TimerClient { +protected: + TimerClientI(); + +public: + virtual ~TimerClientI(); + + virtual int timerclient_setTimer(intptr_t id, int ms); + virtual int timerclient_killTimer(intptr_t id); + + // override this to catch your timer events + virtual void timerclient_timerCallback(int id) { } + + virtual TimerClient *timerclient_getMasterClient() { return NULL; } + virtual void timerclient_onMasterClientMultiplex() { }; + virtual api_dependent *timerclient_getDependencyPtr()=0; + virtual void timerclient_setSkipped(int s) { skipped = s; } + virtual int timerclient_getSkipped() { return skipped; } + virtual void timerclient_setTimerDelay(int td) { timerdelay = td; } + virtual int timerclient_getTimerDelay() { return timerdelay; } + virtual void timerclient_postDeferredCallback(intptr_t param1, intptr_t param2=0, int mindelay=0); + virtual int timerclient_onDeferredCallback(intptr_t param1, intptr_t param2) { return 1; }; + virtual const wchar_t *timerclient_getName() { return NULL; } + +protected: + RECVS_DISPATCH; + +private: + virtual void timerclient_handleDeferredCallback(TimerToken token); + + int skipped; + int timerdelay; + int disallowset; + PtrList<deferred_callback> cbs; +#ifdef _WIN32 + class TokenMap + { + public: + void delItem(TimerToken) {} + bool reverseGetItem(intptr_t id, TimerToken *token) + { + *token = id; + return true; + } + bool getItem(TimerToken token, intptr_t *id) + { + *id = token; + return true; + } + void addItem(TimerToken, intptr_t) {} + }; +#else + typedef std::map<TimerToken, intptr_t> TokenMap; +#endif + TokenMap tokens; +}; + +class NOVTABLE TimerClientDI : public TimerClientI, public DependentI { +protected: + TimerClientDI() { } + +public: + api_dependent *timerclient_getDependencyPtr() { return this; } +}; + + +#endif diff --git a/Src/Wasabi/api/timer/timeslicer.cpp b/Src/Wasabi/api/timer/timeslicer.cpp new file mode 100644 index 00000000..04d37421 --- /dev/null +++ b/Src/Wasabi/api/timer/timeslicer.cpp @@ -0,0 +1,62 @@ +#include "precomp.h" +#include "timeslicer.h" + +#define TIMER_SLICE 0x7816 + +TimeSlicer::TimeSlicer(int percent_cpu_usage/* =50 */, int slice_duration/* =-1 */) { + max_cpu_usage = MIN((float)percent_cpu_usage, 99.0f) / 100.0f; + duration = slice_duration; + started = 0; + slicecount = 0; + firstslicetime = -1; +} + +TimeSlicer::~TimeSlicer() { +} + +void TimeSlicer::startSlicer() { + if (started) return; + started = 1; + timerclient_setTimer(TIMER_SLICE, duration); + onSlicerStart(); + timerclient_timerCallback(TIMER_SLICE); +} + +void TimeSlicer::stopSlicer() { + if (!started) return; + started = 0; + timerclient_killTimer(TIMER_SLICE); + onSlicerStop(); + firstslicetime = -1; +} + +void TimeSlicer::onSlicerStop() { +} + +void TimeSlicer::onSlicerStart() { +} + +int TimeSlicer::isSlicerStarted() { + return started; +} + +void TimeSlicer::timerclient_timerCallback(int id) { + if (id == TIMER_SLICE) { + DWORD now = Std::getTickCount(); + runSlice(now, now + (int)((float)duration * max_cpu_usage)); + } else + TimerClient::timerclient_timerCallback(id); +} + +void TimeSlicer::runSlice(DWORD start, DWORD stopwhen) { + DWORD now = Std::getTickCount(); + slicecount = 0; + onBeginSliceBatch(); + if (slicecount == 0 && firstslicetime != -1) stopwhen = now + firstslicetime; + while (Std::getTickCount() < stopwhen) { + onSlice(); + slicecount++; + if (!started) break; // got aborted + } + onEndSliceBatch(); +} diff --git a/Src/Wasabi/api/timer/timeslicer.h b/Src/Wasabi/api/timer/timeslicer.h new file mode 100644 index 00000000..06ae67f4 --- /dev/null +++ b/Src/Wasabi/api/timer/timeslicer.h @@ -0,0 +1,64 @@ +#ifndef __TIMESLICER_H +#define __TIMESLICER_H + +// TimeSlicer allows you to create a background job to perform while not blocking +// the main GUI thread. You give it the max percentage of CPU it should use, call star() +// and it'll start calling onSlice as many times as it can without using more cpu than requested +// +// To use this class, you need to break down your job into multiple tiny chunks that +// you perform in onSlice. Typical uses include adding files to or filtering entries from +// the database, driving state machines, etc. +// +// onSlice will be called multiple times per timer. + +#include "timerclient.h" + +enum { + GRANULARITY_EXTRALOW = 20, + GRANULARITY_LOW = 50, + GRANULARITY_MEDIUM = 100, + GRANULARITY_HIGH = 250, + GRANULARITY_EXTRAHIGH = 1000, +}; + +class TimeSlicer : public TimerClientI { + public: + + TimeSlicer(int percent_cpu_usage=25, int slice_duration=GRANULARITY_LOW); + virtual ~TimeSlicer(); + + virtual void timerclient_timerCallback(int id); + + void startSlicer(); + void stopSlicer(); + int isSlicerStarted(); + + virtual void onSlicerStart(); + virtual void onSlicerStop(); + virtual void onBeginSliceBatch() {} + virtual void onEndSliceBatch() {} + api_dependent *timerclient_getDependencyPtr() { return timeslicer_getDependencyPtr(); } + virtual api_dependent *timeslicer_getDependencyPtr()=0; + virtual void setFirstSliceMinTime(int ms) { firstslicetime = ms; } + virtual int getSliceCount() { return slicecount; } + + + // override this to do your work + virtual void onSlice() { } + + private: + + virtual void runSlice(DWORD start, DWORD stopwhen); + float max_cpu_usage; + int duration; + int started; + int firstslicetime; + int slicecount; +}; + +class TimeSlicerD : public TimeSlicer, public DependentI { + public: + virtual api_dependent *timeslicer_getDependencyPtr() { return this; } +}; + +#endif
\ No newline at end of file |